image.png

01-内容管理-筛选区域结构

完成内容管理筛选区域基本架子
大致步骤:

  • 准备根容器间距样式
  • 使用Card的组件确定区域
  • 使用Breadcrumb组件作为Card组件标题
  • 使用Form组件搭建筛选项目,表单元素暂时不写

核心代码:
Article/index.module.scss
.root {
padding: 24px;
}
Article/index.jsx
import { Form, Button, Card, Breadcrumb } from “antd”;
import { Link } from “react-router-dom”;
import styles from “./index.module.scss”;
const Article = () => {
return (


title={
// 面包屑


首页

内容管理

}
>
{/ 表单 /}










);
};

export default Article;

02-内容管理-筛选区域表单

完成内容管理筛选区域表单绘制
大致步骤:

  • 状态使用 Radio.Group 组件
  • 频道使用 Select 组件
  • 日期使用 DatePicker.RangePicker 组件,需要本地化(中文)

核心代码:Article/index.jsx

  • 导包

import {
Card,
Form,
+ Radio,
+ Select,
Button,
+ DatePicker,
Breadcrumb,
message,
} from “antd”;
+import “moment/locale/zh-cn”;
+import locale from “antd/es/date-picker/locale/zh_CN”;

  • 状态



全部
草稿
待审核
已通过
已拒绝

  • 频道



  • 日期




注意:Radio 选项的值是 undefined 默认选中,日期组件需要本地化 zh_CN

03-内容管理-结果区域表格

完成结果区域结构和准备表格
大致步骤:

  • 使用 Card 组件搭建基本结构
  • 分析 Table 组件的使用
  • 使用 Table 组件完成文章列表的搭建

核心代码:
Article/index.jsx

  1. 使用 Card 组件搭建基本结构
title={根据筛选条件共查询到 100 条结果:}
style={{ marginTop: 24 }}
>
放置表格组件
  1. 分析 Table 组件的使用
    1. dataSource 属性,设置数据
    2. columns 属性,设置每一列的标题和显示的内容 title 表头,dataIndex 数据字段
    3. 如果显示的内容需要使用JSX可以使用 render 选项,使用函数返回来渲染。
  2. 使用 Table 组件完成文章列表的搭建

import { EditOutlined, DeleteOutlined } from “@ant-design/icons”;
import { Table, Space} from “antd”;
const columns = [
{
title: “封面”,
dataIndex: “cover”,
key: “cover”,
render: () => ‘自定义封面’,
},
{
title: “标题”,
dataIndex: “title”,
key: “title”,
},
{
title: “状态”,
dataIndex: “status”,
key: “status”,
render: () => ‘自定义状态’,
},
{
title: “发布时间”,
dataIndex: “pubdate”,
key: “pubdate”,
},
{
title: “阅读数”,
dataIndex: “read_count”,
key: “read_count”,
},
{
title: “评论数”,
dataIndex: “comment_count”,
key: “comment_count”,
},
{
title: “点赞数”,
dataIndex: “like_count”,
key: “like_count”,
},
{
title: “操作”,
key: “action”,
render: () => (

}/>
}/>

),
},
];

04-内容管理-全局语言本地化

使用ConfigProvider组件项目语言本地化
大致步骤:

  • 入口使用 ConfigProvider 改用全局语言本地化 文档
  • 内容管理移除语言设置

核心代码 :

  • 全局配置 src/index.js

+import ‘moment/locale/zh-cn’;
+import locale from ‘antd/lib/locale/zh_CN’;
+import { ConfigProvider } from ‘antd’;
ReactDOM.render(
// 关联react和redux

+

+

,
document.getElementById(‘root’)
)

  • 日期组件修改 Article/index.jsx

-import “moment/locale/zh-cn”;
-import locale from “antd/es/date-picker/locale/zh_CN”;

-
+

05-内容管理-频道数据和渲染

完成频道数据获取和渲染
大致步骤:

  • 准备获取频道数据的 thunk action
  • 存储获取的频道数据 readucer
  • 组件第一次渲染 dispatch 分发 action
  • 渲染频道

核心代码:

  1. 准备获取频道数据的 thunk action

actions/article.js
import { http } from ‘@/utils’

export const getChannels = () => {
return async dispatch => {
const data = await http.get(‘channels’)
dispatch({ type: ‘article/setChannels’, payload: data.channels })
}
}
actions/index.js
// 把user模块下的所有内容按需导出
export from ‘./user’
+// 把article模块下的所有内容按需导出
+export
from ‘./article’

  1. 存储获取的频道数据 readucer

reducers/article.js
const initialState = {
// 频道
channels: []
}

const article = (state = initialState, action) => {
if (action.type === “article/setChannels”) {
return {
…state,
channels: action.payload
}
}
return state
}

export default article
reducers/index.js
import { combineReducers } from ‘redux’

import user from ‘./user’
+import article from ‘./article’
export default combineReducers({
user,
+ article,
})

  1. 组件第一次渲染 dispatch 分发 action

Article/index.jsx
import { useDispatch, useSelector } from “react-redux”;
import { useEffect } from “react”;
import { getChannels } from “@/store/actions”;
const Article = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getChannels());
}, [dispatch]);
// 省略…

  1. 渲染频道

import { useDispatch, useSelector } from “react-redux”;
const { channels } = useSelector((state) => state.article);

06-内容管理-列表数据

完成获取文章列表数据
大致步骤:

  • 准备获取文章数据的 thunk action
  • 存储获取的文章数据 readucer
  • 组件第一次渲染 dispatch 分发 action
  • 渲染列表

核心代码:

  1. 准备获取文章数据的 thunk action

actions/article.js
export const getArticles = (params) => {
return async dispatch => {
const data = await http.get(‘mp/articles’, { params })
dispatch({ type: ‘article/setArticles’, payload: data })
}
}

  1. 存储获取的文章数据 readucer

reducers/article.js
const initialState = {
// 频道
channels: [],
+ // 文章
+ results: [],
+ page: 1,
+ per_page: 10,
+ total_count: 0
}

const article = (state = initialState, action) => {
if (action.type === “article/setChannels”) {
return {
…state,
channels: action.payload
}
}
+ if (action.type === “article/setArticles”) {
+ return {
+ …state,
+ …action.payload
+ }
+ }
return state
}

export default article

  1. 组件第一次渲染 dispatch 分发 action

+import { getChannels, getArticles } from “@/store/actions”;
const Article = () => {
const dispatch = useDispatch();
useEffect(() => {
// 获取频道数据
dispatch(getChannels());
+ // 获取文章数据
+ dispatch(getArticles({}));
}, [dispatch]);

07-内容管理-列表渲染

完成文章列表渲染
大致步骤:

  • 拿到列表渲染数据
  • 阅读状态数据(不同状态文字和颜色不一样)
  • 渲染自定义列

核心代码:Article/index.jsx

  • 拿到列表渲染数据

+const { channels, results } = useSelector((state) => state.article);

  • 阅读状态数据(不同状态文字和颜色不一样)

const statusLabel = [
{ text: “草稿”, color: “default” },
{ text: “待审核”, color: “blue” },
{ text: “审核通过”, color: “green” },
{ text: “审核拒绝”, color: “red” },
];

  • 通过 colums 配置渲染列表

import defaultImg from “@/assets/error.png”;
const columns = [
{
title: “封面”,
dataIndex: “cover”,
key: “cover”,
render: (cover) => (
src={cover?.images?.[0] || defaultImg}
style={{ objectFit: “cover” }}
width={200}
height={120}
/>
),
},
{
title: “标题”,
dataIndex: “title”,
key: “title”,
},
{
title: “状态”,
dataIndex: “status”,
key: “status”,
render: (status) => {
const info = statusLabel[status];
return {info.text};
},
},
{
title: “发布时间”,
dataIndex: “pubdate”,
key: “pubdate”,
},
{
title: “阅读数”,
dataIndex: “read_count”,
key: “read_count”,
},
{
title: “评论数”,
dataIndex: “comment_count”,
key: “comment_count”,
},
{
title: “点赞数”,
dataIndex: “like_count”,
key: “like_count”,
},
{
title: “操作”,
key: “action”,
render: () => (

} />
} />

),
},
];

08-内容管理-筛选功能

完成点击筛选按钮根据筛选条件查询文章列表
大致步骤:

  • 绑定表单的完成事件 onFinish
  • 处理表单的日期数据为后台需要参数,通过 dispatch 调用 action 传参,完成列表更新

核心代码:Article/index.jsx

  1. 绑定表单的完成事件 onFinish


+

+



// 改变筛选条件查询
const onFinish = (values) => {
console.log(values)
};

  1. 处理表单的日期数据为后台需要参数,通过 dispatch 调用 action 传参,完成列表更新

// 改变筛选条件查询
const onFinish = (values) => {
const params = {}
params.status = values.status;
params.channel_id = values.channel_id;
if (values.dateArr) {
params.begin_pubdate = values.dateArr[0].format(
“YYYY-MM-DD HH:mm:ss”
);
params.end_pubdate = values.dateArr[1].format(
“YYYY-MM-DD HH:mm:ss”
);
} else {
params.begin_pubdate = undefined;
params.end_pubdate = undefined;
}
dispatch(getArticles(params));
};

09-内容管理-分页功能

完成分页的渲染和分页功能
大致步骤:

  • 拿到分页数据通过 Tabel de pagination 属性渲染分页
  • 实现分页功能,支持分页页码和分页条数设置

核心代码:Article/index.jsx

  1. 拿到分页数据通过 Tabel de pagination 属性渲染分页

const {
channels,
results,
+ page,
+ per_page,
+ total_count
} = useSelector((state) => state.article);
columns={columns}
dataSource={state.results}
rowKey=”id”
+ pagination={{
+ current: page,
+ pageSize: per_page,
+ total: total_count,
+ onChange: onPageChange
+ }}
/>

  1. 实现分页功能,支持分页页码和分页条数设置

// 改变分页和size重新查询
const onPageChange = (page,pageSize) => {
const params = {}
params.page = page
params.per_page = pageSize
dispatch(getArticles(params));
}
问题: 分页的参数和筛选的参数是分开的,分页的时候带不上筛选的条件(BUG)

10-内容管理-关联筛选分页参数

把筛选的参数和分页的参数进行关联完成联合查询
大致步骤:

  • 需要知道,怎么存储数据,不导致页面刷新。
  • 声明 ref 保存参数数据
  • 更改筛选参数的时候,分页默认变成第一页,分页的条数不变。
  • 进行分页查询的时候,筛选条件的参数需要保留。

具体内容:

  1. 明白不能使用 state 来存储筛选条件

// 这个来保存参数会导致页面重新渲染,这里没有第二个参数监听渲染完毕后,且查询参数没有在页面上渲染。
setState({})
// 在这里立即去调用dispatch是拿不到最新的参数,因为需要封setState()渲染组件后才能拿到最新状态。
const ref = useRef()
// 这个ref记录的数据改变不会引起组件渲染,且可以存储数据而且是立即生效,就是一个普通的数据
// 使用场景:在组件中要记录数据,且组件每次更新能不变,这些数据不需要去渲染的。

  1. 声明 ref 保存参数数据

import { useEffect, useRef } from “react”;
// 请求参数
const params = useRef({
page: 1,
per_page: 20,
channel_id: undefined,
status: undefined,
begin_pubdate: undefined,
end_pubdate: undefined,
});
useEffect(() => {
dispatch(getChannels());
+ dispatch(getArticles(params.current));
}, [dispatch]);

  1. 更改筛选参数的时候,分页默认变成第一页,分页的条数不变。

// 改变筛选条件查询
const onFinish = (values) => {
params.current.status = values.status;
params.current.channel_id = values.channel_id;
console.log(values);
if (values.dateArr) {
params.current.begin_pubdate = values.dateArr[0].format(
“YYYY-MM-DD HH:mm:ss”
);
params.current.end_pubdate = values.dateArr[1].format(
“YYYY-MM-DD HH:mm:ss”
);
} else {
params.current.begin_pubdate = undefined;
params.current.end_pubdate = undefined;
}
params.current.page = 1
dispatch(getArticles(params.current));
};

  1. 进行分页查询的时候,筛选条件的参数需要保留。

// 改变分页重新查询
const onPageChange = (page,pageSize) => {
params.current.page = page
params.current.per_page = pageSize
dispatch(getArticles(params.current));
}

11-内容管理-删除文章

完成文章的删除功能
大致步骤:

  • 绑定点击按钮,完成确认框弹出
  • 完成删除逻辑(dispatch—->action)

核心代码:

  1. 绑定点击按钮,完成确认框弹出 Article/index.jsx

{
title: “操作”,
key: “action”,
render: (text, record) => (

+ } />

),
},
// 删除
const delArticleFn = id => {
Modal.confirm({
title: ‘您是否确认删除该文章?’,
cancelText: ‘取消’,
okText: ‘确认’,
onOk: () => {
console.log(id)
},
})
}

  1. 完成删除逻辑(dispatch—->action)

actions/article.js
export const delArticle = id => {
return async dispatch => {
await request.delete(‘mp/articles/‘ + id)
}
}
Article/index.jsx
// 删除
const delArticleFn = id => {
Modal.confirm({
title: ‘您是否确认删除该文章?’,
cancelText: ‘取消’,
okText: ‘确认’,
onOk: async () => {
await dispatch(delArticle(id))
await dispatch(getArticles(params.current));
message.success(‘删除成功’)
},
})
}

12-内容管理-编辑跳转

完成点击按钮跳转到编辑文章页面
大致步骤:

  • 绑定事件使用history跳转

核心代码: