前後端分離的webgis項目(二)

前後端分離的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命令查看安裝版本示意是否安裝成功
在這裏插入圖片描述
接下來可以使用vue ui命令進行可視化創建,非常香,但是這裏使用命令行創建項目,以便熟悉命令行操作
隨意進入一個文件夾,cmd創建項目:vue create 你的項目名

vue create -n hello-world

在這裏我加了一個參數-n用來跳過 git 初始化,詳情參見官網

創建之後可以看到如下畫面
在這裏插入圖片描述
第一個是我之前創建項目保留的項目配置,可以在你的C盤用戶文件夾下的.vuerc文件查看和修改配置
第二個是默認配置
第三個是手動選擇配置
這裏選擇第二個默認配置

項目新建完畢後,安裝路由使用下面命令

yarn add vue-router

安裝axios用於前後端通信

yarn add axios

之後安裝leaflet依賴包

yarn add vue2-leaflet

最後安裝leaflet地圖控制依賴包,如果下載的依賴包地圖不全,可以在網上搜索chinesetmsproviders,然後copy覆蓋下載的依賴包

yarn add leaflet.chinesetmsproviders

項目的目錄如下
在這裏插入圖片描述
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)
            })
    })
}

最終效果
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章