从0开始手把手教你做极客园项目(三)——发布文章功能实现


前言

前面两篇博客中详细介绍了登录功能和首页功能的实现,本文详细讲解了文章发布功能的实现,可以参考以前文章
从0开始手把手教你做极客园项目(一)——登录功能实现
从0开始手把手教你做极客园项目(二)——首页功能实现
极客园github源码地址

1.api封装

由于方便后期维护代码,所以将所有接口都存放到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))
    }
}

2.基础文章发布

先创建基础样式,在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>

在这里插入图片描述

3.频道列表渲染

首先要通过后端获取频道列表的数据,在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>

在这里插入图片描述

4.收集数据传给后端

首先在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)
  }

在浏览器中可以看见发送的数据和响应成功的结果
在这里插入图片描述
在这里插入图片描述

5.上传封面图片

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

6.提交表单后端

通过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);

可以看见向后端发送的请求
在这里插入图片描述

总结

本次实现了文章上传功能,包括频道列表渲染以及上传图片的相关操作,后续将会接着更新,喜欢的小伙伴点点关注点点赞

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值