1. 程式人生 > 其它 >極客園PC-第4章 文章列表

極客園PC-第4章 文章列表

文章列表功能

card元件與麵包屑導航

import { Card } from 'antd'
export default class ArticleList extends Component {
  render() {
    return (
      <div className="articleList">
        <Card title="麵包屑導航">我是內容</Card>
      </div>
    )
  }
}
  • 麵包屑導航的使用
export default class ArticleList extends Component {
  render() {
    return (
      <div className={styles.root}>
        <Card
          title={
            <Breadcrumb>
              <Breadcrumb.Item>
                <Link to="/home">首頁</Link>
              </Breadcrumb.Item>
              <Breadcrumb.Item>文章列表</Breadcrumb.Item>
            </Breadcrumb>
          }
        >
          內容
        </Card>
      </div>
    )
  }
}

搜尋表單基本結構

  • 複製表單的基本結構到元件中
  • 修改表單結構
<Card
  title={
    <Breadcrumb separator="/">
      <Breadcrumb.Item>
        <Link to="/home">首頁</Link>
      </Breadcrumb.Item>
      <Breadcrumb.Item>文章列表</Breadcrumb.Item>
    </Breadcrumb>
  }
>
  <Form>
    <Form.Item label="狀態" name="username">
      <Input />
    </Form.Item>

    <Form.Item label="頻道" name="password">
      <Input.Password />
    </Form.Item>

    <Form.Item label="日期" name="password">
      <Input.Password />
    </Form.Item>

    <Form.Item>
      <Button type="primary" htmlType="submit">
        篩選
      </Button>
    </Form.Item>
  </Form>
</Card>
  • 狀態的基本結構
<Form initialValues={{ status: null }}>
  <Form.Item label="狀態" name="status">
    <Radio.Group>
      <Radio value={null}>全部</Radio>
      <Radio value={0}>草稿</Radio>
      <Radio value={1}>待稽核</Radio>
      <Radio value={2}>稽核通過</Radio>
      <Radio value={3}>稽核失敗</Radio>
    </Radio.Group>
  </Form.Item>
  • 下拉框結構
<Form.Item label="頻道" name="password">
  <Select placeholder="請選擇頻道" style={{ width: 200 }}>
    <Option value="jack">Jack</Option>
    <Option value="lucy">Lucy</Option>
    <Option value="Yiminghe">yiminghe</Option>
  </Select>
</Form.Item>
  • 日期選擇基本結構
import { Card, Breadcrumb, Form, Button, Radio, Select, DatePicker } from 'antd'
const { RangePicker } = DatePicker

<Form.Item label="日期" name="password">
  <RangePicker />
</Form.Item>

日期中文處理

https://ant-design.gitee.io/components/date-picker-cn/

在index.js中

import React from 'react'
import ReactDOM from 'react-dom'
// 在 index.js 中匯入 antd 的樣式檔案
import 'antd/dist/antd.css'

import './index.css'
import { ConfigProvider } from 'antd'
import 'moment/locale/zh-cn'
import locale from 'antd/lib/locale/zh_CN'

import App from './App'

ReactDOM.render(
  <ConfigProvider locale={locale}>
    <App />
  </ConfigProvider>,
  document.getElementById('root')
)

頻道資料管理

  • 封裝介面
import request from 'utils/request'

/*
  獲取所有的頻道
*/
export const getChannels = () => {
  return request.get('/channels')
}

  • 傳送請求獲取資料
import { getChannels } from 'api/channel'

state = {
  channels: [],
}
async getChannelList() {
  const res = await getChannels()
  this.setState({
    channels: res.data.channels,
  })
}

componentDidMount() {
  this.getChannelList()
}
  • 渲染頻道資料
<Select placeholder="請選擇頻道" style={{ width: 200 }}>
  {this.state.channels.map((item) => (
    <Option value={item.id} key={item.id}>
      {item.name}
    </Option>
  ))}
</Select>

表格基本結構

  • 基本結構
import {
  Card,
  Breadcrumb,
  Form,
  Button,
  Radio,
  Select,
  DatePicker,
  Table,
  Tag,
  Space,
} from 'antd'

render() {
  const columns = [
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
      render: (text) => <a>{text}</a>,
    },
    {
      title: 'Age',
      dataIndex: 'age',
      key: 'age',
    },
    {
      title: 'Address',
      dataIndex: 'address',
      key: 'address',
    },
    {
      title: 'Tags',
      key: 'tags',
      dataIndex: 'tags',
      render: (tags) => (
        <>
          {tags.map((tag) => {
            let color = tag.length > 5 ? 'geekblue' : 'green'
            if (tag === 'loser') {
              color = 'volcano'
            }
            return (
              <Tag color={color} key={tag}>
                {tag.toUpperCase()}
              </Tag>
            )
          })}
        </>
      ),
    },
    {
      title: 'Action',
      key: 'action',
      render: (text, record) => (
        <Space size="middle">
          <a>Invite {record.name}</a>
          <a>Delete</a>
        </Space>
      ),
    },
  ]

  const data = [
    {
      key: '1',
      name: 'John Brown',
      age: 32,
      address: 'New York No. 1 Lake Park',
      tags: ['nice', 'developer'],
    },
    {
      key: '2',
      name: 'Jim Green',
      age: 42,
      address: 'London No. 1 Lake Park',
      tags: ['loser'],
    },
    {
      key: '3',
      name: 'Joe Black',
      age: 32,
      address: 'Sidney No. 1 Lake Park',
      tags: ['cool', 'teacher'],
    },
  ]

  return (
    <div className="articleList">
      <Card title={`根據篩選條件共查詢到${0}條資料`}>
        <Table dataSource={data} columns={columns} />
      </Card>
    </div>
  )
}

獲取文章列表資料

  • 封裝介面
import request from 'utils/request'

/**
 * 獲取文章列表
 * @param {*} params
 * @returns
 */
export const getArticles = (params) => {
  return request({
    url: '/mp/articles',
    method: 'get',
    params,
  })
}

  • 傳送請求獲取文章列表資料
  state = {
    channels: [],
    articles: [],
    total: 0,
  }
  async getChannelList() {
    const res = await getChannels()
    this.setState({
      channels: res.data.channels,
    })
  }

  async getArticleList() {
    const res = await getArticles()
    this.setState({
      articles: res.data.results,
      total: res.data.total_count,
    })
  }

  componentDidMount() {
    this.getChannelList()
    this.getArticleList()
  }

渲染表格資料

  • 修改columns
const columns = [
  {
    title: '封面',
    dataIndex: 'name',
  },
  {
    title: '標題',
    dataIndex: 'title',
  },
  {
    title: '狀態',
    dataIndex: 'status',
  },
  {
    title: '釋出時間',
    dataIndex: 'pubdate',
  },
  {
    title: '閱讀數',
    dataIndex: 'read_count',
  },
  {
    title: '評論數',
    dataIndex: 'comment_count',
  },
  {
    title: '點贊數',
    dataIndex: 'like_count',
  },
  {
    title: '操作',
  },
]
  • 封面處理
{
  title: '封面',
  dataIndex: 'cover',
  render(data) {
    const { images, type } = data
    if (type === 0) {
      return (
        <Image width={200} preview={false} height={150} src={defaultImg} />
      )
    }
    return (
      <Image width={200} height={150} src={images[0]} fallback={defaultImg} />
    )
  },
},
  • 狀態處理
// 通過物件來優化if/switch
// 使用方式:articleStatus[0] => { text: '草稿', color: '' }
const articleStatus = {
  0: { text: '草稿', color: 'gold' },
  1: { text: '待稽核', color: 'lime' },
  2: { text: '稽核通過', color: 'green' },
  3: { text: '稽核失敗', color: 'red' },
}

{
  title: '狀態',
  dataIndex: 'status',
  render: (data) => {
    const tagObj = articleStatus[data]
    return <Tag color={tagObj.color}>{tagObj.text}</Tag>
  },
},
  • 操作功能
{
    title: '操作',
    render() {
      return (
        <Space>
          <Button
            type="primary"
            shape="circle"
            icon={<EditOutlined />}
          ></Button>
          <Button
            type="primary"
            shape="circle"
            danger
            icon={<DeleteOutlined />}
          ></Button>
        </Space>
      )
    },
},

key屬性處理

<Card title={`根據篩選條件共查詢到${this.state.total}條資料`}>
  <Table
    rowKey="id"
    dataSource={this.state.articles}
    columns={columns}
  />
</Card>

分頁功能

  • 使用分頁元件
<Card title={`根據篩選條件共查詢到${this.state.total_count}條資料`}>
  <Table
    rowKey="id"
    dataSource={results}
    columns={columns}
    pagination={{
      position: ['bottomCenter'],
      current: page,
      pageSize: per_page,
      total: total_count,
      // 每頁大小 或者 頁碼 改變時,觸發的事件
      onChange: this.changePage,
    }}
  />
</Card>
  • 提供changePage事件
changePage = async (page, pageSize) => {
  console.log(page)
  const res = await getArticles({
    page,
    per_page: this.state.articles.per_page,
  })
  this.setState({
    articles: res.data,
  })
}

獲取表單的值進行篩選

  • 給表單註冊事件
<Form initialValues={{ status: -1 }} onFinish={this.onFinish}>
  • 給表單元素提供name屬性
<Form.Item label="狀態" name="status">
  <Radio.Group>
    <Radio value={-1}>全部</Radio>
    <Radio value={0}>草稿</Radio>
    <Radio value={1}>待稽核</Radio>
    <Radio value={2}>稽核通過</Radio>
    <Radio value={3}>稽核失敗</Radio>
  </Radio.Group>
</Form.Item>

<Form.Item label="頻道" name="channel_id">
  <Select placeholder="請選擇頻道" style={{ width: 200 }}>
    {this.state.channels.map((item) => (
      <Option value={item.id} key={item.id}>
        {item.name}
      </Option>
    ))}
  </Select>
</Form.Item>

<Form.Item label="日期" name="date">
  <RangePicker />
</Form.Item>
  • 傳送請求,獲取資料
onFinish = async (values) => {
  console.log(values)
  // 傳送請求,獲取資料
  const params = {}
  // 處理狀態
  if (values.status !== -1) {
    params.status = values.status
  }
  // 處理頻道
  if (values.channel_id) {
    params.channel_id = values.channel_id
  }
  // 處理日期
  if (values.date) {
    params.begin_pubdate = values.date[0].format('YYYY-MM-DD')
    params.end_pubdate = values.date[1].format('YYYY-MM-DD')
  }
  params.page = 1
  const res = await getArticles(params)
  console.log(res.data)
  this.setState({
    articles: res.data,
  })
}

時間的優化

// 處理日期
if (values.date) {
  params.begin_pubdate = values.date[0]
    .startOf('day')
    .format('YYYY-MM-DD HH:mm:ss')
  params.end_pubdate = values.date[1]
    .endOf('day')
    .format('YYYY-MM-DD HH:mm:ss')
}

修改分頁bug

changePage = async (page, pageSize) => {
  const res = await getArticles({
    ...this.params,
    page,
    per_page: this.state.articles.per_page,
  })
  this.setState({
    articles: res.data,
  })
}
onFinish = async (values) => {
  console.log(values)
  // 傳送請求,獲取資料
  const params = {}
  // 處理狀態
  if (values.status !== -1) {
    params.status = values.status
  }
  // 處理頻道
  if (values.channel_id) {
    params.channel_id = values.channel_id
  }
  // 處理日期
  if (values.date) {
    params.begin_pubdate = values.date[0]
      .startOf('day')
      .format('YYYY-MM-DD HH:mm:ss')
    params.end_pubdate = values.date[1]
      .endOf('day')
      .format('YYYY-MM-DD HH:mm:ss')
  }
  params.page = 1
  this.params = params
  const res = await getArticles(params)
  console.log(res.data)
  this.setState({
    articles: res.data,
  })
}

刪除功能

  • 註冊點選事件
<Button
  type="primary"
  shape="circle"
  danger
  icon={<DeleteOutlined />}
  onClick={() => this.handleDelete(data.id)}
></Button>
  • 準備彈窗
handleDelete = (id) => {
  confirm({
    title: '溫馨提示?',
    icon: <ExclamationCircleOutlined />,
    content: '你確定要刪除文章嗎',
    onOk() {
      // 傳送請求進行刪除
    },
  })
}
  • 封裝介面進行刪除
/**
 * 刪除文章
 * @param {*} id
 * @returns
 */
export const delArticle = (id) => {
  return request({
    url: `/mp/articles/${id}`,
    method: 'delete',
  })
}

  • 刪除功能完成
handleDelete = (id) => {
  confirm({
    title: '溫馨提示?',
    icon: <ExclamationCircleOutlined />,
    content: '你確定要刪除文章嗎',
    onOk: async () => {
      // 傳送請求進行刪除
      await delArticle(id)
      this.getArticleList(this.params)
    },
  })
}

釋出文章

基本結構準備

  • 麵包屑
import React, { Component } from 'react'
import { Card, Breadcrumb } from 'antd'
import { Link } from 'react-router-dom'
export default class ArticleList extends Component {
  render() {
    return (
      <div className="ArticleList">
        <Card
          title={
            <Breadcrumb separator=">">
              <Breadcrumb.Item>
                <Link to="/home">首頁</Link>
              </Breadcrumb.Item>
              <Breadcrumb.Item>釋出文章</Breadcrumb.Item>
            </Breadcrumb>
          }
        ></Card>
      </div>
    )
  }
}
  • 表單
import { Card, Breadcrumb, Form, Input, Radio, Space, Button } from 'antd'

<Form labelCol={{ span: 4 }} initialValues={{ type: 0 }}>
  <Form.Item label="標題" name="title">
    <Input placeholder="請輸入文章標題" style={{ width: 400 }} />
  </Form.Item>
  <Form.Item label="頻道" name="channel_id">
    頻道元件
  </Form.Item>
  <Form.Item label="封面">
    <Form.Item name="type">
      <Radio.Group onChange={this.changeImageType}>
        <Radio value={0}>無圖</Radio>
        <Radio value={1}>單圖</Radio>
        <Radio value={3}>三圖</Radio>
        {/* <Radio value={-1}>自動</Radio> */}
      </Radio.Group>
    </Form.Item>
    圖片上傳元件
  </Form.Item>
  <Form.Item label="內容" name="content">
    文章內容
  </Form.Item>
  <Form.Item wrapperCol={{ offset: 4 }}>
    <Space>
      <Button size="large" type="primary" htmlType="submit">
        釋出文章
      </Button>
      <Button size="large">存入草稿</Button>
    </Space>
  </Form.Item>
</Form>
  • 給表單註冊事件
<Form
  labelCol={{ span: 4 }}
  initialValues={{ type: 0 }}
  onFinish={this.onFinish}
>


onFinish = (values) => {
  console.log(values)
}

頻道元件封裝

  • 基礎封裝
import { Component } from 'react'
import { Select } from 'antd'
import { getChannels } from 'api/channel'

const { Option } = Select

class Channel extends Component {
  state = {
    channels: [],
  }

  componentDidMount() {
    this.getChannles()
  }

  // 獲取頻道列表資料的方法
  async getChannles() {
    const res = await getChannels()
    this.setState({
      channels: res.data.channels,
    })
  }

  render() {
    const { channels } = this.state

    return (
      <Select placeholder="請選擇文章頻道">
        {channels.map((item) => (
          <Option key={item.id} value={item.id}>
            {item.name}
          </Option>
        ))}
      </Select>
    )
  }
}

export default Channel

  • 使用頻道元件
import Channel from 'components/Channel'

<Form.Item label="頻道" name="channel_id">
  <Channel></Channel>
</Form.Item>
  • 讓頻道元件受控

參考文件:https://ant-design.gitee.io/components/form-cn/#components-form-demo-customized-form-controls

render() {
  const { channels } = this.state
  const { value, onChange } = this.props
  return (
    <Select
      placeholder="請選擇文章頻道"
      style={{ width: 200 }}
      value={value}
      onChange={onChange}
    >
      {channels.map((item) => (
        <Option key={item.id} value={item.id}>
          {item.name}
        </Option>
      ))}
    </Select>
  )
}

文章內容處理

import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';

<Form.Item label="內容" name="content">
  <ReactQuill
    theme="snow"
    placeholder="請輸入文章內容..."
  ></ReactQuill>
</Form.Item>
  • 注意:必須提供預設值,不然會報錯
  • 提供樣式
.publish {
  :global {
    .ql-editor {
      min-height: 300px;
    }
  }
}

圖片上傳元件

  • 基本結構
import {
  Card,
  Breadcrumb,
  Form,
  Input,
  Radio,
  Space,
  Button,
  Upload,
} from 'antd'
import { PlusOutlined } from '@ant-design/icons'

<Upload listType="picture-card">
  <PlusOutlined></PlusOutlined>
</Upload>
  • 設定圖片預設顯示
<Upload
  listType="picture-card"
  name="image"
  fileList={this.state.fileList}
>
  <PlusOutlined></PlusOutlined>
</Upload>


state = {
  // 存放上傳的檔案列表
  fileList: [
    {
      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
    },
    {
      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
    },
  ],
}

  • 圖片上傳功能, 需要提供name和action引數
<Upload
  listType="picture-card"
  name="image"
  action={`${baseURL}upload`}
  onChange={this.uploadImages}
  fileList={this.state.fileList}
>
  <PlusOutlined></PlusOutlined>
</Upload>
  • 獲取上傳成功的圖片地址
uploadImages = ({ file, fileList }) => {
  this.setState({
    fileList,
  })
}

控制圖片的上傳數量

  • 控制type的切換
state = {
  // 存放上傳的檔案列表
  fileList: [],
  type: 0,
}

<Radio.Group onChange={this.changeImageType}>
  <Radio value={0}>無圖</Radio>
  <Radio value={1}>單圖</Radio>
  <Radio value={3}>三圖</Radio>
</Radio.Group>


changeImageType = (e) => {
  this.setState({
    type: e.target.value,
  })
}
  • 根據type控制圖片的顯示
{this.state.type !== 0 && (
  <Upload
    listType="picture-card"
    name="image"
    action={`${baseURL}upload`}
    onChange={this.uploadImages}
    fileList={this.state.fileList}
  >
    <PlusOutlined></PlusOutlined>
  </Upload>
)}
  • 控制圖片的上傳資料
{this.state.type !== 0 && (
  <Upload
    listType="picture-card"
    name="image"
    action={`${baseURL}upload`}
    onChange={this.uploadImages}
    fileList={this.state.fileList}
  >
    {this.state.fileList.length < this.state.type && (
      <PlusOutlined></PlusOutlined>
    )}
  </Upload>
)}

圖片預覽功能

圖片格式校驗

表單校驗功能

  • 表單基本校驗
<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: '請選擇文章頻道' }]}
>
  <Channel></Channel>
</Form.Item>
<Form.Item label="封面">
  <Form.Item name="type">
    <Radio.Group onChange={this.changeImageType}>
      <Radio value={1}>單圖</Radio>
      <Radio value={3}>三圖</Radio>
      <Radio value={0}>無圖</Radio>
      {/* <Radio value={-1}>自動</Radio> */}
    </Radio.Group>
  </Form.Item>
  <div className="upload-list">
    {this.state.type !== 0 && (
      <Upload
        listType="picture-card"
        name="image"
        action={`${baseURL}upload`}
        onChange={this.uploadImages}
        fileList={this.state.fileList}
      >
        {this.state.fileList.length < this.state.type && (
          <PlusOutlined></PlusOutlined>
        )}
      </Upload>
    )}
  </div>
</Form.Item>
<Form.Item
  label="內容"
  name="content"
  rules={[{ required: true, message: '請輸入文章內容' }]}
>
  <ReactQuill
    theme="snow"
    placeholder="請輸入文章內容..."
  ></ReactQuill>
</Form.Item>
  • 圖片長度的校驗
onFinish = async (values) => {
  // 圖片校驗
  if (this.state.type !== this.state.fileList.length) {
    return message.warn('上傳的圖片數量不對')
  }
}

傳送請求-新增文章

  • 封裝介面
/**
 * 傳送請求新增文章
 * @param {*} data
 * @returns
 */
export const addArticle = (data) => {
  return request({
    url: '/mp/articles',
    method: 'post',
    data,
  })
}

  • 傳送請求-處理資料並且新增文章
onFinish = async (values) => {
  console.log(values)
  // 處理資料,新增文章
  const images = this.state.fileList.map((item) => {
    if (item.url) {
      return item.url
    }
    return item.response.data.url
  })
  const res = await addArticle({
    ...values,
    cover: {
      type: values.type,
      images,
    },
  })
  message.success('新增文章成功')
  this.props.history.push('/home/list')
}

存入草稿功能

  • 修改介面
/**
 * 傳送請求新增文章
 * @param {*} data
 * @returns
 */
export const addArticle = (data, draft = false) => {
  return request({
    url: '/mp/articles?draft=' + draft,
    method: 'post',
    data,
  })
}
  • 註冊點選事件
<Button size="large" onClick={this.addDraft}>
  存入草稿
</Button>
  • 提供事件
  onFinish = async (values) => {
    this.save(values, false)
  }
  save = async (values, draft) => {
    // 圖片校驗
    if (this.state.type !== this.state.fileList.length) {
      return message.warn('上傳的圖片數量不對')
    }
    // 處理資料,新增文章
    const images = this.state.fileList.map((item) => {
      if (item.url) {
        return item.url
      }
      return item.response.data.url
    })
    await addArticle(
      {
        ...values,
        cover: {
          type: values.type,
          images,
        },
      },
      draft
    )
    message.success('新增文章成功')
    this.props.history.push('/home/list')
  }
  addDraft = async () => {
    // 獲取表單的資料
    const values = await this.formRef.current.validateFields()
    this.save(values, true)
  }

修改功能

配置修改文章的路由

  • 給修改按鈕註冊點選事件
<Button
  type="primary"
  shape="circle"
  icon={<EditOutlined />}
  onClick={() => this.handleEdit(data.id)}
/>

// 修改
handleEdit = (id) => {
  this.props.history.push(`/home/publish/${id}`)
}
  • 配置修改文章的路由
{/* 新增 */}
<Route
  exact
  path="/home/publish"
  component={ArticlePublish}
></Route>
{/* 修改的路由 */}
<Route
  path="/home/publish/:id"
  component={ArticlePublish}
></Route>

資料回顯-獲取資料

思路

  1. 在元件中判斷能夠通過params獲取到id值,如果能夠獲取到id,說明是修改,如果獲取不到id說明是新增
  2. 我們根據是否是新增可以修改對應的文字。【釋出文章】或者【修改文章】
  3. 封裝介面,用於獲取文章的詳情資訊
  4. 傳送請求,獲取文章的詳細資訊
  • 獲取位址列的id值
state = {
  // 文章的封面型別
  type: 1,
  // 用於控制上傳的圖片以及圖片的顯示
  fileList: [],
  showPreview: false,
  previewUrl: '',
  // 編輯的id
+  id: this.props.match.params.id,
}
  • 根據是否有id值,控制文字的顯示
{id ? '編輯文章' : '釋出文章'}
  • 封裝介面,獲取文章詳情
/**
 * 獲取文章詳情資訊
 * @param {*} id
 * @returns
 */
export const getArticleById = (id) => {
  return request.get(`/mp/articles/${id}`)
}
  • 頁面渲染完成的時候,傳送請求-獲取資料
async componentDidMount() {
  if (this.state.id) {
    // 需要發請求,獲取文章詳細資訊
    const res = await getArticleById(this.state.id)
    console.log('res', res)
  }
}

資料回顯-回顯資料

思路:

  1. 通過Form元件提供的方法 setFieldsValue 可以給表單設定值
  2. 設定fileList的值,fileList控制封面顯示的
  • 設定表單的值
async componentDidMount() {
  if (this.state.id) {
    // 需要發請求,獲取文章詳細資訊
    const res = await getArticleById(this.state.id)
    const values = {
      ...res.data,
      type: res.data.cover.type,
    }
    // 給表單設定values值
    this.formRef.current.setFieldsValue(values)
    const fileList = res.data.cover.images.map((item) => {
      return {
        url: item,
      }
    })
    this.setState({
      fileList,
    })
  }
}

修復bug-修改切換到新增的bug

{/* 新增 */}
<Route
  exact
  path="/home/publish"
  component={ArticlePublish}
  key="add"
></Route>
{/* 修改的路由 */}
<Route
  path="/home/publish/:id"
  component={ArticlePublish}
  key="edit"
></Route>

修改功能完成

  1. 封裝介面,用於修改文章
  2. 判斷是否有id,如果有,傳送請求修改,否則,傳送請求新增
  • 封裝介面


/**
 * 修改文章的介面
 * @param {*} data
 * @param {*} draft
 * @returns
 */
export const updateArticle = (data, draft) => {
  return request({
    url: `/mp/articles/${data.id}?draft=${draft}`,
    method: 'PUT',
    data,
  })
}

  • 判斷
async save(values, draft) {
  const { fileList, type } = this.state
  if (fileList.length !== type) {
    return message.warn('上傳的圖片數量不正確')
  }
  // 根據fileList得到
  const images = fileList.map((item) => {
    return item.url || item.response.data.url
  })
  if (this.state.id) {
    // 修改文章
    await updateArticle(
      {
        ...values,
        cover: {
          type,
          images,
        },
        id: this.state.id,
      },
      draft
    )
    message.success('修改成功')
  } else {
    // 新增文章
    await addAritcle(
      {
        ...values,
        cover: {
          type,
          images,
        },
      },
      draft
    )
    message.success('新增成功')
  }
  this.props.history.push('/home/list')
}

導航高亮優化

  • 給選單按鈕提供selectedKeys屬性
<Menu
  theme="dark"
  mode="inline"
  selectedKeys={[this.state.selectedKey]}
  style={{ height: '100%', borderRight: 0 }}
>
    
state = {
  profile: {},
  selectedKey: this.props.location.pathname,
}
  • 需要在元件更新的時候,修改selectedKeys的值
// 元件更新完成的鉤子函式,,,路由變化了,元件也是會重新渲染
// prevProps: 上一次的props
componentDidUpdate(prevProps) {
  // 判斷是否是url地址發生了變化,如果是,才更新
  let pathname = this.props.location.pathname
  if (this.props.location.pathname !== prevProps.location.pathname) {
    // 考慮修改文章的高亮問題
    if (pathname.startsWith('/home/publish')) {
      pathname = '/home/publish'
    }
    this.setState({
      selectedKey: pathname,
    })
  }
}

注意:在componentDidUpdate中想要呼叫setState必須新增判斷,不然會死迴圈