网站建设优化服务公司,wordpress 双陈,网站说明书的详细说明,WordPress建站主机推荐reactviteantDreduceecharts项目完整记录
之前写前端项目#xff0c;都是用的vue#xff0c;从最开始的vue2到后来的vue3#xff0c;断断续续写了3年#xff0c;打包工具也从webpack转到了vite#xff0c;全局数据管理工具从vuex转到了pinia。总体而言#xff0c;vue3对…reactviteantDreduceecharts项目完整记录
之前写前端项目都是用的vue从最开始的vue2到后来的vue3断断续续写了3年打包工具也从webpack转到了vite全局数据管理工具从vuex转到了pinia。总体而言vue3对比vue2有非常明显的提升vite比webpack打包的速度更是快了无数倍至于pinia和vuex因人而异我更喜欢pinia组合式api的写法深得我心。总而言之一句话我是全方面拥抱了vue3的新技术栈当然除了TSTS对后端比较友好我只能算半个后端用不用无所谓。时代在前进技术在发展如果永远守着一套陈旧的技术找各种理由为自己辩解实在是不明智的选择。
一直想学一下react中途学过几次因为平时工作事情太多不得不停下来。刚开始接触jsx我是抵制的vue把html、js和css进行了严格的区分并摆脱了原生的dom操作jsx却又把这些混在一起写代码的时候让我感觉像吃了si一样难受。学完之后依然觉得很难受但为啥我还是坚持要学react呢这么几个原因
从全球来看react是最火的前端框架vue只在国内火我在看国外的一些项目的源码时发现自己完全看不懂在写什么甚至国内有些开源项目也只出了react的包比如mapv换一种框架扩展一下自己的技能树熟悉原生js
花了3天速刷了一遍B站黑马前端讲师的课并跟着完整写了一个非常简单的项目后端接口也是用的黑马的感谢黑马记录一下完整的过程为自己后面写项目提供参考也为后来人提供参考
项目最终界面
登录界面 首页 文章管理 创建文章 目前就这些了以下进入正题
〇、代码仓库地址
https://gitee.com/hgandzl/react-vite一、创建项目并配置基础环境
1. vite创建项目
黑马老师是基于CRA创建项目应该是和webpack相关的技术没深入了解我是用的vite
vite创建前端项目的指令
npm create vitelatest创建过程如下 vscode打开创建的项目执行npm i后执行npm run dev即可打开默认的vitereact项目
2. 整理项目目录
项目src文件夹下依次创建如下文件夹
-src-apis 项目接口函数-assets 项目资源文件比如图片等-components 通用组件-pages 页面组件-router 路由-store 集中状态管理-utils 工具比如token、axios 的封装等-App.jsx 根组件-index.scss 全局样式-main.jsx 项目入口删除无关的文件只保留App.jsx和main.jsx并删除相关引入
删除main.jsx中的严格节点模式
import React from react;
import ReactDOM from react-dom/client;
import App from ./App.jsx;ReactDOM.createRoot(document.getElementById(root)).render(App /);删除App.jsx中无关的代码保留基础组件
function App() {return app/;
}export default App;3. 使用scss预处理器
实现步骤
安装解析 sass 的包npm i sass -D创建全局样式文件index.scss
index.scss
* {margin: 0;padding: 0;
}项目入口文件引入index.scss
4. 使用Ant Design作为UI框架
实现步骤
安装 antd 组件库npm i antd页面上导入并使用
5. 配置基础路由
实现步骤 安装路由包 npm i react-router-dom 准备 Layout和 Login俩个基础组件 pages目录下新建两个组件分别是pages/Layout/index.jsx和pages/Login/index.jsx并同步新建样式文件 pages/Layout/index.jsx const Layout () {return divthis is layout/div
}
export default Layoutpages/Login/index.jsx const Login () {return divthis is login/div
}
export default Login配置路由 router文件夹下新建index.jsx文件并配置如下基础路由 import { createBrowserRouter } from react-router-domimport Login from ../pages/Login
import Layout from ../pages/Layoutconst router createBrowserRouter([{path: /,element: Layout /,},{path: /login,element: Login /,},
])export default router全局挂载路由 和vue项目类似路由要全局挂载 main.jsx import React from react;
import ReactDOM from react-dom/client;
import ./index.scss;
import router from ./router;
import { RouterProvider } from react-router-dom;ReactDOM.createRoot(document.getElementById(root)).render(RouterProvider router{router} /
);二、编写登录页面
1. 使用antd搭建基本结构
实现步骤 在 Login/index.js 中创建登录页面基本结构 import ./index.scss;
import { Card, Form, Input, Button } from antd;
import logo from ../../assets/global.png;const Login () {return (div classNameloginCard classNamelogin-containerimg classNamelogin-logo src{logo} alt /{/* 登录表单 */}FormForm.ItemInput sizelarge placeholder请输入手机号 //Form.ItemForm.ItemInput sizelarge placeholder请输入验证码 //Form.ItemForm.ItemButton typeprimary htmlTypesubmit sizelarge block登录/Button/Form.Item/Form/Card/div);
};export default Login; 在 Login 目录中创建 index.scss 文件指定组件样式 .login {width: 100%;height: 100%;position: absolute;left: 0;top: 0;// background: center/cover url(~/assets/login.png);.login-logo {// width: 200px;// height: 60px;display: block;margin: 0 auto 20px;}.login-container {width: 440px;height: 400px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);box-shadow: 0 0 50px rgb(0 0 0 / 10%);}.login-checkbox-label {color: #1890ff;}
}启动项目地址输入登录页面路由显示如下 2. 实现表单校验功能
实现步骤
为 Form 组件添加 validateTrigger 属性指定校验触发时机的集合为 Form.Item 组件添加 name 属性这是为了能取到表单项里面的值为 Form.Item 组件添加 rules 属性用来添加表单校验规则对象这与elementplus的验证机制高度相似
整体实现代码
const Login () {return (Form validateTrigger{[onBlur]}Form.Itemnamemobilerules{[{ required: true, message: 请输入手机号 },{pattern: /^1[3-9]\d{9}$/,message: 手机号码格式不对}]}Input sizelarge placeholder请输入手机号 //Form.ItemForm.Itemnamecoderules{[{ required: true, message: 请输入验证码 },]}Input sizelarge placeholder请输入验证码 maxLength{6} //Form.ItemForm.ItemButton typeprimary htmlTypesubmit sizelarge block登录/Button/Form.Item/Form)
}3. 获取登录form的表单数据
实现步骤
为 Form 组件添加 onFinish 属性该事件会在点击登录按钮时触发。其实这个onFinish也是button中的submit绑定的也就是说点击submit按钮时就会触发onFinish方法创建 onFinish 函数通过函数参数 values 拿到表单值onFinish函数传递默认参数参数就是表单内的每一项数据
const onFinish (formData) {console.log(formData);};
....Form validateTrigger{[onBlur]} onFinish{onFinish}....Form.ItemButton typeprimary htmlTypesubmit sizelarge block登录/Button/Form.Item/Form....export default Login;
4. aixos二次封装
因为需要向后端发起请求涉及token认证的地方需要设置请求拦截器也可能需要设置响应拦截器所以需要对axios二次封装
在此之前我曾详细记录过如何使用reactredux完成登录页面及token的存取和登录保持因此整个登录不再赘述只上关键过程和重要代码
实现步骤
安装 axios 到项目创建 utils/http.jsx 文件创建 axios 实例配置 baseURL请求拦截器响应拦截器
https.jsx
import axios from axios;const http axios.create({baseURL: http://geek.itheima.net/v1_0,timeout: 5000,
});// axios请求拦截器
http.interceptors.request.use((config) {return config;},(e) Promise.reject(e)
);// axios响应式拦截器
http.interceptors.response.use((res) res.data,(e) {console.log(e);return Promise.reject(e);}
);export default http;
5. 引入redux管理全局数据
react中的redux就相当于vue中的vuex都用于管理全局数据登录时后端返回的token数据就是全局需要的数据
实现步骤 安装redux相关包 npm i react-redux reduxjs/toolkit配置redux配置redux在另一篇博客中有详细记录不再具体说明 新建user模块store/moduls/user.jsx填入以下代码 import { createSlice } from reduxjs/toolkit;
import http from ../../utils/http;
const userStore createSlice({name: user,// 数据状态initialState: {token: ,},// 同步修改方法reducers: {setToken(state, action) {state.userInfo action.payload;},},
});// 解构出actionCreater
const { setToken } userStore.actions;// 获取reducer函数
const userReducer userStore.reducer;// 异步方法封装
const fetchLogin (loginForm) {return async (dispatch) {const res await http.post(/authorizations, loginForm);dispatch(setToken(res.data.token));};
};export { fetchLogin };export default userReducer; 在index.jsx中注册子模块store/index.jsx import { configureStore } from reduxjs/toolkit;
import userReducer from ./modules/user;export default configureStore({reducer: {user: userReducer}
})入口文件中全局注册storemain.jsx import ReactDOM from react-dom/client;
import App from ./App.jsx;
import ./index.scss;
import { RouterProvider } from react-router-dom;
import router from ./router/index.jsx;
import { Provider } from react-redux;
import store from ./store/index.jsx;ReactDOM.createRoot(document.getElementById(root)).render(Provider store{store}RouterProvider router{router} //Provider
);
6. 实现登录逻辑
实现步骤
收集表单信息向后端发送登录请求登录成功后跳转到首页提示用户登录成功
主要是修改上面的Login/index.jsx中的onFinish方法
如下
// 省略其他代码
// .......
import { useDispatch } from react-redux;
import { fetchLogin } from ../../store/modules/user;
import { useNavigate } from react-router-dom;// 省略其他代码
// .......const onFinish async (formData) {console.log(formData);await dispatch(fetchLogin(formData))navigate(/)message.success(登录成功)};
// 省略其他代码
// .......7. 实现token持久化存储
其实就是登录时把token存到localstorage中去reactredux完成登录页面及token的存取和登录保持–这篇博客中详细记录了这里只上关键代码
首先封装token的存、取、删方法utils/token.jsx
const TOKENKEY token_key;function setToken(token) {return localStorage.setItem(TOKENKEY, token);
}function getToken() {return localStorage.getItem(TOKENKEY);
}function clearToken() {return localStorage.removeItem(TOKENKEY);
}export { setToken, getToken, clearToken }; localstorage中持久化存储token逻辑就是在redux的同步方法中存储token同时token的初始化不再是空值当localstorage中有token时就取出来没有就是空值 store/moduls/user.jsx
import { createSlice } from reduxjs/toolkit;
import http from ../../utils/http;
import { setToken as _setToken, getToken } from ../../utils/token;
const userStore createSlice({name: user,// 数据状态initialState: {// 差异1token: getToken() || ,},// 同步修改方法reducers: {setToken(state, action) {state.token action.payload;// 存入本地_setToken(state.token);},},
});
8. 请求拦截器中携带token
常规操作在axios二次封装的http.jsx文件中添加以下代码
// axios请求拦截器
http.interceptors.request.use((config) {// 导入getToken方法const token getToken()if (token) {// 请求头携带tokenconfig.headers.Authorization Bearer token;}return config;},(e) Promise.reject(e)
);9. 路由守卫
vue中的路由守卫是在router中实现的react的做法是封装 AuthRoute 路由鉴权高阶组件然后将需要鉴权的页面路由配置替换为 AuthRoute 组件渲染
实现步骤
在 components 目录中创建 AuthRoute/index.jsx 文件登录时直接渲染相应页面组件未登录时重定向到登录页面将需要鉴权的页面路由配置替换为 AuthRoute 组件渲染
AuthRoute/index.jsx中的代码
import { getToken } from ../../utils/token
import { Navigate } from react-router-domconst AuthRoute ({ children }) {const isToken getToken()if (isToken) {return {children}/} else {return Navigate to/login replace /}
}export default AuthRouteLayout页面需要鉴权所以在路由中修改页面的渲染配置router/index.jsx
import { createBrowserRouter } from react-router-dom;import Login from ../pages/Login;
import Layout from ../pages/Layout;
import AuthRoute from ../components/AuthRoute;const router createBrowserRouter([{path: /,element: AuthRouteLayout //AuthRoute,},{path: /login,element: Login /,},
]);export default router;
10. 封装接口调用的api
因为后面涉及多个后端接口调用所以好的做法是把后端接口进行统一的封装
新建apis/user.jsx文件用于处理用户相关的接口
import http from ../utils/http;export const loginAPI (data) {return http({url: /authorizations,method: POST,data,});
};把前面的第5节中redux异步方法请求改写一下
// 异步方法封装
const fetchLogin (loginForm) {return async (dispatch) {const res await loginAPI(loginForm); // 注意loginAPI要引入dispatch(setToken(res.data.token));};
};后面其他api也就抽离出来了
三、Layout首页设计
1. 搭建首页基础框架
首页的基础框架长下面这个样子 先填入基础代码
import React, { useEffect, useState } from react;
import ./index.scss;
import {HomeOutlined,DiffOutlined,EditOutlined,LogoutOutlined,
} from ant-design/icons;
import { Breadcrumb, Layout, Menu, theme, Popconfirm } from antd;
import { Outlet, useLocation, useNavigate } from react-router-dom;
import { useDispatch, useSelector } from react-redux;
// import { fetchUserInfo, clearUserInfo } from ../../store/modules/user;
const { Header, Content, Sider } Layout;const items [{label: 首页,key: /,icon: HomeOutlined /,},{label: 文章管理,key: /article,icon: DiffOutlined /,},{label: 创建文章,key: /publish,icon: EditOutlined /,},
];const GLayout () {const [collapsed, setCollapsed] useState(false);const {token: { colorBgContainer, borderRadiusLG },} theme.useToken();return (Layoutstyle{{minHeight: 100vh,}}Siderthemelightcollapsiblecollapsed{collapsed}onCollapse{(value) setCollapsed(value)}div classNamedemo-logo-vertical /Menu// themedarkdefaultSelectedKeys{[/]}// selectedKeys{selectedKey}modeinlineitems{items}// onClick{clickMenu}//SiderLayoutHeaderclassNameheaderstyle{{padding: 0,background: colorBgContainer,}}div classNamelogo/divdiv classNameuser-infospan classNameuser-nameReact/spanspan classNameuser-logoutPopconfirmtitle是否确认退出okText退出cancelText取消// onConfirm{logout}LogoutOutlined / 退出/Popconfirm/span/div/HeaderContentclassNamecontentstyle{{margin: 5px 5px,background: colorBgContainer,borderRadius: borderRadiusLG,}}Outlet //Content/Layout/Layout);
};
export default GLayout;
补充对应的样式
.header {display: flex;justify-content: space-between;align-items: center;.logo {width: 200px;height: 60px;background: url(../../assets/global.png) no-repeat center / 160px auto;}.user-info {margin-right: 20px;color: #070707;.user-name {margin-right: 20px;}.user-logout {display: inline-block;cursor: pointer;}}
}.content {height: 100%;}2. 配置二级路由
就是把左侧的文章管理、创建文章和首页的路由给配置出来
使用步骤 在 pages 目录中分别创建Home数据概览/Article内容管理/Publish发布文章页面文件夹 分别在三个文件夹中创建 index.jsx 并创建基础组件后导出 在router/index.js 中配置嵌套子路由在Layout中配置二级路由出口 import { createBrowserRouter } from react-router-dom;
import Layout from ../pages/Layout;
import Login from ../pages/Login;
import AuthRoute from ../components/AuthRoute;
import Home from ../pages/Home;
import Article from ../pages/Article;
import Publish from ../pages/Publish;const router createBrowserRouter([{path: /,element: AuthRouteLayout //AuthRoute,children: [{// path: home,index: true,element: Home /},{path: article,element: Article /},{path: publish,element: Publish /},]},{path: /login,element: Login /,},
]);
export default router; 使用 Link 修改左侧菜单内容与子路由规则匹配实现路由切换前面提供的代码中已经配置好了就是Outlet /
3. 点击菜单跳转至对应的二级路由
在menu菜单中添加点击回调函数 const navigate useNavigate();const clickMenu (route) {navigate(route.key);}菜单反向高亮 是个啥意思勒目前点击菜单菜单栏是高亮的但是如果冲地址栏直接输入地址对应的菜单并不能高亮。。
我在使用elementplus时经常遇到这个问题一度以为是框架的bug现在才搞明白原来是自己没有处理好处理逻辑是先获取页面当前的地址然后把menu中的selectedKeys属性设置为当前地址
const GLayout () {// 省略部分代码// 获取当前页面地址-----------------1const location useLocation()const selectedKey location.pathnamereturn (LayoutHeader classNameheaderdiv classNamelogo /div classNameuser-infospan classNameuser-name{name}/spanspan classNameuser-logoutPopconfirm title是否确认退出 okText退出 cancelText取消LogoutOutlined / 退出/Popconfirm/span/div/HeaderLayoutSider width{200} classNamesite-layout-backgroundMenumodeinlinethemedark// 将当前页面地址设置为selectedKeys----------------2selectedKeys{selectedKey}items{items}style{{ height: 100%, borderRight: 0 }}onClick{menuClickHandler}/Menu/SiderLayout classNamelayout-content style{{ padding: 20 }}Outlet //Layout/Layout/Layout)
}4. 头部导航栏显示个人信息
这部分其实应该可以直接登录的时候就直接给了写在user的store中不过黑马提供的逻辑是重新调了一个接口这个接口返回的才是用户信息。别人怎么提供就怎么来吧
实现步骤 编写获取个人信息的接口 export const getProfileAPI () {return http({url: /user/profile,});
};在Redux的store中编写获取用户信息的相关逻辑 // 异步方法获取用户个人信息
const fetchUserInfo () {return async (dispatch) {try {const res await getProfileAPI();// console.log(res)dispatch(setUserInfo(res.data));} catch (error) {message.error(登录信息失效请重新登录);}};
};要把这个方法暴露出去 在Layout组件中触发action的执行 在Layout组件使用使用store中的数据进行用户名的渲染 以上两步代码如下 import { fetchUserInfo } from ../../store/modules/user;// 获取用户信息const dispatch useDispatch();useEffect(() {dispatch(fetchUserInfo());}, []);const { userInfo } useSelector((state) state.user);
span classNameuser-name{userInfo.name}/span5. 退出登录逻辑
也是常规操作之前是在pinia中写一个删除store的方法是个同步方法redux中差不多
实现步骤 为气泡确认框添加确认回调事件实际上就是onConfirm事件 Popconfirmtitle是否确认退出okText退出cancelText取消onConfirm{logout}LogoutOutlined / 退出/Popconfirm在store/userStore.jsx 中新增退出登录的action函数在其中删除token // 同步修改方法reducers: {........clearUserInfo(state) {state.token ;state.userInfo ;clearToken();},注意对外暴露出去 在回调事件中调用userStore中的退出action 清除用户信息返回登录页面 const logout () {dispatch(clearUserInfo())navigate(/login);
}6. 处理token失效
一般是token过期后的处理逻辑后端会返回401代码响应拦截器中根据这个代码进行路由跳转至登录页面
utils/http.jsx中响应拦截器添加如下代码
import router from ../router;// axios响应式拦截器
http.interceptors.response.use((res) res.data,(e) {console.log(e);// 401 -- token失效if(e.response.status 401){clearToken()// router实例router.navigate(/login)}return Promise.reject(e);}
);export default http;7. 首页绘制echarts图
echarts图我在vue中画过无数遍了这里的逻辑基本上一样
首先安装echarts
npm i echarts封装一个画图组件 在Home目录下新建components目录并创建BarChart.jsx组件 封装的代码如下 import * as echarts from echarts;
import { useEffect, useRef } from react;
// 父子组件通讯props
const BarChart ({title,xData,sData,style { width: 400px, height: 300px },
}) {const chartRef useRef(null);let initChart;const drawChart () {if (initChart ! null initChart ! initChart ! undefined) {initChart.dispose(); //销毁}initChart echarts.init(chartRef.current);const option {title: {text: title,},xAxis: {type: category,data: xData,},yAxis: {type: value,},series: [{data: sData,type: bar,},],};initChart.setOption(option);window.addEventListener(resize, () {initChart.resize();});};useEffect(() drawChart(), [xData, sData]);return (div ref{chartRef} style{style}/div/);
};
export default BarChart; 几个要点记录一下 BarChart是子组件父组件应传递 title, xData, sData, style 这几个属性react中获取dom是用的react中useRef钩子vue中直接就是refuseEffect需要监听数据变化然后重绘图 Home组件中调用子组件并传递子组件所需的数据 import BarChart from ./components/BarChart;const Home () {return (BarChartxData{[Vue, React, Angular]}sData{[2000, 5000, 1000]}title{三大框架使用率}/BarChartBarChartxData{[Vue, React, Angular]}sData{[200, 500, 100]}title{三大框架满意度}style{{ width: 500px, height: 400px }}/BarChart/);
};export default Home;
最终展示效果 四、发布文章模块
就是下面这个页面 1. 创建基础结构
import {Card,Breadcrumb,Form,Button,Radio,Input,Upload,Space,Select
} from antd
import { PlusOutlined } from ant-design/icons
import { Link } from react-router-dom
import ./index.scssconst { Option } Selectconst Publish () {return (div classNamepublishCardtitle{Breadcrumb items{[{ title: Link to{/}首页/Link },{ title: 发布文章 },]}/}FormlabelCol{{ span: 4 }}wrapperCol{{ span: 16 }}initialValues{{ type: 1 }}Form.Itemlabel标题nametitlerules{[{ required: true, message: 请输入文章标题 }]}Input placeholder请输入文章标题 style{{ width: 400 }} //Form.ItemForm.Itemlabel频道namechannel_idrules{[{ required: true, message: 请选择文章频道 }]}Select placeholder请选择文章频道 style{{ width: 400 }}Option value{0}推荐/Option/Select/Form.ItemForm.Itemlabel内容namecontentrules{[{ required: true, message: 请输入文章内容 }]}/Form.ItemForm.Item wrapperCol{{ offset: 4 }}SpaceButton sizelarge typeprimary htmlTypesubmit发布文章/Button/Space/Form.Item/Form/Card/div)
}export default Publish样式文件
.publish {position: relative;.publish-quill {.ql-editor {min-height: 300px;}}
}.ant-upload-list {.ant-upload-list-picture-card-container,.ant-upload-select {width: 146px;height: 146px;background: #eee;}
}2. 添加富文本编辑器
实现步骤 安装富文本编辑器 npm i react-quill2.0.0-beta.2这里可能会报错应该改成 npm i react-quill2.0.0-beta.2 --force导入富文本编辑器组件以及样式文件 渲染富文本编辑器组件 调整富文本编辑器的样式 publish.jsx中的代码 import {Card,Breadcrumb,Form,Button,Radio,Input,Upload,Space,Select,
} from antd;
import { PlusOutlined } from ant-design/icons;
import { Link } from react-router-dom;
import ./index.scss;
import ReactQuill from react-quill;
import react-quill/dist/quill.snow.css;const { Option } Select;const Publish () {return (div classNamepublishCardtitle{Breadcrumbitems{[{ title: Link to{/}首页/Link },{ title: 发布文章 },]}/}FormlabelCol{{ span: 4 }}wrapperCol{{ span: 16 }}initialValues{{ type: 1 }}Form.Itemlabel标题nametitlerules{[{ required: true, message: 请输入文章标题 }]}Input placeholder请输入文章标题 style{{ width: 400 }} //Form.ItemForm.Itemlabel频道namechannel_idrules{[{ required: true, message: 请选择文章频道 }]}Select placeholder请选择文章频道 style{{ width: 400 }}Option value{0}推荐/Option/Select/Form.ItemForm.Itemlabel内容namecontentrules{[{ required: true, message: 请输入文章内容 }]}ReactQuillclassNamepublish-quillthemesnowplaceholder请输入内容/ReactQuill/Form.ItemForm.Item wrapperCol{{ offset: 4 }}SpaceButton sizelarge typeprimary htmlTypesubmit发布文章/Button/Space/Form.Item/Form/Card/div);
};export default Publish;
3. antD中的select组件获取频道数据 这个数据是从后端获取的
实现步骤 使用useState初始化数据和修改数据的方法 const [channels, setChannels] useState([]);在useEffect中调用接口并保存数据 封装接口代码apis目录新建article.jsx文件填写接口请求函数 import http from ../utils/http;export const getChannelAPI () {return http({url: /channels,});
};Publish.jsx编写请求数据的函数并在副作用钩子中调用 const fetchChannels async () {const res await getChannelAPI();console.log(res);setChannels(res.data.channels);};useEffect(() {fetchChannels();}, []);使用数据渲染对应模版 Select placeholder请选择文章频道 style{{ width: 400 }}{channels.map((item) (Option value{item.id} key{item.id}{item.name}/Option))}/Select4. 发布文章 先封装发布文章的接口 // 新增
export const publishAPI (data) {return http({url: /mp/articles?draftfalse,method: POST,data,});
};form提交submit的onFinish回调函数 const onFinish async (values) {const { channel_id, content, title } values;const data {channel_id,content,title,type: 1,cover: {type: 1,images: [],},};await publishAPI(data)};apis目录新建article.jsx文件填写接口请求函数
import http from ../utils/http;export const getChannelAPI () {return http({url: /channels,});
};Publish.jsx编写请求数据的函数并在副作用钩子中调用 const fetchChannels async () {const res await getChannelAPI();console.log(res);setChannels(res.data.channels);};useEffect(() {fetchChannels();}, []);使用数据渲染对应模版 Select placeholder请选择文章频道 style{{ width: 400 }}{channels.map((item) (Option value{item.id} key{item.id}{item.name}/Option))}/Select4. 发布文章 先封装发布文章的接口 // 新增
export const publishAPI (data) {return http({url: /mp/articles?draftfalse,method: POST,data,});
};form提交submit的onFinish回调函数 const onFinish async (values) {const { channel_id, content, title } values;const data {channel_id,content,title,type: 1,cover: {type: 1,images: [],},};await publishAPI(data)};未完待续后面再补充吧~~~