欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前后端分离的webgis项目(二)

程序员文章站 2022-05-03 20:53:38
...

前后端分离的webgis项目(二)

二. 前端vue+leaflet
首先你得安装nodejs并配置环境,看这里,然后安装vue-cli,用它来快速新建项目
可以使用下列任一命令安装

npm install -g @vue/cli
# OR
yarn global add @vue/cli

yarn如果没有,要先安装,yarn的安装包的速度比npm快,建议使用,用下面的命令安装,注意在前面参考的文章配置了global-folder和cache-folder,因此yarn安装后也要配置,看这里,不然会出问题

npm install -g yarn

vue-cli安装完后,使用可以使用vue -V或vue --version命令查看安装版本示意是否安装成功
前后端分离的webgis项目(二)
接下来可以使用vue ui命令进行可视化创建,非常香,但是这里使用命令行创建项目,以便熟悉命令行操作
随意进入一个文件夹,cmd创建项目:vue create 你的项目名

vue create -n hello-world

在这里我加了一个参数-n用来跳过 git 初始化,详情参见官网

创建之后可以看到如下画面
前后端分离的webgis项目(二)
第一个是我之前创建项目保留的项目配置,可以在你的C盘用户文件夹下的.vuerc文件查看和修改配置
第二个是默认配置
第三个是手动选择配置
这里选择第二个默认配置

项目新建完毕后,安装路由使用下面命令

yarn add vue-router

安装axios用于前后端通信

yarn add axios

之后安装leaflet依赖包

yarn add vue2-leaflet

最后安装leaflet地图控制依赖包,如果下载的依赖包地图不全,可以在网上搜索chinesetmsproviders,然后copy覆盖下载的依赖包

yarn add leaflet.chinesetmsproviders

项目的目录如下
前后端分离的webgis项目(二)
main.js内容

import Vue from 'vue'
import App from './App.vue'
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
import router from './router'

Vue.prototype.$imgUrl = process.env.VUE_APP_IMAGES

Vue.config.productionTip = false

/* leaflet icon */
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
});

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

.env.development内容

## 配置 正式接口地址
VUE_APP_URL = "http://localhost:8082"
VUE_APP_IMAGES = "http://localhost:8083"

vue.config.js内容

const path = require("path");
const sourceMap = process.env.NODE_ENV === "development";

module.exports = {
    // 基本路径
    publicPath: "./",
    // 输出文件目录
    outputDir: "dist",
    // eslint-loader 是否在保存的时候检查
    lintOnSave: false,
    // webpack配置
    // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
    chainWebpack: () => {},
    configureWebpack: config => {
        if (process.env.NODE_ENV === "production") {
            // 为生产环境修改配置...
            config.mode = "production";
        } else {
            // 为开发环境修改配置...
            config.mode = "development";
        }

        Object.assign(config, {
            // 开发生产共同配置
            resolve: {
                extensions: [".js", ".vue", ".json", ".ts", ".tsx"],
                alias: {
                    vue$: "vue/dist/vue.js",
                    "@": path.resolve(__dirname, "./src")
                }
            }
        });
    },
    // 生产环境是否生成 sourceMap 文件
    productionSourceMap: sourceMap,
    // css相关配置
    css: {
        // 是否使用css分离插件 ExtractTextPlugin
        extract: true,
        // 开启 CSS source maps?
        sourceMap: false,
        // css预设器配置项
        loaderOptions: {},
        // 启用 CSS modules for all css / pre-processor files.
        modules: false
    },
    parallel: require("os").cpus().length > 1,
    pwa: {},
    // webpack-dev-server 相关配置
    devServer: {
        open: process.platform === "darwin",
        overlay: {
            warnings: false,
            errors: true
        },
        host: "localhost",
        port: 3001, //8080,
        https: false,
        hotOnly: false,
        proxy: {
            // 设置代理
            "/api": {
                target: process.env.VUE_APP_URL,
                changeOrigin: true,
                ws: true,
            }
        },
        before: app => {}
    },
};

VueLeaflet.vue内容

<template>
    <div class="vue-leaflet">
        <div class="map" ref="map"></div>
    </div>
</template>

<script>
    import { fetch } from "../utils/http-service";
    import 'leaflet.chinesetmsproviders'

    export default {
        name: "VueLeaflet",
        data() {
            return {
                map: '',
            };
        },
        mounted(){
            this.initMap();
            this.getSpot();

            window.addEventListener('scroll', function () {
                document.querySelector('body').setAttribute('style', 'margin: 0;')
            })
        },
        methods: {
            initMap(){
                const baselayers = this.addMulMap();
                const map =L.map(this.$refs.map, {
                    center: [28.22, 113.01],
                    zoom: 5,
                    layers: [baselayers.天地图],
                    zoomControl: false
                });
                this.map = map;
                L.control.zoom({
                    zoomInTitle: '放大',
                    zoomOutTitle: '缩小'
                }).addTo(map);

                L.control.layers(baselayers, null).addTo(map);
            },
            getSpot(){
                const map = this.map;
                const imgUrl = this.$imgUrl;
                const that = this;

                let naturalArr = [], cultureArr = [], parkArr = [], buildArr = [],
                    templeArr = [], ruinsArr = [], townsArr = [], cemeteryArr = [],
                    formerArr = [], religionArr = [];
                const result = fetch("/spot/virusdata");
                result.then(function (data) {
                    console.log(data);
                    for(let i=0; i<data.length; i++){
                        const t = data[i];
                        if(t.type==='自然风光'){
                            naturalArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='文化古迹'){
                            cultureArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='公园'){
                            parkArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='古建筑'){
                            buildArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='寺庙'){
                            templeArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='遗址'){
                            ruinsArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='古镇'){
                            townsArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='陵墓陵园'){
                            cemeteryArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='故居'){
                            formerArr.push(that.addToMarker(t, imgUrl));
                        }else if(t.type==='宗教'){
                            religionArr.push(that.addToMarker(t, imgUrl));
                        }
                    }
                    const naturalViews = L.layerGroup(naturalArr);
                    const cultureViews = L.layerGroup(cultureArr);
                    const parkViews = L.layerGroup(parkArr);
                    const buildViews = L.layerGroup(buildArr);
                    const templeViews = L.layerGroup(templeArr);
                    const ruinsViews = L.layerGroup(ruinsArr);
                    const townsViews = L.layerGroup(townsArr);
                    const cemeteryViews = L.layerGroup(cemeteryArr);
                    const formerViews = L.layerGroup(formerArr);
                    const religionViews = L.layerGroup(religionArr);
                    const overlayMaps = {
                        "自然风光": naturalViews,
                        "文化古迹": cultureViews,
                        "公园": parkViews,
                        "古建筑": buildViews,
                        "寺庙": templeViews,
                        "遗址": ruinsViews,
                        "古镇": townsViews,
                        "陵墓陵园": cemeteryViews,
                        "故居": formerViews,
                        "宗教": religionViews
                    };
                    map.addLayer(naturalViews);   // 默认添加自然风光景区到地图
                    L.control.layers(null, overlayMaps).addTo(map);
                })
            },
            addToMarker(t, imgUrl){
                const p = t.point.split(",");
                const lng = parseFloat(p[0]);
                const lat = parseFloat(p[1]);
                const l = L.marker([lat, lng]).bindPopup("<img alt="+t.name+" width='280' height='200' src="+imgUrl+"/"+t.data_id+".jpg"+"><h3>"+t.name+"</h3><span>"+t.level+" </span>" +
                    "<span>"+t.product_star_level+"</span><br/><span>类型:"+t.type+"</span><br/><span>地址:"+t.address+"</span><br/><span>"+t.intro+"</span>");
                return l;
            },
            addMulMap(){
                /**
                 * 智图地图内容
                 */
                const normalm1 = L.tileLayer.chinaProvider('Geoq.Normal.Map', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const normalm3 = L.tileLayer.chinaProvider('Geoq.Normal.PurplishBlue', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const normalm2 = L.tileLayer.chinaProvider('Geoq.Normal.Color', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const normalm4 = L.tileLayer.chinaProvider('Geoq.Normal.Gray', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const normalm5 = L.tileLayer.chinaProvider('Geoq.Normal.Warm', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const normalm6 = L.tileLayer.chinaProvider('Geoq.Normal.Cold', {
                    maxZoom: 18,
                    minZoom: 5
                });
                /**
                 * 天地图内容
                 */
                const normalm = L.tileLayer.chinaProvider('TianDiTu.Normal.Map', {
                        maxZoom: 18,
                        minZoom: 5
                    }),
                    normala = L.tileLayer.chinaProvider('TianDiTu.Normal.Annotion', {
                        maxZoom: 18,
                        minZoom: 5
                    }),
                    imgm = L.tileLayer.chinaProvider('TianDiTu.Satellite.Map', {
                        maxZoom: 18,
                        minZoom: 5
                    }),
                    imga = L.tileLayer.chinaProvider('TianDiTu.Satellite.Annotion', {
                        maxZoom: 18,
                        minZoom: 5
                    });

                const normal = L.layerGroup([normalm, normala]),
                    image = L.layerGroup([imgm, imga]);
                /**
                 * 谷歌
                 */
                const normalMap = L.tileLayer.chinaProvider('Google.Normal.Map', {
                        maxZoom: 18,
                        minZoom: 5
                    }),
                    satelliteMap = L.tileLayer.chinaProvider('Google.Satellite.Map', {
                        maxZoom: 18,
                        minZoom: 5
                    });
                /**
                 * 高德地图
                 */
                const Gaode = L.tileLayer.chinaProvider('GaoDe.Normal.Map', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const Gaodimgem = L.tileLayer.chinaProvider('GaoDe.Satellite.Map', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const Gaodimga = L.tileLayer.chinaProvider('GaoDe.Satellite.Annotion', {
                    maxZoom: 18,
                    minZoom: 5
                });
                const Gaodimage = L.layerGroup([Gaodimgem, Gaodimga]);

                const baseLayers = {
                    "智图地图": normalm1,
                    "智图多彩": normalm2,
                    "智图午夜蓝": normalm3,
                    "智图灰色": normalm4,
                    "智图暖色": normalm5,
                    "智图冷色": normalm6,
                    "天地图": normal,
                    "天地图影像": image,
                    "谷歌地图": normalMap,
                    "谷歌影像": satelliteMap,
                    "高德地图": Gaode,
                    "高德影像": Gaodimage,
                };
                return baseLayers;
            }
        },
        //创建前设置
        beforeCreate () {
            document.querySelector('body').setAttribute('style', 'margin: 0;')
        },
        //销毁前清除
        beforeDestroy () {
            document.querySelector('body').removeAttribute('style')
        },
    };
</script>

<style scoped>
    .vue-leaflet {
        width: 100vw;
        height: 100vh;
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 0;
    }
    .map{
        width: 100vw;
        height: 100vh;
    }
</style>

index.js内容

import Vue from 'vue'
import VueRouter from 'vue-router'
//自定义页面
import VueLeaflet from "../views/VueLeaflet";

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/vueLeaflet'   // 重定向
  },
  {
    path: '/vueLeaflet',
    name: 'vueLeaflet',
    component: VueLeaflet
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'hash',
  base: process.env.BASE_URL,
  routes
})

export default router

http-service.js内容

import axios from 'axios'

axios.defaults.timeout = 5000;  //请求超时设置
axios.defaults.baseURL = process.env.VUE_APP_URL

//http request 拦截器
axios.interceptors.request.use(
    config => {
        config.data = JSON.stringify(config.data);
        config.headers = {
            'Content-Type':'application/x-www-form-urlencoded'
        }
        return config;
    },
    error => {
        return Promise.reject(err);
    }
);

//响应拦截器即异常处理
axios.interceptors.response.use(response => {
    return response
}, err => {
    if (err && err.response) {
        switch (err.response.status) {
            case 400:
                console.log('错误请求')
                break;
            case 401:
                console.log('未授权,请重新登录')
                break;
            case 403:
                console.log('拒绝访问')
                break;
            case 404:
                console.log('请求错误,未找到该资源')
                break;
            case 405:
                console.log('请求方法未允许')
                break;
            case 408:
                console.log('请求超时')
                break;
            case 500:
                console.log('服务器端出错')
                break;
            case 501:
                console.log('网络未实现')
                break;
            case 502:
                console.log('网络错误')
                break;
            case 503:
                console.log('服务不可用')
                break;
            case 504:
                console.log('网络超时')
                break;
            case 505:
                console.log('http版本不支持该请求')
                break;
            default:
                console.log(`连接错误${err.response.status}`)
        }
    } else {
        console.log('连接到服务器失败')
    }
    return Promise.resolve(err.response)
})


/**
 * 封装get方法
 * @param url
 * @param data
 * @returns {Promise}
 */
export function fetch(url,params={}){
    return new Promise((resolve,reject) => {
        axios.get(url,{
            params:params
        })
            .then(response => {
                resolve(response.data);
            })
            .catch(err => {
                reject(err)
            })
    })
}

/**
 * 封装post请求
 * @param url
 * @param data
 * @returns {Promise}
 */
export function post(url,data = {}){
    return new Promise((resolve,reject) => {
        axios.post(url,data)
            .then(response => {
                resolve(response.data);
            },err => {
                reject(err)
            })
    })
}

最终效果
前后端分离的webgis项目(二)