前面两篇博客中详细介绍了登录功能和首页功能的实现,本文详细讲解了文章发布功能的实现,可以参考以前文章
从0开始手把手教你做极客园项目(一)——登录功能实现
从0开始手把手教你做极客园项目(二)——首页功能实现
极客园github源码地址
由于方便后期维护代码,所以将所有接口都存放到api文件夹下进行封装统一管理,在api文件夹创建一个user.tsx文件,存放所有用户登录的接口管理
import { logindata } from "@/pages/Login";
import { request } from "@/utils";
//登录请求
export function loginAPI(formdata:logindata){
return request({
url:'/authorizations',
method:'POST',
data:formdata
})
}
//获取个人信息
export function getProfileAPI(){
return request({
url:'/user/profile',
method:'GET',
})
}
在store/modules/user.tsx里修改原来请求接口的代码
const fetchLogin: (loginForm: logindata) => (dispatch: Dispatch) => Promise<void> = (loginForm) => {
return async (dispatch: Dispatch) => {
const res = await loginAPI(loginForm)
dispatch(setToken(res.data.token))
}
}
const fetchUser: () => (dispatch: Dispatch) => Promise<void> = () => {
return async (dispatch: Dispatch) => {
const res= await getProfileAPI()
dispatch(setUser(res.data))
}
}
先创建基础样式,在pages文件夹下新建一个index.tsx和index.scss文件
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'
const { Option } = Select
const Publish = () => {
return (
<div className="publish">
<Card
title={
<Breadcrumb items={[
{ title: <Link to={'/'}>首页</Link> },
{ title: '发布文章' },
]}
/>
}
>
<Form
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
initialValues={{ type: 1 }}
>
<Form.Item
label="标题"
name="title"
rules={[{ required: true, message: '请输入文章标题' }]}
>
<Input placeholder="请输入文章标题" style={{ width: 400 }} />
</Form.Item>
<Form.Item
label="频道"
name="channel_id"
rules={[{ required: true, message: '请选择文章频道' }]}
>
<Select placeholder="请选择文章频道" style={{ width: 400 }}>
<Option value={0}>推荐</Option>
</Select>
</Form.Item>
<Form.Item
label="内容"
name="content"
rules={[{ required: true, message: '请输入文章内容' }]}
></Form.Item>
<Form.Item wrapperCol={{ offset: 4 }}>
<Space>
<Button size="large" type="primary" htmlType="submit">
发布文章
</Button>
</Space>
</Form.Item>
</Form>
</Card>
</div>
)
}
export default Publish
.publish {
position: relative;
}
.ant-upload-list {
.ant-upload-list-picture-card-container,
.ant-upload-select {
width: 146px;
height: 146px;
}
}
npm i react-quill@2.0.0-beta.2 --legacy-peer-deps
在组件里导入富文本编辑器
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'
<Form.Item
label="内容"
name="content"
rules={[{ required: true, message: '请输入文章内容' }]}
>
<ReactQuill
className="publish-quill"
theme="snow"
placeholder="请输入文章内容"
/>
</Form.Item>
首先要通过后端获取频道列表的数据,在apis里面封装一个article.tsx函数用于获取接口数据
import { request } from "@/utils";
//登录请求
export function getChannelAPI(){
return request({
url:'/channels',
method:'GET',
})
}
在publish的组件里用useState存放列表数据,通过useEffect进行界面渲染时就执行获取列表数据的方法
interface Channel{
id:number,
name:string
}
const [channels, setchannels] = useState<Channel[]>([])
useEffect(() => {
const channel = async() => {
const res=await getChannelAPI()
setchannels(res.data.channels)
}
channel()
},[])
在视图上通过map遍历渲染列表
<Select placeholder="请选择文章频道" style={{ width: 400 }}>
{channels.map(item=><Option key={item.id} value={item.id}>{item.name}</Option>)}
</Select>
首先在apis里封装一个post请求,用于将收集的表单数据传输给后端,在article.tsx里添加
export function subArticleAPI(formdata:article){
return request({
url:'/mp/articles?draft=false',
method:'POST',
data:formdata
})
}
在publish.tsx组件中给form表单添加onfinish功能,用于点击发布文章时向后端传递收集的表单数据
export interface article{
title:string,
content:string,
cover:{
type:number,
images:string[]
},
channel_id:number
}
const onfinish=(data:article)=>{
const {title,content,channel_id} =data
const reqdata={
title,
content,
cover:{
type:0,
images:[]
},
channel_id
}
subArticleAPI(reqdata)
}
在publish.tsx里添加以下样板代码
<Form.Item label="封面">
<Form.Item name="type">
<Radio.Group onChange={onType}>
<Radio value={1}>单图</Radio>
<Radio value={3}>三图</Radio>
<Radio value={0}>无图</Radio>
</Radio.Group>
</Form.Item>
{type > 0 && <Upload
listType="picture-card"//文件框外观样式
showUploadList//控制显示上传列表
action={'http://geek.itheima.net/v1_0/upload'}
onChange={onUploadChange}
name="image"
maxCount={type}
>
<div style={{ marginTop: 8 }}>
<PlusOutlined />
</div>
</Upload>}
</Form.Item>
添加两个函数,onUploadChange表示上传图片,onType表示当前选中的是单图还是多图还是无图
const onUploadChange = (image: imageList) => {
setImageList(image.fileList)
}
const [type, settype] = useState(1)
const onType = (e: RadioChangeEvent) => {
settype(e.target.value)
}
通过map方法把state里面的url获取出来传递给后端,把原来基础的提交的数据改为获取到的数据即可
const onfinish = (data: article) => {
const { title, content, channel_id } = data
const reqdata = {
title,
content,
cover: {
type,
images
},
channel_id
}
if (imageList.length !== type) { return message.warning }
else { subArticleAPI(reqdata) }
}
const images = imageList.map((image) => image.response.data.url);
本次实现了文章上传功能,包括频道列表渲染以及上传图片的相关操作,后续将会接着更新,喜欢的小伙伴点点关注点点赞