摘要
在上一篇中,我們已經(jīng)把和頁面相關(guān)的接口完成的差不多了。從創(chuàng)建頁面,更新頁面等等:
有了接口之后,我們就可以構(gòu)建前端頁面了。那這部分前端內(nèi)容我們應(yīng)該寫在哪里呢?
有兩種方式:
- 直接寫在我們的XinBuilder項(xiàng)目里面,然后通過前端路由拆分成兩個(gè)路由
- 在創(chuàng)建一個(gè)項(xiàng)目,然后打包到后端服務(wù)中,也就是通過后端路由去控制
因?yàn)槲也淮_定這個(gè)項(xiàng)目后面會(huì)有多少代碼,雖然我們目前只是想實(shí)現(xiàn)頁面的管理功能,但是后面我也不知道會(huì)增加到多少。
所以我準(zhǔn)備使用兩個(gè)React項(xiàng)目,和頁面相關(guān)的這些功能我都會(huì)寫在新的項(xiàng)目里,
1.創(chuàng)建項(xiàng)目
首先就是創(chuàng)建項(xiàng)目了,我們使用create-react-app創(chuàng)建一個(gè)項(xiàng)目:
> npx create-react-app app-builder --template typescript
然后再安裝antD
npm install antd --save
然后把項(xiàng)目里沒有用的文件刪一刪:
最后,因?yàn)槲覀円?qǐng)求我們寫好的接口,在安裝一下axios。
npm install axios --save
2.路由的配置
對(duì)于這個(gè)項(xiàng)目,我們現(xiàn)在只準(zhǔn)備完成和pageJson相關(guān)的。但是后面可能會(huì)有其他的頁面,所以我們是需要路由的。
我們就先安裝一下react-router-dom,然后使用路由來管理前端的頁面。
npm install react-router-dom --save
對(duì)于路由,我們?cè)趕rc下新建一個(gè)routes用來管理所有的路由頁面。
page文件夾就是代表和pageJson相關(guān)的路由。
現(xiàn)在我們回到index.tsx中,對(duì)page路由進(jìn)行引入。
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.服務(wù)端的CORS配置
這時(shí)候,如果我們?cè)陧?xiàng)目里調(diào)用服務(wù)端的接口,會(huì)有跨域的問題。所以在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: 第二節(jié):修改CORS配置解決跨域問題
4.構(gòu)建前端頁面
那我們需要的效果就是:
至于這部分比較簡單,我把代碼的注釋寫一下,讀者自己看就行。
不過編輯頁面和預(yù)覽頁面,一會(huì)再細(xì)說。
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: '獲取頁面列表失敗',
});
})
}
/**
* 更改搜索框的內(nèi)容
* @param value 搜索框的內(nèi)容
*/
const onSearch = (value: string) => {
setSearchValue(value)
}
/**
* 新建頁面的彈窗
*/
const addNewPage = () => {
setIsModalOpen(true);
setPageName('')
}
/**
* 搜索內(nèi)容的過濾
* @param list 頁面列表
* @returns 過濾后的頁面列表
*/
const getSearchList = (list: PageJson [] | undefined) => {
return (list || []).filter(item => {
return item.pageName.indexOf(searchValue) > -1
})
}
/**
* 根據(jù)頁面ID進(jìn)行刪除
* @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: '新建頁面失敗',
});
})
}
/**
* 新建頁面彈窗的取消回調(diào)
*/
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'>輕量級(jí)的低代碼平臺(tái)</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'>預(yù)覽頁面</Button>
</div>
</Card>
</Col>
})
}
</Row>
</div>
</div>
<Modal title="創(chuàng)建頁面" open={isModalOpen} onOk={handleOk} onCancel={handleCancel} okText='創(chuàng)建' cancelText='取消'>
<Input addonBefore="頁面名稱" value={pageName} onChange={changePageName} />
</Modal>
</div>
)
}
5.跳轉(zhuǎn)頁面詳情
當(dāng)我點(diǎn)擊編輯頁面的時(shí)候,應(yīng)該跳轉(zhuǎn)到對(duì)應(yīng)頁面的編輯狀態(tài)。也就是我們之前實(shí)現(xiàn)的項(xiàng)目。
那我在我們的設(shè)計(jì)器項(xiàng)目怎么知道當(dāng)前的頁面ID呢?
所以我們需要再跳轉(zhuǎn)的時(shí)候,將pageId帶過去,怎么帶呢,只能通過URL上面的參數(shù)實(shí)現(xiàn),所以我們現(xiàn)在可以實(shí)現(xiàn)一下toBuilderPage方法。
/**
* 根據(jù)頁面ID跳轉(zhuǎn)到詳情頁
* @param pageId 頁面ID
* @returns
*/
const toBuilderPage = (pageId: string) => {
return () => {
window.open(`http://localhost:3000?pageId=${pageId}`)
}
}
6.修改XinBuilder項(xiàng)目
OK,現(xiàn)在我們現(xiàn)在回到我們的低代碼項(xiàng)目里,在builder目錄下的index.tsx中,我們要根據(jù)URL上的pageId,調(diào)取接口來獲取到頁面詳情
獲取到之后,我們?cè)偻ㄟ^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,現(xiàn)在我們還需要就是給設(shè)計(jì)器增加保存的功能,我們來到designTop中,給它添加一個(gè)保存的按鈕。
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>
)
}
到此為止,在上一篇中實(shí)現(xiàn)的所有接口,我們就實(shí)現(xiàn)完對(duì)它的調(diào)用了。
和XinBuilder相關(guān)的代碼提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第十七節(jié):實(shí)現(xiàn)頁面的保存以及加載
博主補(bǔ)充
本篇相關(guān)的代碼提交在github上:
https://github.com/TeacherXin/AppBuilder
commit: 第一節(jié):初始化項(xiàng)目,實(shí)現(xiàn)頁面的創(chuàng)建等操作
目前我們已經(jīng)有三個(gè)項(xiàng)目了:文章來源:http://www.zghlxwxcb.cn/news/detail-815978.html
- AppBuilder 最外層的殼子,提供創(chuàng)建頁面等操作
- XinBuilder 設(shè)計(jì)器項(xiàng)目,負(fù)責(zé)對(duì)頁面進(jìn)行配置
- XinBuilderServer 后端服務(wù),負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)
后面還會(huì)有一個(gè)運(yùn)行時(shí)的項(xiàng)目。。。。。文章來源地址http://www.zghlxwxcb.cn/news/detail-815978.html
到了這里,關(guān)于從零實(shí)現(xiàn)一套低代碼(保姆級(jí)教程)【后端服務(wù)】 --- 【18】實(shí)現(xiàn)頁面接口對(duì)應(yīng)的前端的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!