欢迎来到 WebGIS 入门系列的第十四篇文章!在本文中,我们将介绍如何利用 Node.js
来实现 WebGIS 系统开发时后台接口的开发知识,其中包括数据库初始化、数据表创建、后端接口程序初始化、Vue 项目中路由配置等。
调整 Vue 项目路由配置
调整 src
目录下 App.vue
组件中的代码,配置菜单跳转地址及 router-view
信息:
<template>
<header>
<h1>我的 WebGIS 应用</h1>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/map">地图</a></li>
<li><a href="/data">数据管理</a></li>
</ul>
</nav>
</header>
<main> <router-view></router-view> </main>
</template>
<script setup lang="ts"></script>
<style>
// ......
</style>
在 src/views
目录下分别新建 MapView.vue
和 DataManageView.vue
组件:
// MapView.vue
<template>
<main class="map-main"> <MapView /> </main>
</template>
<script setup lang="ts">
import MapView from "@/components/MapView.vue"
</script>
<style>
.map-main {
width: 100%;
height: 100%;
}
</style>
// DataManageView.vue
<template>
<main class="data-main"> <DataManage /> </main>
</template>
<script setup lang="ts">
import DataManage from "@/components/DataManage.vue"
</script>
<style>
.data-main {
width: 100%;
height: 100%;
}
</style>
在 src/components
目录下新建 DataManage.vue
组件:
<template>
<div class="data-manage">data manage</div>
</template>
<script setup lang="ts"></script>
<style scoped>
.data-manage {
width: 100%;
height: 100%;
}
</style>
优化调整 src/router
目录下的路由配置信息:
import { createRouter, createWebHistory } from "vue-router"
import HomeView from "../views/HomeView.vue"
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/map",
name: "map",
component: () => import("../views/MapView.vue"),
},
{
path: "/data",
name: "data",
component: () => import("../views/DataManageView.vue"),
},
],
})
export default router
界面预览如下所示:
优化 DataManage 组件
在 src/components
目录下的 DataManage.vue
组件中拷贝下面代码,完善数据管理界面:
<template>
<div class="data-manage">
<div class="data-manage-form">
<div class="data-manage-header">
<el-alert title="填入以下信息后进行数据入库" type="info" show-icon />
</div>
<el-form :model="form" label-width="auto" style="max-width: 600px">
<el-form-item label="名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="区域">
<el-select v-model="form.region" placeholder="请选择">
<el-option
v-for="item in regionData"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="日期">
<el-date-picker
v-model="form.date"
type="date"
placeholder="请选择"
style="width: 50%"
/>
</el-form-item>
<el-form-item label="已激活">
<el-switch v-model="form.delivery" />
</el-form-item>
<el-form-item label="激活类型">
<el-checkbox-group v-model="form.type">
<el-checkbox
v-for="item in typeData"
:key="item.value"
:label="item.value"
:name="item.value"
>{{ item.label }}</el-checkbox
>
</el-checkbox-group>
</el-form-item>
<el-form-item label="资源">
<el-radio-group v-model="form.resource">
<el-radio
v-for="item in resourceData"
:key="item.value"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.desc" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">提交</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</div>
<div class="data-manage-table">
<div class="data-manage-header">
<el-alert title="入库数据反显" type="info" show-icon />
</div>
<el-table
:data="tableData"
height="400"
style="width: 100%"
empty-text="暂无数据"
>
<el-table-column
v-for="item in tableColumn"
:key="item.prop"
:prop="item.prop"
:label="item.label"
/>
</el-table>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from "vue"
const regionData = ref([
{
value: "beijing",
label: "北京",
},
{
value: "shanghai",
label: "上海",
},
{
value: "chengdu",
label: "成都",
},
])
const typeData = ref([
{
value: "type-1",
label: "类型一",
},
{
value: "type-2",
label: "类型二",
},
{
value: "type-3",
label: "类型三",
},
{
value: "type-4",
label: "类型四",
},
])
const resourceData = ref([
{
value: "resource-1",
label: "资源一",
},
{
value: "resource-2",
label: "资源二",
},
])
const tableColumn = ref([
{
prop: "name",
label: "名称",
},
{
prop: "region",
label: "区域",
},
{
prop: "date",
label: "日期",
},
{
prop: "delivery",
label: "已激活",
},
{
prop: "type",
label: "激活类型",
},
{
prop: "resource",
label: "资源",
},
{
prop: "desc",
label: "备注",
},
])
const tableData = ref([])
const form = reactive({
name: "",
region: "",
date: "",
delivery: false,
type: [],
resource: "",
desc: "",
})
const onSubmit = () => {
console.log("submit!", JSON.parse(JSON.stringify(form)))
}
</script>
<style scoped>
.data-manage {
width: 100%;
height: 100%;
display: flex;
padding: 16px;
box-sizing: border-box;
}
.data-manage-header {
margin-bottom: 16px;
}
.data-manage-form {
width: 480px;
border: 1px solid #dcdfe6;
border-radius: 4px;
margin-right: 8px;
padding: 16px;
box-sizing: border-box;
}
.data-manage-table {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 16px;
box-sizing: border-box;
}
</style>
界面预览如下:
上述界面操作逻辑:左侧数据入库面板中依次填入相关信息后点击下方“提交”按钮,用户输入的信息数据会被提交到后台数据库中,此时右侧数据预览面板会更新,显示最新数据库中的数据。
数据库初始化
数据在前端界面被用户输入后,会经过后端接口程序,最终保存在数据库中,数据库可以安装部署在本地开发环境或远端服务器环境中。 此系列文章中为了简化操作,在腾讯云租了一个 PostgreSQL 云数据库,所以省去了安装部署流程,如果想安装部署在本机或服务器的话,建议大家通过搜索引擎去寻找一些相关教程,无脑安装即可。 数据库安装部署之后,接下来进行库表初始化。 首先在 PostgreSQL 数据库管理面板中选择新建数据库,名称为“webgis”:
然后选择新建的“webgis”数据库进入该数据库,在 public
模式下选择操作中的“新建表”,新建一份数据表,其结构如下:
至此,一个名为“webgis”的数据库创建成功,里面又新建了一份名为“webgis_project_bilibili”的数据表,我们后续入库的数据就存放在此。
后端接口程序初始化
为了降低后端接口开发门槛,避免短时间内又学习一门新的开发语言,此系列的后端接口开发我们使用 Node.js
,它是一个类似于浏览器的 JavaScript 运行环境,允许 JavaScript 语言编写的程序在浏览器环境之外可以运行,简单理解就是:Node.js
让 JavaScript 可以在浏览器之外运行成为了可能!
Node.js
在使用之前必须要在本机安装部署,不过不用担心,我们已经安装部署过了,详见《6、快速上手:使用 Vue 3 创建您的第一个 WebGIS 地图》。
在现有的项目根目录下新建 node
目录,然后通过 npm init
命令初始化一个后端接口程序目录,此时该目录下会出现一个 package.json
文件,里面包含当前后端程序的项目名称、版本号、作者信息等,以及后续我们安装的依赖包信息。
运行命令 npm install express
安装 express
框架。
新建 index.js
文件,将下述代码拷贝至此:
// eslint-disable-next-line no-undef
var express = require("express")
var app = express()
app.get("/", function (req, res) {
res.send("hello world")
})
app.listen(3001)
通过上述操作,我们已经初始化了一个基础的后端接口程序,其中已经新建了一个路径为“/”的 get 类型接口,通过 node index
命令将其启动后,在浏览器地址栏中输入“http://localhost:3001/
”,可以访问该接口:
GET 和 POST 接口开发
GET 和 POST 接口逻辑中都涉及到从 PostgreSQL 数据库中获取数据和插入数据,所以后端接口程序中需要安装 pg
模块,通过命令 npm install pg
安装。
在 node
目录下新建 routers
目录,然后该目录下新建 user.js
文件,在此文件中分别完成用户数据获取和插入接口:
// eslint-disable-next-line no-undef
var express = require("express")
var pg = require("pg")
var router = express.Router()
// postgres://[数据库用户名]:[数据库密码]@[数据库地址及端口]/[数据库名称]
var pgConfig = "postgres://postgres:webgis@localhost:5432/webgis"
// 获取全部用户数据
router.get("/all-data", function (req, res) {
var client = new pg.Client(pgConfig)
client.connect(function (isErr) {
if (isErr) {
console.log("connect error:" + isErr.message)
client.end()
return
}
client.query(
'SELECT * FROM "webgis_project_bilibili"',
function (isErr, rst) {
if (isErr) {
console.log("query error:" + isErr.message)
res.send({
status: "fail",
msg: "query error",
})
} else {
console.log("query success, data is: " + rst)
res.send({
status: "success",
data: rst.rows,
})
}
client.end()
}
)
})
})
// 插入数据
router.post("/insert-data", function (req, res) {
var id = Number(req.body.id)
var name = req.body.name
var region = req.body.region
var date = req.body.date
var delivery = Boolean(req.body.delivery)
var type = req.body.type
var resource = req.body.resource
var desc = req.body.desc
var client = new pg.Client(pgConfig)
client.connect(function (isErr) {
if (isErr) {
console.log("connect error:" + isErr.message)
client.end()
return
}
client.query(
'INSERT INTO "webgis_project_bilibili" ("id", "name", "region", "date", "delivery", "type", "resource", "desc") VALUES ($1, $2, $3, $4, $5, $6, $7, $8);',
[id, name, region, date, delivery, type, resource, desc],
function (isErr, rst) {
if (isErr) {
console.log("query error:" + isErr.message)
res.send({
status: "fail",
msg: "insert error",
})
} else {
console.log("insert success, data is: " + rst)
res.send({
status: "success",
data: [],
})
}
client.end()
}
)
})
})
// eslint-disable-next-line no-undef
module.exports = router
优化 node
目录下的 index.js
文件,在其中配置接口跨域、body 数据解析、接口地址等信息:
/* eslint-disable no-undef */
// eslint-disable-next-line no-undef
var express = require("express")
var app = express()
var bodyParser = require("body-parser")
var user = require("./routers/user")
//设置跨域访问
app.all("*", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*")
res.header(
"Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With"
)
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
res.header("X-Powered-By", " 3.2.1")
res.header("Content-Type", "application/json;charset=utf-8")
next()
})
app.use(
bodyParser.urlencoded({
extended: true,
})
)
app.use(bodyParser.json())
app.use("/user", user)
app.get("/", function (req, res) {
res.send("hello world")
})
app.listen(3001)
至此,接口开发工作已完成。
DataManage 中数据插入和获取
在 DataManage.vue
组件中,优化完善 onSubmit
方法:
const onSubmit = () => {
const formData = JSON.parse(JSON.stringify(form))
axios
.post(
"http://localhost:3001/user/insert-data",
qs.stringify({
...formData,
type: formData.type.join(","),
id: new Date().getTime(),
})
)
.then((res) => {
openMessage("数据入库成功", "success")
fetchData()
})
.catch((err) => {
console.error("err", err)
openMessage("数据入库失败", "error")
})
}
每次入库成功后,都需要重新获取一遍数据库中的最新数据,所以需要定义获取数据的方法 fetchData:
function fetchData() {
axios
.get("http://localhost:3001/user/all-data")
.then((res) => {
console.log("res", res)
tableData.value = lodash.map(res?.data?.data, (item) => {
return {
...item,
region: lodash.find(regionData.value, { value: item.region })?.label,
date: new Date(item.date).toLocaleDateString(),
delivery: item.delivery ? "是" : "否",
type: item.type.split(",").map((type) => {
return lodash.find(typeData.value, { value: type })?.label
}),
resource: lodash.find(resourceData.value, { value: item.resource })
?.label,
}
})
})
.catch((err) => {
console.error("err", err)
})
}
onMounted(() => {
fetchData()
})
界面预览如下:
小作业:数据管理支持检索数据
对于 WebGIS 系统中前端和后端开发流程全部介绍完毕,数据管理界面完成了数据插入和获取,但是在数据获取时并没有按条件去筛选,接下来继续优化 GET 接口,使其按接口传参来获取相应的数据,并在界面中增加条件筛选模块。
结语
通过本文的介绍,应该对如何使用 Node.js
开发 WebGIS 系统的后台接口有了基本的了解。从数据库的初始化到后端接口的创建,再到 Vue 项目中的路由配置,每一步都是构建一个高效、可靠的 WebGIS 系统不可或缺的部分。完成小作业可以帮助巩固所学知识,并将其应用到实际的项目开发中。