从0开始手把手教你做极客园项目(四)——文章列表功能实现


前言

本文详细讲解了文章列表功能的实现,完整项目可参考github
从0开始手把手教你做极客园项目(三)——发布文章功能实现
从0开始手把手教你做极客园项目(二)——首页功能实现
从0开始手把手教你做极客园项目(一)——登录功能实现

1.静态结构准备

在article文件夹新建一个index.tsx文件,创建模拟数据和antd组件模板

import { Link } from 'react-router-dom'
import { Card, Breadcrumb, Form, Button, Radio, DatePicker, Select } from 'antd'
import locale from 'antd/es/date-picker/locale/zh_CN'//引入汉化包
import { Table, Tag, Space } from 'antd'
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
import img404 from '@/assets/error.png'

const { Option } = Select
const { RangePicker } = DatePicker

const Article = () => {
   // 准备列数据
   const columns = [
    {
      title: '封面',
      dataIndex: 'cover',
      width: 120,
      render: (cover: { images: any[] }) => {
        return <img src={cover.images[0] || img404} width={80} height={60} alt="" />
      }
    },
    {
      title: '标题',
      dataIndex: 'title',
      width: 220
    },
    {
      title: '状态',
      dataIndex: 'status',
      render: (data: any) => <Tag color="green">审核通过</Tag>
    },
    {
      title: '发布时间',
      dataIndex: 'pubdate'
    },
    {
      title: '阅读数',
      dataIndex: 'read_count'
    },
    {
      title: '评论数',
      dataIndex: 'comment_count'
    },
    {
      title: '点赞数',
      dataIndex: 'like_count'
    },
    {
      title: '操作',
      render: (data: any) => {
        return (
          <Space size="middle">
            <Button type="primary" shape="circle" icon={<EditOutlined />} />
            <Button
              type="primary"
              danger
              shape="circle"
              icon={<DeleteOutlined />}
            />
          </Space>
        )
      }
    }
  ]
  // 准备表格body数据
  const data = [
    {
      id: '8218',
      comment_count: 0,
      cover: {
        images: [],
      },
      like_count: 0,
      pubdate: '2019-03-11 09:00:00',
      read_count: 2,
      status: 2,
      title: 'wkwebview离线化加载h5资源解决方案'
    }
  ]
  return (
    <div>
      <Card
        title={
          <Breadcrumb items={[
            { title: <Link to={'/'}>首页</Link> },
            { title: '文章列表' },
          ]} />
        }
        style={{ marginBottom: 20 }}
      >
        <Form initialValues={{ status: '' }}>
          <Form.Item label="状态" name="status">
            <Radio.Group>
              <Radio value={''}>全部</Radio>
              <Radio value={0}>草稿</Radio>
              <Radio value={2}>审核通过</Radio>
            </Radio.Group>
          </Form.Item>

          <Form.Item label="频道" name="channel_id">
            <Select
              placeholder="请选择文章频道"
              defaultValue="lucy"
              style={{ width: 120 }}
            >
              <Option value="jack">Jack</Option>
              <Option value="lucy">Lucy</Option>
            </Select>
          </Form.Item>

          <Form.Item label="日期" name="date">
            {/* 传入locale属性 控制中文显示*/}
            <RangePicker locale={locale}></RangePicker>
          </Form.Item>

          <Form.Item>
            <Button type="primary" htmlType="submit" style={{ marginLeft: 40 }}>
              筛选
            </Button>
          </Form.Item>
        </Form>
      </Card>
      <Card title={`根据筛选条件共查询到 count 条结果:`}>
        <Table rowKey="id" columns={columns} dataSource={data} />
      </Card>
    </div>
  )
}

export default Article

在这里插入图片描述

2.封装自定义hook

由于频道列表重复渲染,有三种方式实现,一是在之前发布文章的时候的获取频道列表渲染的代码重新写一份,但是这样代码的复用性不是很高,二是在redux里存入状态数据,比较复杂,三是封装自定义hook用户通过解构获得数据,这里采用的是第三种方法
在src新建一个hooks文件夹,添加useChannel.tsx的文件,把之前写的获取频道列表数据的代码粘贴过来

import { getChannelAPI } from "@/apis/article"
import { Channel } from "@/pages/Publish"
import { useEffect, useState } from "react"


function useChannels() {
    const [channels, setchannels] = useState<Channel[]>([])
    useEffect(() => {
        const channel = async () => {
            const res = await getChannelAPI()
            setchannels(res.data.channels)
        }
        channel()
    }, [])
    return {channels}
}

export {useChannels}

把之前调用频道列表的代码改写,在需要新获取频道列表数据的地方使用自定义hook获取数据

const { channels } = useChannels()
 <Select
              placeholder="请选择文章频道"
              defaultValue="lucy"
              style={{ width: 120 }}
            >
              {channels.map(item=><Option value={item.id}>{item.name}</Option>)}
            </Select>

在这里插入图片描述

3.渲染文章列表

首先封装一个获取列表数据的api,在apis/articles里添加

export function getArticleAPI(){
    return request({
         url:'/mp/articles',
         method:'GET',
     })
 }

然后在article的组件里利用useState将获取的数据存入,并且通过useEffect异步获取数据


const [list,setlist]=useState([])
  
  useEffect(()=>{
    async function getList() {
      const res=await getArticleAPI()
      setlist(res.data.results)
    }
    getList()
  },[])
<Card title={`根据筛选条件共查询到 ${list.length} 条结果:`}>
        <Table rowKey="id" columns={columns} dataSource={list} />
      </Card>

在这里插入图片描述

4.状态适配

在文章刚发布的时候显示待审核,等到后端设置时间过去时变成审核通过,需要在原来文章组件中修改审核状态字段为三元运算,data为后端返回的status数据1和2,1表示待审核,2表示审核通过

{
      title: '状态',
      dataIndex: 'status',
      //data为后端返回的状态status
      render: (data:number) => <Tag color={data===1?'warning':'success'}>{data===1?'待审核':'审核通过'}</Tag>
    },

在这里插入图片描述

5.筛选功能实现

给form表单添加一个onFinish方法,指定获取表单value的类型ValueInterface

  const [reqdata,setdata]=useState({
    status:'',
    channel_id:'',
    begin_pubdate:'',
    end_pubdate:'',
    page:1,
    per_page:4
  })
  const onfinish=(value:ValueInterface)=>{
     setdata(
      {
        ...reqdata,
        channel_id:value.channel_id,
        status:value.status,
        begin_pubdate:value.date[0].format('YYYY-MM-DD'),
        end_pubdate:value.date[1].format('YYYY-MM-DD')
      }
     )
  }
interface ValueInterface {
  channel_id: string;
  status: string;
  date: [moment.Moment, moment.Moment];
}

但是这个moment库需要在命令行下载

 npm install moment --legacy-peer-deps

在渲染列表的useEffect函数中添加依赖数组reqdata,这样当我们提交数据时将获取的参数传给后端

  useEffect(()=>{
    async function getList() {
      const res=await getArticleAPI(reqdata)
      setlist(res.data.results)
    }
    getList()
  },[reqdata])

在这里插入图片描述

6.分页功能实现

在table里添加组件的pagination属性,包括总共的数量和每页的数量来自动划分页数,并且添加一个事件改变函数,当切换不同页数时,根据页数的不同来重新渲染列表

<Card title={`根据筛选条件共查询到 ${count} 条结果:`}>
        <Table rowKey="id" columns={columns} dataSource={list} pagination={
          {
            total: count,
            pageSize: reqdata.per_page,
            onChange: onchange
          }
        } />
      </Card>
const onchange = (page: number) => {
    setdata({
      ...reqdata,
      page
    })
  }

在这里插入图片描述

总结

本次文章列表实现了渲染,筛选,分页的逻辑,并且对一些功能做了一些优化,明天将会是极客园项目的最后一篇,喜欢的小伙伴点点关注,点点赞

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值