前后端分离的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命令查看安装版本示意是否安装成功
接下来可以使用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)
})
})
}
最终效果
推荐阅读
-
Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之二 || 后端项目搭建
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之七使用JWT生成Token(个人见解)
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之十一Swagger使用一
-
解决vue+springboot前后端分离项目,前端跨域访问sessionID不一致导致的session为null问题
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之十数据库基础方法的封装
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之九如何进行用户权限控制
-
基于Vue的前后端分离项目实践
-
踩坑记:前后端分离的项目启动时间过长
-
前后端分离项目shiro的未登录和权限不足
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之六使用过滤器进行全局请求数据验证