Featured image of post WebGIS 从前端到后端:后台接口开发

WebGIS 从前端到后端:后台接口开发

欢迎来到 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.vueDataManageView.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

界面预览如下所示:

WebGIS 从前端到后端:后台接口开发

优化 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>

界面预览如下:

WebGIS 从前端到后端:后台接口开发

上述界面操作逻辑:左侧数据入库面板中依次填入相关信息后点击下方“提交”按钮,用户输入的信息数据会被提交到后台数据库中,此时右侧数据预览面板会更新,显示最新数据库中的数据。

数据库初始化

数据在前端界面被用户输入后,会经过后端接口程序,最终保存在数据库中,数据库可以安装部署在本地开发环境或远端服务器环境中。 此系列文章中为了简化操作,在腾讯云租了一个 PostgreSQL 云数据库,所以省去了安装部署流程,如果想安装部署在本机或服务器的话,建议大家通过搜索引擎去寻找一些相关教程,无脑安装即可。 数据库安装部署之后,接下来进行库表初始化。 首先在 PostgreSQL 数据库管理面板中选择新建数据库,名称为“webgis”:

WebGIS 从前端到后端:后台接口开发

然后选择新建的“webgis”数据库进入该数据库,在 public 模式下选择操作中的“新建表”,新建一份数据表,其结构如下:

WebGIS 从前端到后端:后台接口开发 WebGIS 从前端到后端:后台接口开发 WebGIS 从前端到后端:后台接口开发

至此,一个名为“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/”,可以访问该接口:

WebGIS 从前端到后端:后台接口开发

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 从前端到后端:后台接口开发

小作业:数据管理支持检索数据

对于 WebGIS 系统中前端和后端开发流程全部介绍完毕,数据管理界面完成了数据插入和获取,但是在数据获取时并没有按条件去筛选,接下来继续优化 GET 接口,使其按接口传参来获取相应的数据,并在界面中增加条件筛选模块。

结语

通过本文的介绍,应该对如何使用 Node.js 开发 WebGIS 系统的后台接口有了基本的了解。从数据库的初始化到后端接口的创建,再到 Vue 项目中的路由配置,每一步都是构建一个高效、可靠的 WebGIS 系统不可或缺的部分。完成小作业可以帮助巩固所学知识,并将其应用到实际的项目开发中。