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

02-27 1089阅读 0评论

摘要

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

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,安装,第1张
(图片来源网络,侵删)

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

有了接口之后,我们就可以构建前端页面了。那这部分前端内容我们应该写在哪里呢?

有两种方式:

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

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

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

1.创建项目

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

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,安装,第3张
(图片来源网络,侵删)
>  npx create-react-app app-builder --template typescript

然后再安装antD

 npm install antd --save

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

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

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

npm install axios --save

2.路由的配置

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

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

从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,从零实现一套低代码(保姆级教程)【后端服务】 --- 【18】实现页面接口对应的前端,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,安装,第5张
(图片来源网络,侵删)
 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(
  
    
    
      '/'} element={
    
    
  
);

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(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()
  const [isModalOpen,setIsModalOpen] = useState(false)
  const [pageName,setPageName] = useState('')
  const [searchValue,setSearchValue] = useState('')
  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 (
    
{contextHolder}
XinBuilder
轻量级的低代码平台
{ width: 304 }} onSearch={onSearch} / addNewPage}新建页面
{width:'100%'}} gutter={16} { (getSearchList(pageList) || []).map(item => { return {marginTop:'10px'}} key={item._id} span={6} item.pageName || '匿名'}deletePage(item.pageId)}style={{float:'right',cursor:'pointer'}} /false} headStyle={{fontSize:'14px'}} {height:'50px'}} toBuilderPage(item.pageId)}编辑页面 预览页面
}) }
isModalOpen} onOk={handleOk} onCancel={handleCancel} okText='创建' cancelText='取消' pageName} onChange={changePageName} / ) }

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 (
    
) }

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 (
    
XinBuilder savePage} type='primary' ghost保存
) }

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

和XinBuilder相关的代码提交在github上:

https://github.com/TeacherXin/XinBuilder2

commit: 第十七节:实现页面的保存以及加载

博主补充

本篇相关的代码提交在github上:

https://github.com/TeacherXin/AppBuilder

commit: 第一节:初始化项目,实现页面的创建等操作

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

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

后面还会有一个运行时的项目。。。。。


免责声明
本网站所收集的部分公开资料来源于AI生成和互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
评论列表 (暂无评论,1089人围观)

还没有评论,来说两句吧...

目录[+]