记录我的webgis项目(4):从零开始,构建电子地图网站 spring boot3 +vue3 + leaflet之前端搭建
(依旧还是叠甲,我也是菜鸟,悉知我的功力还很浅薄,如果有哪里做的不行欢迎批评指正,大部分代码都是借助AI辅助编写的,可能会有些问题,遇到可留言指正)
Day 4:
终于要开始进行前端搭建了QAQ,前端框架很多,我们选择vue3(绝对不是因为我只会这个),关于webgis方面的我们就选择leaflet,(下次整openlayers),先梳理一下思路,前端 搭建大致以下几个步骤:
话不多说我们直接进入正题
第一步:安装各种插件
1.node.js
1.下载
下载地址:Node.js — 下载 Node.js® 下个吻合电脑版本号,下完一路next就行,下完 win+r 输入cmd打开控制台

能显示出这俩就算完成了
2.环境配置
说明:这里的环境配置主要配置的是npm安装的全局模块所在的路径,以及缓存cache的路径,之所以要配置,是因为以后在执行类似:npm install express [-g] (后面的可选参数-g,g代表global全局安装的意思)的安装语句时,会将安装的模块安装到【C:\Users\用户名\AppData\Roaming\npm】路径中,占C盘空间。
例如:我希望将全模块所在路径和缓存路径放在我node.js安装的文件夹中,则在我安装的文件夹【D:\Develop\nodejs】下创建两个文件夹【node_global】及【node_cache】如下图:

创建完两个空文件夹之后,打开cmd命令窗口,输入:
路径就是你创的文件夹的路径
npm config set prefix "D:\Develop\nodejs\node_global"
npm config set cache "D:\Develop\nodejs\node_cache"
接下来设置环境变量,关闭cmd窗口,“我的电脑”-右键-“属性”-“高级系统设置”-“高级”-“环境变量”

进入环境变量对话框,在【系统变量】下新建【NODE_PATH】,输入【D:\Develop\nodejs\node_global\node_modules】,将【用户变量】下的【Path】修改为【D:\Develop\nodejs\node_global】




配置完后,安装个module测试下,我们就安装最常用的express模块,打开cmd窗口,
输入如下命令进行模块的全局安装:
npm install express -g # -g是全局安装的意思

ok,结束
2.Vite(替代webpack 和 vue-cli)
1.切换路径
在我们的工程文件夹下建一个git文件夹,cmd切换到 c:\Users\zhou0\Desktop\MyMap\git 路径下,我把前端工程放在这里。
输入:
cd c:\Users\zhou0\Desktop\MyMap
2.全局安装vite
输入:
npm install -g vite@8.0.10
到此准备工作就差不多了,我们就可以开始搭建我们的前端。
第二步:前端搭建
1.vite脚手架新建项目
在cmd中输入:
npm create vite@latest vue-gismap -- --template vue

意思需要安装自带的内些页面吗,我们直接y

项目名字:vue-gismap

前端框架,你猜选啥,肯定是vue啊

语言就选js吧ts有点麻烦

用npm下载然后运行吗?直接回车

我们按住ctrl+左键点击这个地址打开我们的初始页面

就长这样,算是完成了,我们开始下一步
2. 安装element plus (vue3不支持element)
在终端里输入:
normalize是icon图标
npm install element-plus @element-plus/icons-vue
npm install normalize.css
npm install vue-router //安装路由
3. 开始布局
1. 准备工作
样式效果一样一样来,先做一下页面布局。大概长这个样子:顶部三个tab标签,历史地图、专题地图、帮助文档;左侧一栏树状复选框,用以选择朝代,中间部分是地图,右侧是一个可隐藏的表,用以显示属性。
布局采用了layout+container,路由用的是router,左侧的树状结构是tree控件,右侧点击“打开嵌套表格的drawer”,弹出的是drawer控件,内嵌table控件。
文件结构如下图所示,src文件夹下有components、js、router三个文件夹。安装完vue脚手架,在src文件夹下会生成一些默认的文件夹和组件,不需要的直接删掉就可以,右键——new file/new folder,新建需要的文件和文件夹即可。

Vite 项目初始 src 下只有 assets/、components/ 以及 App.vue、main.js。我们需要手动创建:
-
src/router/index.js -
src/js/treedata.js -
src/components/Header.vue、HistoryMap.vue、SpecialMap.vue、HelperDocument.vue

在vite.config.js里面我们弄一些配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) // 路径别名,便于引用
}
}
})
2. main.ts
这个文件,主要是把用到的一些依赖导入,这些依赖都在文件夹node_modules下。
原教程用的vue2
Vue3 的入口写法完全不同,没有 new Vue(),而是通过 createApp 创建应用。Element Plus 的引入路径也变了,且全局注册图标的方式不同。完整改写为:
import { createApp } from 'vue' // ▶ Vue 3 使用 createApp
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' // ▶ 样式路径变化
import 'normalize.css'
// ▶ 不再使用 vue-awesome,改用 element-plus 图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import router from './router'
const app = createApp(App)
// 全局注册所有图标,方便在模板中直接使用 <el-icon><Menu /></el-icon>
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus)
app.use(router)
app.mount('#app')
3. App.vue
因为浏览器自带的滚动条太丑,所以在这里更改了全局的滚动条。<router-view></router-view>引入的是路由视图。
<template>
<div id="app">
<el-container>
<!-- 两个容器形成上下结构 -->
<el-header class="header">
<Header />
</el-header>
<el-container>
<!-- 两个容器形成上下结构 -->
<el-main class="el-main">
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>//使用语法糖引入 Header组件
import Header from '@/components/Header.vue' // 引入 Header组件,之后直接就能在template中使用了
</script>
<style>
/* 样式 */
.header {
background-color: #c00000;
color: #fff;
line-height: 60px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
height: calc(100vh - 60px); //地图部分高度和视口一样大
}
/*更改全局的滚动条样式*/
::-webkit-scrollbar {
width: 8px;
height: 8px;
background-color: #fff;
}
::-webkit-scrollbar-thumb {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: rgba(0, 0, 0, .1);
}
</style>
4.Header.vue
这个就是主页面菜单栏,历史地图、专题地图、帮助文档。<el-row><el_col>用layout进行了一个布局。<el-menu>引入菜单。<router-link>引入路由,点击跳转页面。
以下是Header.vue 的代码
<template>
<el-row :gutter="20">
<el-col :span="4">
<div>中国历史地图</div>
</el-col>
<el-col :span="16">
<el-menu class="el-menu-demo" mode="horizontal" background-color="#c00000" text-color="#fff"
active-text-color="#000">
<el-menu-item index="1">
<span><router-link to="/">历史地理</router-link></span>
</el-menu-item>
<el-menu-item index="2">
<span><router-link to="/specialmap">专题地图</router-link></span>
</el-menu-item>
<el-menu-item index="3">
<span><router-link to="/helperdocument">帮助文档</router-link></span>
</el-menu-item>
</el-menu>
</el-col>
</el-row>
</template>
<style>
.el-row {
/* margin-top:10px;*/
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
.el-col {
border-radius: 4px;
}
.bg-purple {
background: #d3dce6;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
/* 去掉菜单栏底部白边 */
.el-menu--horizontal {
border-bottom: none !important;
}
.el-menu-item {
border-bottom: none !important;
}
.el-header {
padding: 0;
}
</style>
5. router/index.js
路由信息放在这个文件中。Path是url路径,是一个相对路径,component是组件页面。例如点击Header.vue中的帮助文档,就会跳转到HelperDocument.vue页面,url: http://localhost:8080/#/helperdocument
-
Vue Router 4(对应 Vue3)的导入方式改变:
import { createRouter, createWebHashHistory } from 'vue-router' -
不再通过
new Router()创建实例,而是使用createRouter。 -
路由历史模式保留 hash 模式,使用
createWebHashHistory。 -
组件导入建议加
.vue后缀(Vite 要求)。
import { createRouter, createWebHashHistory } from 'vue-router' // ▶ Vue Router 4
import HistoryMap from '@/components/HistoryMap.vue'
import SpecialMap from '@/components/SpecialMap.vue'
import HelperDocument from '@/components/HelperDocument.vue'
const routes = [
{ path: '/', name: 'historymap', component: HistoryMap },
{ path: '/specialmap', name: 'specialmap', component: SpecialMap },
{ path: '/helperdocument', name: 'helperdocument', component: HelperDocument }
]
const router = createRouter({ // ▶ 新式创建
history: createWebHashHistory(), // hash 模式,保持 URL #/xxx 风格
routes
})
export default router
6. HistoryMap.vue
这里主要两个控件,一个是<el-tree>,它的数据单独放在了js/treedata.js中了。一个是<el-drawer>,数据在<script>的gridData中。methods 中有一个方法changecheck(e,data),e是当前选中的eltree节点,这个方法的用处是,对于复选框状态来说,父节点直接影响子节点。
老师教程Element Plus 中 el-drawer 的 .sync 修饰符不再支持,需改用 v-model。将 :visible.sync="table" 改为 v-model="table"。
<template>
<div>
<!-- 左侧树结构容器:白色背景、高度75%屏幕、宽度200px、左浮动 -->
<div style="background-color: #fff; height: 75vh;width: 200px;float:left;">
<!-- Element 滚动条组件,让树内容太多时可以滚动 -->
<el-scrollbar style="height: 100%;">
<!--
el-tree:树形控件
:data="treedata" 绑定树的数据
show-checkbox 显示复选框
node-key="id" 每个节点唯一标识用 id
:props="defaultProps" 配置子节点、显示文字用哪个字段
@check="changecheck" 勾选/取消勾选时触发的方法
-->
<el-tree :data="treedata" show-checkbox node-key="id" :props="defaultProps" @check="changecheck"></el-tree>
</el-scrollbar>
</div>
<!--
el-drawer:右侧抽屉组件
v-model="table" 控制抽屉显示/隐藏(true显示/false隐藏)
direction="rtl" 从右往左弹出
size="20%" 抽屉宽度占屏幕20%
-->
<el-drawer title="属性" v-model="table" direction="rtl" size="20%">
<el-table :data="gridData" height=85vh border>
<!-- 表格列:field 字段名 -->
<el-table-column property="field" label="字段" width="100"></el-table-column>
<!-- 表格列:value 字段值 -->
<el-table-column property="value" label="值" width="200"></el-table-column>
</el-table>
</el-drawer>
<!-- 按钮:点击让 table = true → 打开抽屉 -->
<el-button type="text" @click="table = true">打开嵌套表格的 Drawer</el-button>
</div>
</template>
<script>
// 导入外部静态树数据(写死的数据)
import { treedata } from '../js/treedata.js'
export default {
// 组件数据
data() {
return {
treedata: treedata, // 树的数据
defaultProps: { // 树的配置
children: 'children', // 子节点用 children 字段
label: 'label' // 显示文字用 label 字段
},
table: false, // 控制右侧抽屉显示隐藏
// ======================
// 表格数据:纯写死数据
// 不管点哪个树节点,都只显示这些内容
// ======================
gridData: [
{ field: 'name_py', value: 'Baode Zhou' },
{ field: 'name_ch', value: '保德州' },
{ field: 'name_ft', value: '保德州' },
{ field: 'pres_loc', value: '山西保德县城西侧' },
{ field: 'type_ch', value: '州' },
{ field: 'lev_rank', value: '6' },
{ field: 'beg_yr', value: 1376 },
{ field: 'beg_rule', value: '4' },
{ field: 'end_yr', value: 1911 },
{ field: 'geo_src', value: 'FROM_AC' },
{ field: 'compiler', value: '' },
{ field: 'gecomplr', value: '' },
{ field: 'checker', value: '' },
{ field: 'ent_date', value: '' },
{ field: 'beg_chg_ty', value: '更名' },
{ field: 'end_chg_ty', value: '数据下限' }
]
}
},
// 方法集合
methods: {
// 树节点勾选时触发
changecheck(e, data) {
// 如果是 1级节点
if (e['level'] === 1) {
e['ischeck'] = !e['ischeck']; // 切换自身选中状态
// 循环所有二级子节点 → 跟着选中/取消
for (var i = 0; i < e['children'].length; i++) {
e['children'][i]['ischeck'] = e['ischeck'];
// 循环三级子节点 → 跟着选中/取消
for (var j = 0; j < e['children'][i]['children'].length; j++) {
e['children'][i]['children'][j] = e['ischeck'];
}
}
}
// 如果是 2级节点
else if (e['level'] === 2) {
e['ischeck'] = !e['ischeck'];
// 让下面3个子节点跟着选中/取消
e['children'][0]['ischeck'] = e['ischeck'];
e['children'][1]['ischeck'] = e['ischeck'];
e['children'][2]['ischeck'] = e['ischeck'];
}
// 3级节点(叶子节点)
else {
e['ischeck'] = !e['ischeck']; // 只切换自己
}
console.log(e); // 打印当前节点信息
}
}
};
</script>
7. treedata.js
eltree的数据放在这里。
export const treedata = [
{
id: 0,
label: "全部内容",
ischeck: false,
level: 1,
timerange: [-5000, 1840],
children: [
{
id: 1,
label: "商周",
ischeck: false,
level: 2,
timerange: [-5000, -221],
children: [
{ id: 2, label: "cnty点", ischeck: false, level: 3, timerange: [-5000, -221] },
{ id: 3, label: "pref点", ischeck: false, level: 3, timerange: [-5000, -221] },
{ id: 4, label: "pref面", ischeck: false, level: 3, timerange: [-5000, -221] }
]
},
{
id: 5,
label: "秦",
ischeck: false,
level: 2,
timerange: [-220, -202],
children: [
{ id: 6, label: "cnty点", ischeck: false, level: 3, timerange: [-220, -202] },
{ id: 7, label: "pref点", ischeck: false, level: 3, timerange: [-220, -202] },
{ id: 8, label: "pref面", ischeck: false, level: 3, timerange: [-220, -202] }
]
},
{
id: 9,
label: "汉",
ischeck: false,
level: 2,
timerange: [-201, 220],
children: [
{ id: 10, label: "cnty点", ischeck: false, level: 3, timerange: [-201, 220] },
{ id: 11, label: "pref点", ischeck: false, level: 3, timerange: [-201, 220] },
{ id: 12, label: "pref面", ischeck: false, level: 3, timerange: [-201, 220] }
]
},
{
id: 13,
label: "晋三国",
ischeck: false,
level: 2,
timerange: [221, 589],
children: [
{ id: 14, label: "cnty点", ischeck: false, level: 3, timerange: [221, 589] },
{ id: 15, label: "pref点", ischeck: false, level: 3, timerange: [221, 589] },
{ id: 16, label: "pref面", ischeck: false, level: 3, timerange: [221, 589] }
]
},
{
id: 17,
label: "隋唐",
ischeck: false,
level: 2,
timerange: [590, 907],
children: [
{ id: 18, label: "cnty点", ischeck: false, level: 3, timerange: [590, 907] },
{ id: 19, label: "pref点", ischeck: false, level: 3, timerange: [590, 907] },
{ id: 20, label: "pref面", ischeck: false, level: 3, timerange: [590, 907] }
]
},
{
id: 21,
label: "五代十国",
ischeck: false,
level: 2,
timerange: [908, 960],
children: [
{ id: 22, label: "cnty点", ischeck: false, level: 3, timerange: [908, 960] },
{ id: 23, label: "pref点", ischeck: false, level: 3, timerange: [908, 960] },
{ id: 24, label: "pref面", ischeck: false, level: 3, timerange: [908, 960] }
]
},
{
id: 25,
label: "宋",
ischeck: false,
level: 2,
timerange: [961, 1276],
children: [
{ id: 26, label: "cnty点", ischeck: false, level: 3, timerange: [961, 1276] },
{ id: 27, label: "pref点", ischeck: false, level: 3, timerange: [961, 1276] },
{ id: 28, label: "pref面", ischeck: false, level: 3, timerange: [961, 1276] }
]
},
{
id: 29,
label: "元",
ischeck: false,
level: 2,
timerange: [1277, 1368],
children: [
{ id: 30, label: "cnty点", ischeck: false, level: 3, timerange: [1277, 1368] },
{ id: 31, label: "pref点", ischeck: false, level: 3, timerange: [1277, 1368] },
{ id: 32, label: "pref面", ischeck: false, level: 3, timerange: [1277, 1368] }
]
},
{
id: 33,
label: "明",
ischeck: false,
level: 2,
timerange: [1369, 1644],
children: [
{ id: 34, label: "cnty点", ischeck: false, level: 3, timerange: [1369, 1644] },
{ id: 35, label: "pref点", ischeck: false, level: 3, timerange: [1369, 1644] },
{ id: 36, label: "pref面", ischeck: false, level: 3, timerange: [1369, 1644] }
]
},
{
id: 37,
label: "清",
ischeck: false,
level: 2,
timerange: [1645, 1840],
children: [
{ id: 38, label: "cnty点", ischeck: false, level: 3, timerange: [1645, 1840] },
{ id: 39, label: "pref点", ischeck: false, level: 3, timerange: [1645, 1840] },
{ id: 40, label: "pref面", ischeck: false, level: 3, timerange: [1645, 1840] }
]
}
]
}
];
8. SpecialMap和HelperDocument
SpecialMap
<template>
<div>专题地图</div>
</template>
HelperDocument
<template>
<div>帮助文档</div>
</template>
这两个预留,可能以后会用专题地图这个页面做一个三维的地图(老师好像到最后也没做,但是我可以给老师填个坑)。
4. 整合leaflet
我们大概要做这样的地图底图,腾讯底图,上面覆盖物有点point和面polygon,点击覆盖物会出现触发事件。页面长成这个样子,因为仅作示例用,所以写在了helpdocument上面,除了地图外,覆盖物有一个点、一个标签、一个面,右侧是chrome浏览器的开发者工具(crtl+shift+i),触发事件在工具上(console)打印了日志(老师原话加老师原图)

1. 安装leaflet模块
首先要安装leaflet模块。 Ctrl+c,退出项目运行。 然后在cmd运行:
npm install leaflet
2. 加载腾讯底图
1.瓦片原理
Leaflet加载底图以栅格瓦片的形式加载。官方示例是这样的:
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
其中{}中的s是源,z是比例尺,xy是瓦片的坐标,加载的是openstreetmap。地图瓦片是比较基础的概念,为了提升性能,将地图按照比例尺逐级切成瓦片,调用的时候,根据比例尺和网页尺寸调用需要调用的瓦片。关于地图瓦片,开放地理信息空间联盟OGC专门有规范WMTS对其进行阐述,国内的高德、腾讯地图瓦片切分方式,完全按照WMTS来,但是谷歌和mapbox、OSM等稍有变化。因为leaflet默认加载的是谷歌地图这样的瓦片,所以要加载腾讯地图,还需要对瓦片函数进行一个转换,才能正确加载。
2.程序源码
新建一个 src/js/txTileLayer.js 文件,放瓦片函数。
// 自定义地图瓦片图层:继承 Leaflet 原生 L.TileLayer
// 作用:自定义瓦片地图的 URL 拼接规则(让地图瓦片加载更灵活)
export const TXTileLayer = L.TileLayer.extend({
// 重写【获取瓦片 URL】的核心方法
getTileUrl: function (tilePoint) {
// 定义变量:存储最终要拼接进 URL 的参数(x,y,z等)
let urlArgs = null
// 从配置项里取出用户传入的 getUrlArgs 方法
let getUrlArgs = this.options.getUrlArgs
// ======================================
// 如果外部传入了自定义 getUrlArgs 函数
// 就用外部自定义的方式生成 url 参数
// ======================================
if (getUrlArgs) {
urlArgs = getUrlArgs(tilePoint)
}
// ======================================
// 否则使用默认规则:z/x/y
// 这是标准瓦片地图的格式
// ======================================
else {
urlArgs = {
z: tilePoint.z, // 地图层级
x: tilePoint.x, // 瓦片横向坐标
y: tilePoint.y // 瓦片纵向坐标
}
}
// ======================================
// 拼接最终瓦片 URL 并返回
// 1. L.Util.template:把参数填充到 url 模板里
// 2. L.extend:合并参数(urlArgs + 配置项 + 子域名)
// 3. this._getSubdomain:获取子域名(用于多域名加速加载)
// ======================================
return L.Util.template(this._url, L.extend(urlArgs, this.options, {
s: this._getSubdomain(tilePoint)
}))
}
})
在 src/icon 中放一个 dian.png,这个是点的图标(推荐去阿里矢量图标库下iconfont-阿里巴巴矢量图标库)。
剩下的所有都写在了 src/components/HelperDocument.vue 中。(测试的一个地图)
<template>
<div id="map"></div>
</template>
<script>
import L from 'leaflet' // ▶ 引入 Leaflet 主对象
import 'leaflet/dist/leaflet.css' // 样式不变
import { TXTileLayer } from '../js/txTileLayer.js'
import dianIcon from '../icon/dian.png' // ▶ 替换 require('../icon/dian.png')
import markerShadow from 'leaflet/dist/images/marker-shadow.png' // ▶ 替换 require('leaflet/dist/images/marker-shadow.png')
export default {
data() {
return {
map: null
}
},
methods: {
initLeaflet() {
this.map = L.map('map', {
center: [39.916527, 116.397128],
zoom: 10,
maxZoom: 23,
minZoom: 3
})
//按照新定义瓦片规则加载底图
let url = 'http://rt1.map.gtimg.com/realtimerender/?z={z}&x={x}&y={y}&type=vector&style=1&v=1.1.1'
let getUrlArgs = function(tilePoint) {
return {
z: tilePoint.z,
x: tilePoint.x,
y: Math.pow(2, tilePoint.z) - 1 - tilePoint.y
}
}
let options = {
subdomain: '012',
getUrlArgs: getUrlArgs
}
const txMap = new TXTileLayer(url, options)
txMap.addTo(this.map)
//marker
var myDivIcon = L.divIcon({ className: 'my-div-icon', html: '这是一个测试用的marker', iconSize: 150 });
var marker = L.marker([39.916527, 116.397128], { icon: myDivIcon }).addTo(this.map);
marker.on('click', function(e) {
console.log(e);
alert('我是Marker。');
})
//point
var myIcon = L.icon({
iconUrl: dianIcon, // ▶ 直接使用导入的图片
iconSize: [30, 30],
iconAnchor: [15, 15],
popupAnchor: [40, 0],
shadowUrl: markerShadow, // ▶ 使用导入的阴影
shadowSize: [30, 30],
shadowAnchor: [15, 15]
});
var point = L.marker([39.916527, 116.397128], { icon: myIcon, title: '这是一个测试用的point' }).addTo(this.map);
point.on('click', function(e) {
console.log(e);
alert('我是point。');
})
//polygon
var polygon = L.polygon([
[[40.0, 116.4],[40.1, 116.4],[40.1, 116.5],[40.0, 116.5]], // 外部
[[40.05, 116.45],[40.06, 116.45],[40.06, 116.46],[40.05, 116.46]] // 孔洞
], {
color: 'green',
fillColor: '#f03',
fillOpacity: 0.5
}).addTo(this.map);
polygon.bindTooltip('这是个多边形');
polygon.on('click', function(e) {
alert('我是polygon');
})
}
},
mounted() {
this.initLeaflet()
}
}
</script>
<style>
#map {
width: 100%;
height: 100%;
}
.my-div-icon {
font-size: 15px;
width: 5px;
color: red;
}
</style>
效果如图:

到这咱的简单的前端静态就差不多了
第三步: 前端接入后端接口
1. 修改端口
1. 后端
\src\main\resources\application.yml
修改port即可,把端口号由之前的8080,修改为了8081。(别一下子复制上去,到时候用户密码不一样啥的。)
server:
port: 8081
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:5432/postgres
username: postgres
password: "0164523"
druid:
initial-size: 8
min-idle: 5
max-active: 10
query-timeout: 6000
transaction-query-timeout: 6000
remove-abandoned-timeout: 1800
filters: stat,config
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.history.gismap.model
logging:
config: classpath:logback-boot.xml
2. 前端
vue-gismap/vite.config.js
Vite 项目没有 config/index.js 文件。端口号需要在项目根目录的 vite.config.js 中设置。 你需要打开 vite.config.js,在 server 选项里添加 port 字段。
修改port即可,把8080改成了8082。
export default defineConfig({
// ... 其他配置
server: {
port: 8082, // ▶ 修改前端端口为 8082
open: true, // 可选,自动打开浏览器
},
// ...
})
2. 解决跨域问题
因为前后端端口号不一样,所以会遇到跨域问题。
会提示:No 'Access-Control-Allow-Origin' header is present on the requested resource
就是因为跨域,前端无法请求后端数据。
解决跨域问题也很简单。后端加一个注释就可以,@CrossOrigin。
\src\main\java\com\history\gismap\controller\MapController.java3.
3. vue+axios网络请求
1.安装axios模块
npm install axios
2.axios get请求
一个非常简单的get请求,写在组件SpecialMap.vue里。
初始化加载getdata()函数,更新axiosdata数据,写在div中,vue用两个花括号{{}},在模板中引入数据。
<template>
<div>
{{axiosdata}}
专题地图
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
axiosdata: null
}
},
methods: {
getdata() {
axios.get('http://localhost:8081/history/geometry?category=prefpgn&start=-5000&end=-221').then((response) => {
this.axiosdata = response.data
console.log(response.data)
}).catch((response) => {
console.log(response)
})
}
},
mounted() {
this.getdata()
}
}
</script>
唯一需要注意:请求的 URL 是 http://localhost:8081,如果后端端口变了,记得同步修改。另外,由于跨域已经在后端通过 @CrossOrigin 解决,前端无需额外配置。
模板搭建好了,leaflets能加载底图和覆盖物了,axios能和后端交互了,接下来要做的,就是把要实现的功能都实现了。
OK,今天就到这了,主要就是搭建前端静态页面,和改一下接口,下篇开始实现前后端结合的数据交换,大家心急的可以先看大佬的教程,我们下篇见,拜拜!
该项目并非原创(侵权了私聊删QAQ),而是翻新一个知乎上的大佬的spring boot 2 + vue 2 的项目, 项目教程:知乎的才华横溢吴道简 大佬的教程
更多推荐
所有评论(0)