当前位置: 首页 > article >正文

基于Express+vue+高德地图API实现的出行可视化APP

基于Express+vue+高德地图API实现的出行可视化APP

1.项目简介

解决出行问题,用于出行行程记录,路线规划,数据可视化分析的移动端webapp

1.1技术栈

  • 前端:移动端,vue全家桶,Mand组件库,Echarts.js,Scss
  • 后端:Node,Express框架,高德地图API
  • 数据库:Mysql

1.2功能模块

1.2.1个人出行

用户个人出行,不确定路程、目的地等信息,选择出行工具,点击开始,系统实时监听用户手机位置得到WGS84经纬度坐标(w3c HTML5 Geolocation地理定位标准),行程结束,记录本次出行信息,经纬度坐标转换GCJ-02坐标体系,通过高德地图提供三方API绘制出行轨迹。

1.2.2公共交通

用户确定出发地、目的地、交通工具,选择公共交通出行,用户输入位置关键字,选择合适出发/目的位置,选择乘坐交通工具,规划出行路线,选择某一条路线,确定后点击保存上传本次出行记录

1.2.3历史列表

按时间顺序查看所有出行的历史记录,可查看出行的详情信息、行程轨迹、路线规划

1.2.4我的信息(未完成)

查看我的详情信息,通过出行数据分析得到的出行趋势折线图,与出行数据相关的数据分析图表,其他功能未写

2.项目构建

2.1前端

前端在vue-cli3基础上开发,在此之上根据项目需求对项目工程作出几点修改,前端代码在view/文件夹中

  • 移动端适配:对移动端分辨率用webpack在工程中配置。
  • 请求拦截器:在view/src/request/中,基于axios提供的interceptors对所有ajax的请求和响应添加相应操作,如请求头添加,token添加,响应后台错误状态码的识别与报错;简单封装了下axios请求,主要为get,post两种。
  • 导航守卫:在view/src/router/中,做了全局导航守卫,未登录用户只能访问项目登录页面。
  • 工具类:在view/src/utils/中,对常用枚举值、全局组件注册、常用类封装等功能做模块化封装。
  • css样式:在view/src/style/中,全局公共样式,初始化样式。
  • svg组件:在view/src/icons/中,封装用于svg展示组件,用做小icon的展示,svg保存该文件夹中。
  • 模块化:对路由与vuex做模块化封装。
  • 地图:所有地图、地理信息、轨迹、路线规划功能有高德地图第三方API提供

2.2后端

  • 使用Nodeexpress框架,连接Mysql数据库,做数据接口开发,数据的增删改查与简单封装。

3.系统设计

3.1项目配置

const path = require('path')

function resolve (dir) {
  return path.join(__dirname, './', dir)
}

module.exports = {
  chainWebpack: config => {
    // 为src下文件配别名,不使用相对路径
    config.resolve.alias
      .set('@', resolve('src'))
      .set('assets', resolve('src/assets'))
      .set('components', resolve('src/components'))
      .set('views', resolve('src/views'))
      .set('icons', resolve('src/icons'))
      .set('router', resolve('src/router'))
      .set('utils', resolve('src/utils'))
      .set('style', resolve('src/style'))

    /** 设置处理svg的router,使svg可直接用名称调用,无需路径 */
    // svg rule loader
    const svgRule = config.module.rule('svg') // 找到svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加svg新的loader处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
  },
  publicPath: './',
  /** 开发环境代理 */
  devServer: {
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        ws: true,
        changeOrigin: true,
        pathRequires: {
          '^/api': ''
        }
      }
    }
  }
}

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var userRouter = require('./routes/user');
var tripRouter = require('./routes/trip');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/user', userRouter);
app.use('/trip', tripRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

3.2路由转发

var express = require('express');
var fs = require("fs");
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {


        // res.statusCode = 200;
        // res.setHeader("Access-Control-Allow-Origin", "*");
        // res.setHeader('Content-Type', 'application/json');
        // res.json(data);

function readImage(path,res){
  console.log(path)
  fs.readFile(path,'binary',function(err,  file)  {
      if  (err)  {
          console.log(err);
          return;
      }else{
          console.log("输出文件");
          res.writeHead(200,  {'Content-Type':'image/jpeg'});
          res.write(file,'binary');
          res.end();
      }
  });
}
readImage('/public/images/head.jpeg',res)
});

module.exports = router;

3.3请求拦截

import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
import store from './store/index'

import './icons'
// 注册全局组件
import registerComponents from './utils/registerComponents'
import mandMobile from 'mand-mobile'
import 'mand-mobile/lib/mand-mobile.css'
// 请求拦截器
import requestPlugin from './request/http.js'

Vue.use(mandMobile)

Vue.use(registerComponents)
Vue.use(requestPlugin)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

3.4正则验证规则

/** 正则验证规则 */

/* 合法uri */
export function validateURL (textval) {
  const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  return urlregex.test(textval)
}

/* 小写字母 */
export function validateLowerCase (str) {
  const reg = /^[a-z]+$/
  return reg.test(str)
}

/* 大写字母 */
export function validateUpperCase (str) {
  const reg = /^[A-Z]+$/
  return reg.test(str)
}

/* 大小写字母 */
export function validatAlphabets (str) {
  const reg = /^[A-Za-z]+$/
  return reg.test(str)
}

export function getTime () {
  let now = new Date()
  let year = now.getFullYear() // 年
  let month = now.getMonth() + 1 // 月
  let day = now.getDate() // 日

  let hh = now.getHours() // 时
  let mm = now.getMinutes() // 分
  let ss = now.getSeconds()

  let time = {
    date1: `${year}年${month}月${day}日`,
    date2: `${year}/${month}/${day}`,
    date3: `${year}年${month}月${day}日/${hh}:${mm}:${ss}`,
    date4: `${hh}:${mm}:${ss}`
  }
  return time
}

4.项目运行

# 分别进入view/,server/文件夹分别下载依赖
cd view/server

npm install

# 导入mysql数据库表

# view下前端项目 打开端口localhost:8080
npm run serve
# 也可以不运行后端,项目已上线会访问线上接口,想连接本地服务器需改变接口地址
# serve下运行后端项目(必须先导入数据库表)
DEBUG=myapp:* npm start

# 前端项目打包
npm run build

5.项目截图

私人出行

公共交通

历史列表

我的信息

6.小结

6.1项目简结

  • 难度:简单
  • 开发时长:前期调研,编码一周
  • 关键字:移动端,出行,可视化,高德地图,Echart图表

6.2过程总结

  • 想法产出:因为在滴滴出行的实习经历,准备做款有关出行平台的,有关前端可视化的产品。
  • 需求调研:结合出行 可视化 关键字做需求分析,调研悦动圈悦跑圈滴滴出行百度地图高德地图确定几个主要功能
    1. 实时定位,绘制出行轨迹(悦跑圈,已完成)
    2. 路线规划,规划路线绘制路径图(百度地图,已完成)
    3. 网约车,快车,专车,顺风车在线叫车(滴滴出行,未完成)
    4. 可视化分析,出行数据的可视化分析(已完成)
  • 技术调研
    1. 选取HTML5 Geolocation提供的物理位置实时监听功能,获取到WGS84经纬坐标
    2. 选取高德地图第三方API提供地图,地理位置,轨迹绘制,路线规划等功能
    3. 选择开发移动端项目,用滴滴的Mand作为移动端UI组件库
    4. 选取Node为服务端,Express为后端框架,Mysql为数据库

6.3难点总结

产品从无到有是最困难的,项目虽然不难,可前期产品调研,技术调研,项目构建确花费了大量时间,相比照着成品写多了很多不一样的体验

6.4补充

注:因为马上毕业,这只是为了应对毕业设计临时写的小项目,没想到会有这么多star,时间有限主要实现核心功能,产品还是很粗糙,我会找时间完善下,谢谢

因为浏览器端定位本身受限因素很多,定位成功率也不是很高,很可能出现没有权限,定位失败问题,可尝试开启权限或更换浏览器。


http://www.kler.cn/a/487628.html

相关文章:

  • 【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜
  • 【马来西亚理工大学主办,ACM出版】2025年大数据、通信技术与计算机应用国际学术会议(BDCTA 2025)
  • 微信小程序map组件所有markers展示在视野范围内
  • RK3562编译Android13 ROOT固件教程,触觉智能开发板演示
  • Numpy数组的属性
  • GoChina备案管家
  • 升级 Spring Boot 3 配置讲解 — JDK 23 会给 SpringBoot 带来什么特性?
  • Hadoop常见面试题
  • RabbitMQ发布确认高级篇(RabbitMQ Release Confirmation Advanced Edition)
  • Azure Synapse Analytics和Azure Databricks的共同点和区别
  • 岚图N次方KOC项目复盘总结---记录踩坑日记
  • 网络授时笔记
  • 30天开发操作系统 第 12 天 -- 定时器 v1.0
  • Jenkins使用入门
  • 保护性暂停原理
  • 刷式直流电机驱动芯片,适用于打印机、电器、工业设备以及其他小型机器中——GC8870
  • 解决Vscode中使用netdb.h的getaddrinfo和addrinfo会报错的方法
  • 【HTML+CSS+JS+VUE】web前端教程-9-列表标签之有序列表
  • Seed-TTS: A Family of High-Quality Versatile Speech Generation Models
  • ChatGPT网络错误如何解决
  • Leetcode 931. 下降路径最小和 动态规划
  • Windows安装ES单机版设置密码
  • golang 的 panic
  • Vue.js Ajax(vue-resource)
  • XML通过HTTP POST 请求发送到指定的 API 地址,进行数据回传
  • 【算法】一阶低通滤波