街道管理难题怎么破?高德地图“黑科技”给出完美答案!

背景

本次分享的实战案例项目名为"智慧街道一体化平台"。由于开发时间紧迫,我采用了Vue2框架进行前后端架构开发,并基于高德地图的JS API和Web基础服务API,实现了一个简单的街道管理项目。客户需要可视化大屏展示,因此我们重点展示了街道社区在地图中的效果。

目标

客户关于街道可视化部分的主要需求包括:在地图上展示街道全貌,实现街道中社区的区域化展示效果;通过序号标注小区,并在点击标注点位时将地图视角定位并放大到对应社区;同时,在信息窗体中显示对应小区的相关信息。

方案设计

1、街道的边界区域化

为了实现对街道边界的区域化展示,我查阅了JS API文档,并采用了多边形AMap.Polygon功能。通过该功能,我们可以方便地绘制出街道的边界。

官方功能示意图

demo效果图

完整示例代码如下。

js 复制代码
 
const streetCoordinates = [
            [
              [119.017987, 33.616876],
              [119.019426, 33.616761],
             此处省略我当时手动采集的街道边界的经纬度坐标点.....
            ],
          ];
          var HuaihaiPolyline = new AMap.Polyline({
            path: streetCoordinates,
            isOutline: true,
 outlineColor: "#D9251C",
            borderWeight: 3,
            strokeColor: "#D9251C",
            strokeOpacity: 1,
            strokeWeight: 6,
            // 折线样式还支持 'dashed'
             strokeStyle: "dashed",
            // strokeStyle是dashed时有效
            strokeDasharray: [15, 5],
            lineJoin: "round",
            lineCap: "round",
            zIndex: 50,
           strokeWeight: 1,
          });
          this.map.add([HuaihaiPolyline]);

2、社区范围的区域化显示

在该街道中共有7个社区。为了方便展示,我手动采集了社区的坐标点位,并通过不同色块展示了街道中的社区。

覆盖物效果图

覆盖物功能采集的demo完整代码如下。

js 复制代码
 
<!doctype html>
<html>
 
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
  <style>
    html,
    body,
container {
      width: 100%;
      height: 100%;
    }
</style>
  <title>多边形编辑器吸附功能</title>
   <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
  <script 
src="https://webapi.amap.com/maps?v=2.0&key=09023f93f5e47cb8087795cff9eadec5&plugin=AMap.PolygonEditor">
</script>
 <script src="https://a.amap.com/jsapi_demos/static/demo-center/js/demoutils.js"></script>
</head>
 
<body>
  <div id="container"></div>
  <div class="input-card" style="width: 120px">
 <button class="btn" onclick="createPolygon()" style="margin-bottom: 5px">新建</button>
 <button class="btn" onclick="polyEditor.open()" style="margin-bottom: 5px">开始编辑</button>
 <button class="btn" onclick="polyEditor.close()">结束编辑</button>
    <button class="btn" onclick="test()">获取经纬度数组</button>
  </div>
  <script type="text/javascript">
    var map = new AMap.Map("container", {
      center: [116.471354, 39.994257],
      zoom: 16.8
    });
    
     // 1号小区
    var path1 = [
      [
        119.017987,
        33.616882
      ],
  [
        119.01945,
        33.616795
      ],
      [
 119.023023,
        33.615225
      ],
      [
119.027848,
        33.614045
      ],
      [
 119.027083,
        33.612268
      ],
      [
 119.024476,
        33.613368
      ],
      [
 119.024079,
        33.613259
      ],
      [
        119.023838,
  33.613042
      ],
      [
        119.023734,
        33.61289
      ],
      [
 119.023573,
        33.612815
      ],
      [
        119.023399,
33.61258
      ],
      [
        119.019942,
        33.613188
      ],
 [
        119.019465,
        33.613172
      ],
      [
        119.01884,
 33.615088
      ]
    ]
    // 2号小区
var path3 = [
      [
        119.030715,
        33.603081
 ],
      [
        119.029033,
        33.599871
 ],
      [
        119.028916,
        33.599187
 ],
      [
        119.030978,
        33.599661
      ],
      [
        119.031845,
33.599977
      ],
      [
        119.032563,
        33.600343
 ],
      [
        119.033177,
        33.601044
 ],
      [
        119.031667,
        33.602829
 ],
      [
        119.031161,
        33.602987
  ]
    ]
    // 3号
    var path4 = [
      [
        119.032683,
        33.599499
      ],
  [
        119.032617,
        33.599665
      ],
      [
 119.032531,
        33.599818
      ],
      [
        119.032382,
33.600133
      ],
      [
        119.032594,
 33.60027
      ],
      [
        119.032743,
        33.600443
      ],
      [
        119.033037,
        33.600258
      ],
      [
        119.033209,
33.599652
      ],
      [
        119.033302,
        33.599331
      ],
      [
 119.033332,
        33.599153
      ],
      [
        119.033322,
        33.598985
      ],
      [
        119.032735,
33.598823
      ],
      [
        119.032358,
        33.598742
      ],
      [
119.03196,
        33.598664
      ],
      [
        119.031566,
 33.599015
      ],
      [
        119.032166,
        33.599237
      ],
      [
119.03256,
        33.599382
      ],
      [
        119.032648,
        33.599447
      ]
 ]
    // 4
    var path5 = [
      [
        119.028981,
        33.599134
      ],
      [
        119.03123,
 33.599657
      ],
      [
        119.03135,
        33.599181
      ],
 [
        119.031609,
        33.598887
      ],
      [
        119.029051,
        33.598446
 ]
    ]
    var path2 = [
      [119.030785, 33.604298],
      [119.032148, 33.604396],
    ]
 
    var polygon1 = new AMap.Polygon({
      path: path1
    })
    var polygon2 = new AMap.Polygon({
      path: path2
    })
 var polygon3 = new AMap.Polygon({
      path: path3
    })
    var polygon4 = new AMap.Polygon({
      path: path4
    })
var polygon5 = new AMap.Polygon({
      path: path5
    })
 
    map.add([polygon1, polygon2, polygon3, polygon4, polygon5]);
    map.setFitView();
var polyEditor = new AMap.PolygonEditor(map);
    polyEditor.addAdsorbPolygons([polygon1, polygon2]);
    polyEditor.on('add', function (data) {
      console.log(data);
var polygon = data.target;
      polyEditor.addAdsorbPolygons(polygon);
      polygon.on('dblclick', () => {
 polyEditor.setTarget(polygon);
        polyEditor.open();
      })
    })
 polygon1.on('dblclick', () => {
      polyEditor.setTarget(polygon1);
      polyEditor.open();
    })
 polygon2.on('dblclick', () => {
      polyEditor.setTarget(polygon2);
      polyEditor.open();
    })
 
    function createPolygon() {
      polyEditor.close();
      polyEditor.setTarget();
      polyEditor.open();
    }
 polyEditor.setTarget(polygon2);
    polyEditor.open();
 
 function test(params) {
      console.log(polygon2.getPath()); // 输出多边形的轮廓线节点数组
      console.log(polygon2.getOptions()); // 返回覆盖物的中心点的经纬度坐标
    }
</script>
</body>

通过该demo网页手动画出覆盖物区域,最后点击获取经纬度数组按钮即可在浏览器控制台打印出来。

获取覆盖物经纬度demo效果图

3、小区marker点采集

对于小区marker点的采集,我采用了管理员在平台添加小区时一并添加小区坐标点的方式。这样,无论后续新增多少小区,都可以由街道管理员自行添加或修改坐标点位。

具体解决方案是,管理员在新增小区的时候,先输入小区名称,通过点击查询小区坐标点,这里调用高德开放平台的POI高级搜索API功能,获取到小区的坐标点,最后点击创建即可。这里要非常感谢高德开放平台的web服务有每日免费额度,为我们开发者提供了便利。

通过POI服务获取小区坐标点demo的完整代码如下。

js 复制代码
<!doctype html>
<html>
 
<head>
 <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
 <title>输入提示后查询POI</title>
    <link rel="stylesheet" href="https://cache.amap.com/lbs/static/main1119.css" />
    <!-- 引入样式 -->
 <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <style type="text/css">
panel {
  position: absolute;
            background-color: white;
            max-height: 90%;
            overflow-y: auto;
            top: 10px;
            right: 10px;
            width: 280px;
            }
</style>
    <script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=09023f93f5e47cb8087795cff9eadec5">
</script>
 <script src="../js/vue@v2.6.14.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="../js/axios@1.3.4.js"></script>
</head>
 
<body>
    <div id="app">
        <div id="container"></div>
        <div id="panel"></div>
        <div id="myPageTop">
   <table>
                <tr>
                    <td>
                        <label>请输入关键字:</label>
                    </td>
                </tr>
                <tr>
                    <td>
 
 <input v-model="address" id="tipinput" />
                    </td>
                </tr>
                <tr>
                    <td>
<label>请输入城市:</label>
                    </td>
                </tr>
                <tr>
                    <td>
  <input v-model="city" id="tipinput" />
                    </td>
                </tr>
                <tr>
                    <td>
  <button @click="btn()">开始查询</button>
                    </td>
                </tr>
            </table>
        </div>
  </div>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                msg: 'Hello Vue!',
                address: "向阳人家",
  city: "淮安市",
                addressList: [], //住宅列表
                map: null, //地图实例
                firstLocation: null, // 存储首个maker的位置
            },
 mounted() {
                console.log(this.msg);
                this.init()
            },
            methods: {
  addMarkersToMap() {
                    this.firstLocation = this.addressList[0].location.split(',').map(parseFloat);
 
                    this.addressList.forEach((location, index) => {
 const position = location.location.split(',').map(parseFloat);
                        const marker = new AMap.Marker({
                            position: new AMap.LngLat(position[0], position[1]),
  // content: `<div>${location.name}</div>` // 如果需要显示名称的话
                        });
 
                        marker.setMap(this.map);
 
                        // 如果是第一个maker,添加地图平移和缩放
  if (index === 0) {
                            this.map.setCenter(new AMap.LngLat(this.firstLocation[0], this
                                .firstLocation[1])); // 移动地图中心到第一个maker位置
   this.map.setZoom(15); // 设置地图缩放到合适的级别,您可以根据实际情况调整
                        }
  }
                         marker.on('click', () => {
                        const currentPos = marker.getPosition(); // 获取当前位置
 console.log(`点击的maker坐标:${currentPos.lng}, ${currentPos.lat}`);
                    });
                });
        },
  // 已有btn方法...
        init() {
            var that = this
            this.map = new AMap.Map("container", {
 resizeEnable: true
            });
        },
 getinfo() {
            axios.get(
                'https://restapi.amap.com/v3/place/text?keywords=淮安&city=beijing&offset=20&page=1&key=67164699cd494100c2e6ec9e75257a2c&extensions=all'
  ).then(res => {
                console.log("请求结果", res);
            })
        },
        btn() {
            axios.get(
               `https://restapi.amap.com/v3/assistant/inputtips?output=JSON&city=${this.city}&keywords=${this.address}&citylimit=true&key=67164699cd494100c2e6ec9e75257a2c`
   ).then(res => {
                console.log("请求结果", res);
                console.log("请求结果22", res.data.tips);
    if (res.data.tips.length > 0) {
                    this.addressList = res.data.tips
                    this.addMarkersToMap()
          }
            })
        }
    },
        })
</script>
</body>

效果展示

通过接口获取marker点列表后,我为每个marker添加了点击事件。点击后,地图会定位并放大到对应位置。最终实现的网页效果如下所示。

大屏效果图

最终我将网页中地图区域的内容完整的封装成了一个Vue组件,完整代码如下。

js 复制代码
 
<template>
  <div id="container"></div>
</template>
 
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
// import { COORDINATES } from "@/utils/position.min"; // 导入住宅覆盖物坐标数组
import { COORDINATES } from "@/utils/positionArea"; // 导入住宅覆盖物坐标数组
import { getList as getResidenceList } from "@/api/streetManage/residenceManage";
 
export default {
  name: "map-view",
  props: {
    // 当前住宅
    currentLsitData: {
      type: Object,
    },
  },
  watch: {
    // 监听当前点击的住宅列表的变化
    currentLsitData: {
      handler(newVal) {
        this.setMapViewMiddle(newVal);
      },
    },
  },
  data() {
    return {
      infoWindow: null, //信息窗体
      map: null, //地图实例
      numberList: [], //住宅列表
    };
  },
  async mounted() {
    await this.getResidenceList();
    await this.initAMap();
  },
  unmounted() {
    this.map?.destroy();
  },
  methods: {
    // 查询住宅列表
    async getResidenceList() {
      let queryParams = {
        pageNum: 1,
        pageSize: 100,
      };
      let res = await getResidenceList(queryParams);
      const newData = res.rows.map((item) => ({
        ...item,
        location: item.location.split(",").map(parseFloat),
      }));
      this.numberList = newData;
    },
    // 设置地图当前中心点位
    setMapViewMiddle(newVal) {
      this.map.setCenter(newVal.location);
      this.map.setZoom(18);
    },
    // 初始化地图
    initAMap() {
      AMapLoader.load({
        key: "09023f93f5e47cb8087795cff9eadec5", // 申请好的Web端开发者Key,首次调用 load 时必填
        version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
        plugins: ["AMap.Scale"], //需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
      })
        .then((AMap) => {
          var that = this;
          this.map = new AMap.Map("container", {
            // 设置地图容器id
            viewMode: "3D", // 是否为3D地图模式
            // zoom: 13, // 初始化地图级别
            center: [119.032116, 33.603177], // 初始化地图中心点位置
            resizeEnable: true,
            mapStyle: "amap://styles/dark", //设置地图的显示样式
            pitch: 10, //地图俯仰角度,有效范围 0 度- 83 度
            rotateEnable: true, //是否开启地图旋转交互 鼠标右键 + 鼠标画圈移动 或 键盘Ctrl + 鼠标左键画圈移动
            pitchEnable: true, //是否开启地图倾斜交互 鼠标右键 + 鼠标上下移动或键盘Ctrl + 鼠标左键上下移动
            zoom: 29, //初始化地图层级
            rotation: -15, //初始地图顺时针旋转的角度
            terrain: true, //开启地形图
            // features: ["bg", "road", "point","building"],
            // mapStyle: "amap://styles/light",
            // layers: [
            //   // 高德默认标准图层
            //   new AMap.TileLayer.Satellite(),
            //   // 楼块图层
            //   new AMap.Buildings({
            //     zooms: [16, 18],
            //     zIndex: 10,
            //     heightFactor: 2, //2倍于默认高度,3D下有效
            //   }),
            // ],
          });
          const points = this.numberList;
          const streetCoordinates = [
            [
              [119.017987, 33.616876],
              [119.019426, 33.616761],
              [119.023956, 33.614935],
              [119.027885, 33.614054],
              [119.029664, 33.613541],
              [119.031662, 33.612804],
              [119.033995, 33.611714],
              [119.035281, 33.611063],
              [119.036521, 33.610694],
              [119.037669, 33.609992],
              [119.03871, 33.6091],
              [119.039827, 33.607965],
              [119.041144, 33.60646],
              [119.043777, 33.605198],
              [119.043239, 33.602682],
              [119.044324, 33.600741],
              [119.045281, 33.598641],
              [119.043207, 33.59912],
              [119.038866, 33.598773],
              [119.034334, 33.59803],
              [119.02527, 33.596435],
              [119.021408, 33.606749],
              [119.019844, 33.611825],
              [119.019142, 33.614297],
              [119.017986, 33.616876],
            ],
          ];
          const createTextStyle = (styleName) => {
            switch (styleName) {
              case "1":
                return {
                  width: "0.3rem",
                  height: "0.3rem",
                  borderRadius: "50%",
                  backgroundColor: "#0093DD",
                  color: "#fff",
                  textAlign: "center",
                  fontSize: "0.2rem",
                  lineHeight: "0.3rem",
                  fontWeight: "bold",
                  display: "flex",
                  justifyContent: "center",
                  alignItems: "center",
                };
              case "2":
                return {
                  width: "0.3rem",
                  height: "0.3rem",
                  borderRadius: "50%",
                  backgroundColor: "#00923F",
                  color: "#fff",
                  textAlign: "center",
                  fontSize: "0.2rem",
                  lineHeight: "0.3rem",
                  fontWeight: "bold",
                  display: "flex",
                  justifyContent: "center",
                  alignItems: "center",
                };
              case "3":
                return {
                  width: "0.3rem",
                  height: "0.3rem",
                  borderRadius: "50%",
                  backgroundColor: "#E67817",
                  color: "#fff",
                  textAlign: "center",
                  fontSize: "0.2rem",
                  lineHeight: "0.3rem",
                  fontWeight: "bold",
                  display: "flex",
                  justifyContent: "center",
                  alignItems: "center",
                };
              default:
                return {
                  width: "50px",
                  height: "50px",
                  borderRadius: "50%",
                  backgroundColor: "skyblue",
                  color: "#fff",
                  textAlign: "center",
                  fontSize: "0.3rem",
                  lineHeight: "50px",
                  fontWeight: "bold",
                  display: "flex",
                  justifyContent: "center",
                  alignItems: "center",
                };
            }
          };
          var HuaihaiPolyline = new AMap.Polyline({
            path: streetCoordinates,
            isOutline: true,
            outlineColor: "#D9251C",
            borderWeight: 3,
            strokeColor: "#D9251C",
            strokeOpacity: 1,
            strokeWeight: 6,
            // 折线样式还支持 'dashed'
            strokeStyle: "dashed",
            // strokeStyle是dashed时有效
            strokeDasharray: [15, 5],
            lineJoin: "round",
            lineCap: "round",
            zIndex: 50,
            strokeWeight: 1,
          });
          this.map.add([HuaihaiPolyline]);
          this.map.setFitView();
          for (var i = 0; i < COORDINATES.length; i++) {
            var area = COORDINATES[i];
            // 2.动态添加多个添加覆盖物
            var polygon = new AMap.Polygon({
              path: area.path,
              strokeWeight: 6,
              strokeOpacity: 0.2,
              fillOpacity: 0.4,
              zIndex: 50,
              bubble: false,
              strokeStyle: "dashed",
            });
            if (area.areaId == 1) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#B3A6A1",
              });
            } else if (area.areaId == 2) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#A6B5C7",
              });
            } else if (area.areaId == 3) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#7A8BC3",
              });
            } else if (area.areaId == 4) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#BFA479",
              });
            } else if (area.areaId == 5) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#95A675",
              });
            } else if (area.areaId == 6) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#B9B36A",
              });
            } else if (area.areaId == 7) {
              polygon.setOptions({
                strokeColor: "#896A6A",
                fillColor: "#A7A2B1",
              });
            }
            this.map.add(polygon);
          }
          this.infoWindow = new AMap.InfoWindow({
            isCustom: true,
            offset: new AMap.Pixel(16, -45),
          });
          console.log("@points", points);
          for (var i = 0; i < points.length; i++) {
            var point = points[i];
            // 1.动态添加多个make点
            var text = new AMap.Text({
              text: i + 1,
              anchor: "center", // 设置文本标记锚点
              draggable: false,
              cursor: "pointer",
              angle: 10,
              style: createTextStyle(point.type),
              position: point.location,
            });
            text.on("click", function (e) {
              console.log("被点击", e);
              that.onMarkerClick(e);
            });
            // 2.动态添加多个添加覆盖物
            // var polygon = new AMap.Polygon({
            //   path: point.path,
            //   strokeWeight: 6,
            //   strokeOpacity: 0.2,
            //   fillOpacity: 0.4,
            //   zIndex: 50,
            //   bubble: false,
            //   strokeStyle: "dashed",
            // });
            // if (point.type === "1") {
            //   polygon.setOptions({
            //     strokeColor: "#0094D9",
            //     fillColor: "#76C1E1",
            //   });
            // } else if (point.type === "2") {
            //   polygon.setOptions({
            //     strokeColor: "#01913C",
            //     fillColor: "#7DC699",
            //   });
            // } else if (point.type === "3") {
            //   polygon.setOptions({
            //     strokeColor: "#E57915",
            //     fillColor: "#EBB17F",
            //   });
            // }
            this.map.add(text);
            // this.map.add(polygon);
          }
        })
        .catch((e) => {
          console.log(e);
        });
    },
    // 地图maker点点击事件
    onMarkerClick(e) {
      const targetPosition = e.target._position;
      let clickedItem = null;
 
      this.numberList.forEach((item) => {
        if (
          item.location[0] === targetPosition[0] &&
          item.location[1] === targetPosition[1]
        ) {
          clickedItem = item;
          return;
        }
      });
 
      if (clickedItem) {
        console.log("clickedItem", clickedItem);
        const typeName = this.getResidentialTypeName(clickedItem.type);
        const customInfo = `
        <div class='custom-info-window'>
    <!-- 标题区域 -->
    <div class='header-section'>
        <img src="https://webapi.amap.com/images/autonavi.png" alt="高德标志" style="float:left;width:67px;height:16px;">
        <!-- 新增关闭按钮 -->
        <span class='close-button'>关闭</span>
    </div>
 
    <!-- 主体内容区域 -->
    <div class='body-section'>
      <p class='detail-item'>小区名称 : ${clickedItem.name}</p>
      <p class='detail-item'>小区类型 : ${typeName}</p>
      <!-- 其他静态或动态信息 -->
      <!-- <p class='detail-item'>电话 : 010-84107000</p> -->
      <!-- <p class='detail-item'>邮编 : 223001</p> -->
      <p class='other-info'>其他信息:暂无</p>
    </div>
</div>
            <!-- 背景色 -->
        <style>
            .custom-info-window {
                background-color: white;
                padding: 15px;
                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
                font-size: 14px;
                line-height: 1.5;
                color: #333;
                overflow: hidden;
            }
            .header-section {
                display: flex;
                align-items: center;
                justify-content: space-between;
                margin-bottom: 10px;
            }
            .info-title h4, .community-type {
                margin: 0;
            }
            .detail-item, .other-info {
                margin-bottom: 5px;
            }
            .close-button {
              float: right;
              margin-left: 10px;
              cursor: pointer;
              color:#1296db;
              font-size: 0.225rem;
          }
        </style>
        `;
        const bindCloseEvent = () => {
          const closeButton = document.querySelector(
            ".custom-info-window .close-button"
          );
          closeButton.addEventListener("click", this.closeInfoWindow);
        };
        this.infoWindow.setContent(customInfo);
        this.infoWindow.open(this.map, e.target.getPosition());
        this.$emit("getChartInfo");
        bindCloseEvent();
      } else {
        console.log("No item found for the given position");
      }
    },
    // 住宅类型判断
    getResidentialTypeName(type) {
      switch (type) {
        case 1:
          return "一类住宅";
        case 2:
          return "二类住宅";
        case 3:
          return "三类住宅";
        default:
          return "未知类型";
      }
    },
    // 关闭信息窗体
    closeInfoWindow() {
      this.infoWindow.close();
    },
  },
};
</script>
 
<style scoped lang="scss">
#container {
  width: 100%;
  height: 100%;
}
</style>

高德开放平台第二期实战案例,三等奖作品

作者:谢洋

仅代表作者个人观点

相关推荐
小小坤4 分钟前
前端基于AI生成H5 vue3 UI组件
前端·javascript·vue.js
既见君子19 分钟前
透明视频
前端
竹等寒23 分钟前
Go红队开发—web网络编程
开发语言·前端·网络·安全·web安全·golang
lhhbk26 分钟前
angular中下载接口返回文件
前端·javascript·angular·angular.js
界面开发小八哥29 分钟前
数据可视化图表库LightningChart JS 全新发布v7.0——提高视觉质量
开发语言·javascript·信息可视化·图表·lightningchart
YUELEI11831 分钟前
Vue使用ScreenFull插件实现全屏切换
前端·javascript·vue.js
问道飞鱼1 小时前
【技术方案设计】H5埋点方案设计以及实现(入门版)
开发语言·javascript·h5·事件·埋点
我自纵横20231 小时前
第一章:欢迎来到 HTML 星球!
前端·html
发财哥fdy1 小时前
3.12-2 html
前端·html
ziyu_jia1 小时前
React 组件测试【React Testing Library】
前端·react.js·前端框架