从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端

这篇具有很好参考价值的文章主要介绍了从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

在上一篇中,我们已经把和页面相关的接口完成的差不多了。从创建页面,更新页面等等:

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,低代码,前端
有了接口之后,我们就可以构建前端页面了。那这部分前端内容我们应该写在哪里呢?

有两种方式:

  1. 直接写在我们的XinBuilder项目里面,然后通过前端路由拆分成两个路由
  2. 在创建一个项目,然后打包到后端服务中,也就是通过后端路由去控制

因为我不确定这个项目后面会有多少代码,虽然我们目前只是想实现页面的管理功能,但是后面我也不知道会增加到多少。

所以我准备使用两个React项目,和页面相关的这些功能我都会写在新的项目里,

1.创建项目

首先就是创建项目了,我们使用create-react-app创建一个项目:

>  npx create-react-app app-builder --template typescript

然后再安装antD

 npm install antd --save

然后把项目里没有用的文件删一删:

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,低代码,前端

最后,因为我们要请求我们写好的接口,在安装一下axios。

npm install axios --save

2.路由的配置

对于这个项目,我们现在只准备完成和pageJson相关的。但是后面可能会有其他的页面,所以我们是需要路由的。

我们就先安装一下react-router-dom,然后使用路由来管理前端的页面。

 npm install react-router-dom --save

对于路由,我们在src下新建一个routes用来管理所有的路由页面。

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,低代码,前端

page文件夹就是代表和pageJson相关的路由。

现在我们回到index.tsx中,对page路由进行引入。

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import Page from './routes/page';
import { HashRouter as Router, Routes , Route} from "react-router-dom";


const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <Router>
    <Suspense>
    <Routes>
      <Route path={'/'} element={<Page />}></Route>
    </Routes>
    </Suspense>
  </Router>
);

3.服务端的CORS配置

这时候,如果我们在项目里调用服务端的接口,会有跨域的问题。所以在XinBuilderServer中,我们修改一下main.ts文件:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'
import { NestExpressApplication } from '@nestjs/platform-express';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule,{ cors: true });
  const options = new DocumentBuilder()
  .setTitle('API example')
  .addBearerAuth()
  .setVersion('1.0')
  .build()
  const document = SwaggerModule.createDocument(app, options)
  SwaggerModule.setup('api-docs', app, document)
  await app.listen(4000);
}
bootstrap();

通过修改CORS配置,来解决跨域的问题

的代码提交在github上:
https://github.com/TeacherXin/XinBuilderServer2
commit: 第二节:修改CORS配置解决跨域问题

4.构建前端页面

那我们需要的效果就是:

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,低代码,前端
至于这部分比较简单,我把代码的注释写一下,读者自己看就行。

不过编辑页面和预览页面,一会再细说。

import React, { useEffect, useState } from 'react'
import { Card, Col, Row, Button,Input, message, Modal,Divider, Select  } from 'antd';
import {DeleteOutlined,DatabaseOutlined,FormOutlined,InsertRowBelowOutlined,UsergroupDeleteOutlined} from '@ant-design/icons';
import axios from 'axios'
import './index.css'
const { Search } = Input

interface PageJson {
  pageName: string,
  pageId: string,
  pageJson: {
    [key: string]: any
  },
  _id: string
}

export default function Page() {
  const [messageApi, contextHolder] = message.useMessage();
  const [pageList, setPageList] = useState<PageJson []>()
  const [isModalOpen,setIsModalOpen] = useState<boolean>(false)
  const [pageName,setPageName] = useState<string>('')
  const [searchValue,setSearchValue] = useState<string>('')

  useEffect(() => {
    getPageList()
  }, [])

  /**
   * 获取全部List的接口
   */
  const getPageList = () => {
    axios.post(`http://localhost:4000/page-json/findAllPage`)
    .then(res => {
      setPageList(res.data.data)
    })
    .catch(err => {
      messageApi.open({
        type: 'error',
        content: '获取页面列表失败',
      });
    })
  }

  /**
   * 更改搜索框的内容
   * @param value 搜索框的内容
   */
  const onSearch = (value: string) => {
    setSearchValue(value)
  }

  /**
   * 新建页面的弹窗
   */
  const addNewPage = () => {
    setIsModalOpen(true);
    setPageName('')
  }

  /**
   * 搜索内容的过滤
   * @param list 页面列表
   * @returns 过滤后的页面列表
   */
  const getSearchList = (list: PageJson [] | undefined) => {
    return (list || []).filter(item => {
      return item.pageName.indexOf(searchValue) > -1
    })
  }

  /**
   * 根据页面ID进行删除
   * @param pageId 页面的ID
   * @returns 
   */
  const deletePage = (pageId: string) => {
    return () => {
      axios.post(`http://localhost:4000/page-json/deletePage`,{
        pageId
      })
      .then(res => {
        messageApi.open({
          type: 'success',
          content: '删除成功',
        });
        getPageList()
      })
      .catch(err => {
        messageApi.open({
          type: 'error',
          content: '删除失败',
        });
      })
    }
  }

  /**
   * 新增页面掉的接口
   */
  const handleOk = () => {
    const user = JSON.parse(localStorage.getItem('user') || '{}');
    axios.post(`http://localhost:4000/page-json/addPage`,{
      pageName: pageName,
      pageId:'pageInfo_' + new Date().getTime(),
      pageJson: {},
    })
    .then(res => {
      messageApi.open({
        type: 'success',
        content: '新建页面成功',
      });
      getPageList()
      setIsModalOpen(false)
    })
    .catch(err => {
      messageApi.open({
        type: 'error',
        content: '新建页面失败',
      });
    })
  }

  /**
   * 新建页面弹窗的取消回调
   */
  const handleCancel = () => {
    setIsModalOpen(false)
  }

  /**
   * 更改输入的页面名称
   * @param e 页面名称
   */
  const changePageName = (e: any) => {
    setPageName(e.target.value)
  }

  const toBuilderPage = (pageId: string) => {
    return () => {

    }
  }

  

  return (
    <div className='PageList'>
      {contextHolder}
      <div className='pageLeft'>
        <div className='leftHeader'>XinBuilder</div>
        <div className='leftDiscribe'>轻量级的低代码平台</div>
        <Divider />
      </div>
      <div className='pageRight'>
        <div className='PageHeader'>
          <Search
            style={{ width: 304 }}
            onSearch={onSearch}
          />
          <Button className='pageButton' onClick={addNewPage}>新建页面</Button>
        </div>
        <Divider />
        <div className='PageBody'>
          <Row style={{width:'100%'}} gutter={16}>
            {
              (getSearchList(pageList) || []).map(item => {
                return <Col style={{marginTop:'10px'}} key={item._id} span={6}>
                  <Card
                    title={<div><span>{item.pageName || '匿名'}</span><DeleteOutlined onClick={deletePage(item.pageId)}style={{float:'right',cursor:'pointer'}} /></div>}
                    bordered={false}
                    headStyle={{fontSize:'14px'}}
                  >
                    <div style={{height:'50px'}}>
                      <Button type='text' onClick={toBuilderPage(item.pageId)}>编辑页面</Button>
                      <Button type='text'>预览页面</Button>
                    </div>
                  </Card>
                </Col>
              })
            }
          </Row>
        </div>
      </div>
      <Modal title="创建页面" open={isModalOpen} onOk={handleOk} onCancel={handleCancel} okText='创建' cancelText='取消'>
          <Input addonBefore="页面名称" value={pageName} onChange={changePageName} />
      </Modal>
    </div>
  )
}

5.跳转页面详情

当我点击编辑页面的时候,应该跳转到对应页面的编辑状态。也就是我们之前实现的项目。
那我在我们的设计器项目怎么知道当前的页面ID呢?

所以我们需要再跳转的时候,将pageId带过去,怎么带呢,只能通过URL上面的参数实现,所以我们现在可以实现一下toBuilderPage方法。

  /**
   * 根据页面ID跳转到详情页
   * @param pageId 页面ID
   * @returns 
   */
  const toBuilderPage = (pageId: string) => {
    return () => {
      window.open(`http://localhost:3000?pageId=${pageId}`)
    }
  }

6.修改XinBuilder项目

OK,现在我们现在回到我们的低代码项目里,在builder目录下的index.tsx中,我们要根据URL上的pageId,调取接口来获取到页面详情

获取到之后,我们再通过Store去更新redux。

import { useEffect } from 'react'
import DesignTop from './designTop'
import LeftCom from './leftPart'
import MainCom from './mainPart'
import RightCom from './rightPart'
import axios from 'axios'
import Store from '../../store'
import { message } from 'antd'

export default function Builder() {

  useEffect(() => {
    const search = window.location.search || '';
    const pageId = search.replace('?pageId=', '');
    axios.post('http://localhost:4000/page-json/findPageByID', {
      pageId
    })
    .then(res => {
      if(res.data.data) {
        Store.dispatch({type: 'changeComList', value: res.data.data.pageJson || []})
      }else{
        message.error('获取页面详情失败')
      }
    })
  }, [])

  return (
    <div>
      <DesignTop />
      <LeftCom />
      <MainCom />
      <RightCom />
    </div>
  )
}

OK,现在我们还需要就是给设计器增加保存的功能,我们来到designTop中,给它添加一个保存的按钮。

import { Button, message } from 'antd'
import './index.css'
import Store from '../../../store'
import axios from 'axios'

export default function DesignTop() {

  const savePage = () => {
    const search = window.location.search || '';
    const pageId = search.replace('?pageId=', '');
    const comList = Store.getState().comList;
    axios.post('http://localhost:4000/page-json/updatePage', {
      pageId,
      pageJson: comList
    })
    .then(res => {
      if(res.data.code == 200) {
        message.success('保存成功')
      }
    })
  }

  return (
    <div className='designTop'>
      <span className='title'>XinBuilder</span>
      <Button onClick={savePage} type='primary' ghost>保存</Button>
    </div>
  )
}

到此为止,在上一篇中实现的所有接口,我们就实现完对它的调用了。

和XinBuilder相关的代码提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第十七节:实现页面的保存以及加载

博主补充

本篇相关的代码提交在github上:
https://github.com/TeacherXin/AppBuilder
commit: 第一节:初始化项目,实现页面的创建等操作

目前我们已经有三个项目了:

  1. AppBuilder 最外层的壳子,提供创建页面等操作
  2. XinBuilder 设计器项目,负责对页面进行配置
  3. XinBuilderServer 后端服务,负责数据的存储

后面还会有一个运行时的项目。。。。。文章来源地址https://www.toymoban.com/news/detail-815978.html

到了这里,关于从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 保姆级教程从零搭建云服务器(小彩蛋,请大家看烟花秀)

    笔者已从零搭建好云服务器,在文章开始笔者请大家看一场简单的烟花秀,该篇博文是写给小白的保姆级教程,不论是有基础还是没基础的,都可以根据本篇博文,轻松搭建个人云服务器。 烟花秀:浏览器直接输入ysw.world:666回车即可,由于个人域名没有备案,所以目前只能

    2024年02月03日
    浏览(44)
  • 保姆级教程!!教你通过【Pycharm远程】连接服务器运行项目代码

    这篇文章主要解决一个问题—— 我有服务器,但是不知道怎么拿来写代码,跑深度学习项目 。确实,玩深度学习的成本比较高,无论是前期的学习成本,还是你需要具备的硬件成本,都是拦路虎。小罗没有办法帮你解决硬件上的问题,所以只能帮你们理清一下有了服务器以

    2024年03月27日
    浏览(51)
  • 【模型+代码/保姆级教程】使用Pytorch实现手写汉字识别

    参考文章: 最初参考的两篇: 【Pytorch】基于CNN手写汉字的识别 「Pytorch」CNN实现手写汉字识别(数据集制作,网络搭建,训练验证测试全部代码) 模型: EfficientNetV2网络详解 数据集(不必从这里下载,可以看一下它的介绍): CASIA Online and Offline Chinese Handwriting Databases 鉴于

    2024年02月07日
    浏览(42)
  • Unity教程2:保姆级教程.几行代码实现输入控制2D人物的移动

    目录 人物的创建以及刚体的设置 图层渲染层级设置 角色碰撞箱设置 使用代码控制人物移动 创建脚本文件  初始函数解释 控制移动代码 初始化变量  获得键盘输入  调用函数 手册链接在这:Unity User Manual (2019.3) - Unity 手册 没有控制人物移动的2D游戏就太说不过去了!那么接

    2024年02月06日
    浏览(41)
  • docker从零部署jenkins保姆级教程

    jenkins,基本是最常用的持续集成工具。在实际的工作中,后端研发一般没有jenkins的操作权限,只有一些查看权限,但是我们的代码是经过这个工具构建出来部署到服务器的,所以我觉着有必要了解一下这个工具的搭建过程以及简单的一些使用。尽可能多的了解公司中和你开

    2024年02月10日
    浏览(51)
  • 高级圣诞树代码实现合集-保姆级教程【前端三件套实现—0基础直接运行】

    0基础直接运行教程: 1.新建txt文本: 2.将代码粘贴到txt文本里: 3.将后缀改为html 4.双击打开html文件,观察效果~ 这段代码是一个用HTML和JavaScript实现的圣诞树动画效果。我将代码分成几个部分进行讲解。 HTML结构: 在 head 标签中定义了页面的标题、字符集和样式。 样式部分

    2024年02月04日
    浏览(64)
  • docker从零部署jenkins保姆级教程(下)

    上一篇文章,我们完成了以下工作。 1)、docker部署jenkins 2)、建立第一个jenkins job 3)、通过jenkins job自动编译构建我们的github项目 上面所做的3个工作,其实都是为了这一篇文章打基础,不管是部署docker还是部署jenkins,我们最终的目的还是部署我们的项目,让项目跑起来,让流

    2024年02月09日
    浏览(86)
  • docker从零部署jenkins保姆级教程(上)

    jenkins,基本是最常用的持续集成工具。在实际的工作中,后端研发一般没有jenkins的操作权限,只有一些查看权限,但是我们的代码是经过这个工具构建出来部署到服务器的,所以我觉着有必要了解一下这个工具的搭建过程以及简单的一些使用。尽可能多的了解公司中和你开

    2024年02月09日
    浏览(45)
  • 保姆级教程:从零构建GitHub Pages静态网站

    Github Pages官网:https://pages.github.com/ GitHub Pages 是 GitHub 提供的一个免费的静态网站托管服务,它允许 GitHub 用户创建和托管自己的静态网站,这些网站可以通过特定的 GitHub 仓库进行管理和托管。 GitHub Pages 的主要特点包括 : 免费托管 : GitHub Pages 提供免费的静态网站托管服务

    2024年02月04日
    浏览(52)
  • 「教程」如何使用一套代码在多种程序中接入天气预警API

    天气预警的重要性不言而喻,在遭受自然灾害和极端天气时,及时获得预警信息可以拯救生命和减少财产损失。如今,随着科技的进步,开发者和企业可以借助天气预警 API 这款强大的服务,将实时预警信息集成到自己的应用中,为用户提供准确的预警通知。 本文将重点探讨

    2024年02月14日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包