徐汇区“一网统管”——12345热线分析

项目介绍——2020年6月-2020年9月

业务:数据可视化大屏看板,将百姓通过12345市长热线投诉的问题,借助图、表、地图等形式以可视化的方式分不同维度展示,帮助政府部门作出分析研判。
技术:基于Vue2+TypeScript+iView+Webpack开发的单页面应用

我的职责

  • 后期的前端责任人,9月驻场开发
  • 用最小的成本,处理现场频繁变更的需求(忘记具体问题)
  • 与GIS现场调优,对业主提出的热力聚合效果进行打磨
  • 与第三方公司研发沟通,处理项目间通信
  • 针对多分辨率,对内嵌的移动端位置做响应式处理
  • pdf主题色,css滤镜
  • 大屏适配方案

    我做了哪些

    大屏适配方案

    Situation背景
    一般大屏适配是这样处理的,设计稿的宽高是40961080。取当前屏幕分辨率下根元素documentElement的宽高(clientWidth、 client Hei ght )。以根元素的高度为基准,求根元素高度与设计稿高度的比值。设置根元素的height为clientHeight,设置根元素的width为设计稿width乘以比值,以保持宽高比不变。设置根元素fontSize值为100比值作为rem的参考值。此时如果在40961080,页面刚好铺满。如果在小屏上,则高度不变,横向会出现滚动条。
    然而部署到现场有两个问题,一是现场演示所用的屏幕不全是4096
    1080的,有的屏幕比这个尺寸大的多,因此会在个别分辨率的屏幕上出现滚动条;二是现场要求在笔记本这种小屏上也不能出现滚动条,需要保持大屏尺寸类似于图片的object-fit:contain的效果,上下两侧留白。
    Task任务
  1. 要求适配其他分辨率的大屏,不能出现滚动条
  2. 对于小屏,应该保持宽高比,居中显示,上下两侧留白

Action行动

  1. 忽略设计稿尺寸,当文档元素的clientWidth大于1920时,文档元素、body元素、#app元素的宽高都为100%。文档元素的fontSize的计算逻辑不变
  2. 当文档元素的clientWidth小于1920时,此时将#app元素的宽高设置为设计稿的宽高。为了保证大屏能完整展示,以宽度为基准,求根元素clientWidth与设计稿宽度的比值。并以该比值作为transform: scale的值对大屏进行整体缩放。注意此时根元素的fontSize值始终为100px。在body中使用flex布局,将#app在body中垂直居中。

Result结果
监听文档视图调整的resize事件,当自适应功能开启后,根据当前窗口下的文档元素clientWidth展示不同类型的视角。
技术要点

  • resize事件
  • addEventListener方法,都有接受哪些参数,有什么区别,DOM事件的捕获与冒泡
  • clientWidth offsetWidth scrollWidth都有啥区别
  • flex布局
  • Echarts中的文字使用的是px,超大分辨率下如何保证文字正常显示?
  • rem px em % 的含义

    项目间通信

    :::info 重点体现做事的主观能动性。关键词:任务优先级、主动推进
    一个需要第三方协助的需求,从提出到对接完成只用0.5天。 ::: Situation背景
    项目大屏是使用iframe嵌在第三方公司产品(监督指挥)上的。其中项目大屏菜单中有两个模块是第三方公司提供,我们需要在点击菜单时与对方通信。因此有两个问题:
  1. 跨iframe通信
  2. 如何联调

Task任务

  1. 跨iframe与父页面通信
  2. 与第三方公司研发沟通联调功能

Action行动

  1. 梳理需求,明确采用postMessage的技术方式向父级页面通信
  2. 由于是在现场开发,主动找到第三方公司的研发负责人,描述需要协助处理的内容及处理方式。与他指定的研发进一步沟通,明确通信内容

Result结果
从接到需求,到明确解决方案完成对接总共花了不到半天时间。技术方面并不复杂,但是涉及到与第三方公司,越是这种需要第三方配合的工作,越要尽早提出,主动推进。不然拖到最后加班的还是自己。
技术要点

  1. 将iframe背景改成暖黄色

Action行动

  1. 使用css中的filter滤镜

Result结果
PDF文件直接使用iframe加载,羊皮卷效果通过css的filter属性实现
filter: invert(.2) sepia(60%) saturate(100%);

技术要点

  • filter
  • 兼容性

    • Can I Use
    • polyfill
    • shim
    • browserslist
    • Autoprefixer

      城市运行中心

      项目介绍——2019年7月——2021年2月

      业务:城市运行数据大屏看板,将城市中城管、市政、环卫、园林、执法、道桥等场景中的相关数据借助图表、地图等形式以可视化的方式分不同维度展示,帮助政府部门作出分析研判。
      技术栈:Vue+TypeScript+Axios+iView+Webpack 多应用模式,单页面形式打包,可视化图表以Echarts为主

      我的职责

      常规的大屏卡片组件开发
      地图相关功能开发主要包括:图层控制菜单、图层搜索、人车轨迹面板等

      我做了哪些

      地图图层搜索

      :::info 任务描述:
      表层事实:(用了哪些新技能或工具)
      为了完成该任务,在根据不同图层类型获取图层搜索相关数据方面,为了减少工作量,使匹配策略易维护、易扩展。我使用了Map结构,以正则作为key去匹配对应图层数据。为了解决不同图层的搜索结果样式相同字段不同的问题,固定了搜索结果列表组件的HTML和CSS样式,引入字段映射配置文件,利用字段解析工具函数解析映射后的数据。
      关键词:Map、正则表达式、字段映射、字段解析
      深度细节:
  • 如何完成任务的

定义了一个返回一个Map结构的函数,Map中每一项元素的key为正则形式,value是一个接收图层code和关键字参数的函数。调用函数后,过滤出符合正则规则的项。然后再调用value函数根据传参获取搜索项的label、placeholder、keyword等数据。定义不同图层的字段映射文件该文件导出一个对象,对象中是图层code与图层字段映射对象组成的键值对,在HTML中根据图层code去匹配对应的字段映射对象。给Object增加解析字段方法Object.getValue(),该方法接受一个对象,以及一个映射字段字符串(a.b.c),返回映射字段对应的值。

  • 同事间如何沟通协作的

感受和观点:

  • (成就感)简化了图层搜索功能的开发流程

对于新增的A图层,给它加图层搜索功能只需要两步,一是增加该图层的字段映射对象。二是在Map中增加新的策略以供搜索使用。如果该图层是某个图层的子类,比如改图层是渣土车图层,如果已经有车辆图层,就只需要增加字段映射对象1步即可。

  • (完成任务的关键)平时看博客与学习组长的代码

    会看一些文章,使用Map这种形式就是从文章中学习到的。统一数据结构,解析映射字段的灵感来自组长的代码,他在别的场景中用到了,当时我还去调试了字段解析方法的逻辑。 ::: Situtaion背景
    GIS地图图层数据搜索功能,不同项目、不同专题都有不同的图层。图层数据量往往很大,分布在城市的各个地方,用户想精确查找某个数据时很不方便,因此需要在大屏中增加图层数据搜索功能。这里存在两个问题:

  1. 选择搜索不同的图层数据有三个地方不同:一是搜索的关键字,比如人员是根据 name字段搜索,车辆是根据车牌号搜索等;二是placeholder不同,必须选择了搜索人员图层,placeholder应该提示“请输入人员姓名”;三是label标签不同,选择了人员图层,label中会显示”人员“二字。因此,需要根据不同图层类型去匹配不同的数据。使用什么样的模式能让代码更加清晰、方便接入,易维护呢?(可用 ifelse switch object Map)
  2. 搜索结果列表的视图展示。不同图层搜索出的结果不同,但是HTML结构和CSS样式相同。单是因为图层字段不同,给每个图层都写一遍同样的HTML显然不太合理。那么,如何在保证使用同一套HTML和CSS结构的情况下,展示不同图层的不同数据呢?

Task任务

  1. 贴合业务选择合适的策略模式,根据不同图层类型,匹配不同的搜索相关数据(label placeholder searchKey);
  2. 统一HTML和CSS结构,展示不同图层数据;

Action行动

  • 对于问题1。使用Map数据结构,使用正则当做key。这样同类型图层,比如车辆图层、渣土车、环卫车都可以使用同一套逻辑。同时,value返回一个函数,可以根据传入的key获取值,也可以根据layerCode做其他逻辑。 ```javascript function actions() { const vehicleSearch = (layerCode,key) => { // 可根据layerCode做不同处理,比如都是车, // 有的label需要显示具体类型的车辆,比如消防车,则可以根据layerCode单独处理 const vehicle = {
    1. label: "车辆",
    2. placeholder: "请输入车牌号",
    3. key: "plateNo",
    }; if (!vehicle.hasOwnProperty(key)) {
    1. console.error("对象中不存在" + key + "属性,请检查!");
    2. return;
    } return vehicle[key]; }; return new Map([[/^layervehicle(|\w+)$/g, vehicleSearch]]); }

function getSearchConfig(layerCode, configKey) { const action = […actions()].filter(([key, value]) => key.test(layerCode)); return action.map(([key, value]) => value(layerCode, configKey)).join(“”); } const label = getSearchConfig(“layer_vehicle_fire”, “label”); console.log(label); // 车辆

  1. - 对于问题2。固定HTML结构和CSS样式,增加数据映射配置映射功能,根据图层的code从配置文件中找到展示信息对应的配置。开发配置解析工具,对配置进行解析。
  2. - 数据映射文件
  3. ```javascript
  4. export const configData: any = {
  5. layer_records: {
  6. icon: "icon-file",
  7. title: {
  8. name: "taskNo",
  9. text: "案件"
  10. },
  11. status: {
  12. name: "pageStatus",
  13. text: ""
  14. },
  15. data: [
  16. {
  17. name: "subType.text",
  18. text: "类别"
  19. },
  20. {
  21. name: "address",
  22. text: "地点"
  23. },
  24. {
  25. name: "timestamp",
  26. text: "时间"
  27. }
  28. ]
  29. }
  30. };
  • 固定的HTML和CSS ```html

    {{configData[layerCode].title.text}}

    {{Object.getValue(item, configData[layerCode].title.name)}}

  1. - 开发getValue方法,取出映射字段的值。支持深度取值,比如"a.b.c.d"。大概思路就是将需要解析的name根据"."拆分成数组。然后循环从对象中寻找值。
  2. **Result结果**
  3. 1. 将搜索功能开发精简到1步。对于同种类型的图层比如渣土车图层,开发搜索功能只需要新增数据映射即可。
  4. 2. 如果遇到新增的类型,比如视频图层,只需要在Map中新增它的keyvalue。不会对其他类型造成影响。
  5. 3. 配置化思想,低代码雏形。日后配置文件可以在平台生成
  6. <a name="mS02k"></a>
  7. ### 人车轨迹面板
  8. :::info
  9. 以前:用户选日期 时间段查询轨迹。两个问题:
  10. 1. 使用麻烦;有时候这个时间段没有轨迹数据,只能换下个时间段去查,没有就再换时间段
  11. 2. 排查麻烦;现场看到没有数据直接找前端排查,前端看接口发现没有轨迹数据,再反馈给现场
  12. 过程:排查该类问题出现多次,再加上用户使用确实麻烦。主动找产品,看是否出一个新的轨迹面板。该面板能将一天内的车轨迹数据可视展示,用户在选择时间时能做到有的放矢。<br />现在:轨迹面板中存在一个带时间刻度的面积图,面积图时间刻度下方有个slider双向滑块。用户根据面积图中轨迹数据,使用slider选择时间段。
  13. :::
  14. **Situation背景**<br />人车轨迹数据不是每个时间段都有,以前用户只能盲选时间,如果没有轨迹数据就换其他时间,在使用上很不方便。因此,需要一个可视化的面板,将一天中人车轨迹数据展示出来,让用户知道哪个时间段有数据,配合slider滑块,选择对应时间段的时间查询轨迹。<br />**Task任务**
  15. 1. 实现一个带时间刻度的面积图,展示一天中包含轨迹数据的时间段
  16. 2. 使用slider双向滑块选择时间段查询
  17. **Action行动**
  18. 1. 实现面积图
  19. - 后台数据只返回有轨迹数据的时间段,不包含所有时间。因此需要先生成一个 时间数组,这个数组包含 一天 24个小时的原始值。此外为了保证时间刻度的完整性,最后一个数据是第二天零点的原始值。
  20. - 新建一个typelineseries,将时间数组中的时间原始值作为x轴的数据。将时间数组的值作为data赋值给series。这样就能在echarts中展示出一个包含 24个小时的时间轴,只是数据为空。
  21. - 新建一个typelineseries,将后台返回的数据进行处理,将日期处理成 时间原始值。可以用moment也可以用Date.prototype.valueOf()。将处理后的值赋值给该seriesdata。此时就实现了一个能展示一天中轨迹数据的echarts图。
  22. ```javascript
  23. time25 = [];
  24. function time25Format() {
  25. const date = new Date();
  26. const year = date.getFullYear();
  27. const month = date.getMonth() + 1;
  28. const day = date.getDate();
  29. const formatDate = `${year}-${month}-${day}`;
  30. for (let i = 0; i < 24; i++) {
  31. const curTimestamp = new Date(`${formatDate} ${i}:00:00`).valueOf();
  32. time25.push({
  33. name: curTimestamp,
  34. value: [curTimestamp, '']
  35. });
  36. }
  37. // 3600000 是一个小时的毫秒数
  38. time25.push({
  39. name: new Date(`${formatDate} 23:00:00`).valueOf() + 3600000,
  40. value: [new Date(`${formatDate} 23:00:00`).valueOf() + 3600000, '']
  41. });
  42. }
  43. time25Format();
  1. 实现刻度(这个亮点gg了,特么的echart自带次刻度线** minorTick **就能实现,绝了,不复盘的结果就是用复杂的方法实现了简单效果,还沾沾自喜。)不过这个次刻度线是4.6.0版本提出的,如果没有这个功能,我的方法倒是可以polyfill一下(算是心里安慰吧)
  • x轴增加一个类目轴
  • 新增一个type为bar的series。利用柱状图实现时间刻度。只是柱状图中的数据均为负值。
    • 那一共有多少根柱子呢?根据设计图,整点的刻度长一点,整点之间的分钟刻度短一点。一天有24个小时,就是有24个整点,为了保证刻度完成,还需要追加一个第二天的零点作为最后一根柱子。因此 一共有25个整点。两个整点之间的分钟刻度有三个,表示每隔15分钟显示一根柱子 xx:15 \xx:30 \xx:45 因此25个整点之间存在24个区块,每块有3个分钟刻度,因此一共有 24*3=72个分钟刻度。加上整点刻度就是97个。
    • 整点和分钟刻度的长度如何确定?采用负值,比如整点刻度为-10,分钟刻度为-5
      1. let timeScale = [];
      2. for (let i = 0; i < 97; i++) {
      3. const value = i % 4 === 0 ? -10 : -5;
      4. timeScale.push(value);
      5. }
      Result结果
      就是这个样子,挺好用的。
      image.png