CRA是一个底层基于webpack快速创建React项目的脚手架工具
# 使用npx创建项目
npx create-react-app react-jike
# 进入到项
cd react-jike
# 启动项目
npm start
SASS
是一种预编译的 CSS,支持一些比较高级的语法,可以提高编写样式的效率,CRA接入scss非常简单只需要我们装一个sass工具
npm i sass -D
index.scss
npm install antd --save
npm i react-router-dom
pages/Layout/index.js
const Layout = () => {
return this is layout
}
export default Layout
pages/Login/index.js
const Login = () => {
return this is login
}
export default Login
router/index.js
import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Layout from '../pages/Layout'
const router = createBrowserRouter([
{
path: '/',
element: ,
},
{
path: '/login',
element: ,
},
])
export default router
index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.scss'
import router from './router'
import { RouterProvider } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root')).render(
)
craco
工具包craco.config.js
配置文件scripts 命令
npm i @craco/craco -D
const path = require('path')
module.exports = {
// webpack 配置
webpack: {
// 配置别名
alias: {
// 约定:使用 @ 表示 src 文件所在路径
'@': path.resolve(__dirname, 'src')
}
}
}
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
}
import { createBrowserRouter } from 'react-router-dom'
import Login from '@/pages/Login'
import Layout from '@/pages/Layout'
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
},
{
path: '/login',
element: <Login />,
},
])
export default router
实现步骤
jsconfig.json
配置文件代码实现
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}
:::warning
说明:VSCode会自动读取jsconfig.json
中的配置,让vscode知道@就是src目录
:::
git remote add origin http
git add .
git commit -m 'init'
git push
**实现步骤**
1. 在 `Login/index.js` 中创建登录页面基本结构
2. 在 Login 目录中创建 index.scss 文件,指定组件样式
3. 将 `logo.png` 和 `login.png` 拷贝到 assets 目录中
代码实现
pages/Login/index.js
import './index.scss'
import { Card, Form, Input, Button } from 'antd'
import logo from '@/assets/logo.png'
const Login = () => {
return (
{/* 登录表单 */}
)
}
export default Login
pages/Login/index.scss
.login {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
background: center/cover url('~@/assets/login.png');
.login-logo {
width: 200px;
height: 60px;
display: block;
margin: 0 auto 20px;
}
.login-container {
width: 440px;
height: 360px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 50px rgb(0 0 0 / 10%);
}
.login-checkbox-label {
color: #1890ff;
}
}
**实现步骤**
1. 为 Form 组件添加 `validateTrigger` 属性,指定校验触发时机的集合
2. **为 Form.Item 组件添加 name 属性**
3. 为 Form.Item 组件添加 `rules` 属性,用来添加表单校验规则对象
代码实现
page/Login/index.js
const Login = () => {
return (
)
}
**实现步骤**
1. 为 Form 组件添加 `onFinish` 属性,该事件会在点击登录按钮时触发
2. 创建 onFinish 函数,通过函数参数 values 拿到表单值
3. Form 组件添加 `initialValues` 属性,来初始化表单值
代码实现
pages/Login/index.js
// 点击登录按钮时触发 参数values即是表单输入数据
const onFinish = formValue => {
console.log(formValue)
}
业务背景: 前端需要和后端拉取接口数据,axios是使用最广的工具插件,针对于项目中的使用,我们需要做一些简单的封装
**实现步骤**
1. 安装 axios 到项目
2. 创建 utils/request.js 文件
3. 创建 axios 实例,配置 `baseURL,请求拦截器,响应拦截器`
4. 在 utils/index.js 中,统一导出request
npm i axios
import axios from 'axios'
const http = axios.create({
baseURL: 'http://geek.itheima.net/v1_0',
timeout: 5000
})
// 添加请求拦截器
http.interceptors.request.use((config)=> {
return config
}, (error)=> {
return Promise.reject(error)
})
// 添加响应拦截器
http.interceptors.response.use((response)=> {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response.data
}, (error)=> {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error)
})
export { http }
import { request } from './request'
export { request }
npm i react-redux @reduxjs/toolkit //安装Redux相关工具包
import { createSlice } from '@reduxjs/toolkit'
import { http } from '@/utils'
const userStore = createSlice({
name: 'user',
// 数据状态
initialState: {
token:''
},
// 同步修改方法
reducers: {
setUserInfo (state, action) {
state.userInfo = action.payload
}
}
})
// 解构出actionCreater
const { setUserInfo } = userStore.actions
// 获取reducer函数
const userReducer = userStore.reducer
// 异步方法封装
const fetchLogin = (loginForm) => {
return async (dispatch) => {
const res = await http.post('/authorizations', loginForm)
dispatch(setUserInfo(res.data.token))
}
}
export { fetchLogin }
export default userReducer
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './modules/user'
export default configureStore({
reducer: {
// 注册子模块
user: userReducer
}
})
业务逻辑:
- 跳转到首页
- 提示用户登录成功
import { message } from 'antd'
import useStore from '@/store'
import { fetchLogin } from '@/store/modules/user'
import { useDispatch } from 'react-redux'
const Login = () => {
const dispatch = useDispatch()
const navigate = useNavigate()
const onFinish = async formValue => {
await dispatch(fetchLogin(formValue))
navigate('/')
message.success('登录成功')
}
return (
)
}
export default Login
业务背景: Token数据具有一定的时效时间,通常在几个小时,有效时间内无需重新获取,而基于Redux的存储方式又是基于内存的,刷新就会丢失,为了保持持久化,我们需要单独做处理
// 封装存取方法
const TOKENKEY = 'token_key'
function setToken (token) {
return localStorage.setItem(TOKENKEY, token)
}
function getToken () {
return localStorage.getItem(TOKENKEY)
}
function clearToken () {
return localStorage.removeItem(TOKENKEY)
}
export {
setToken,
getToken,
clearToken
}
import { createSlice } from '@reduxjs/toolkit'
import { setToken as _setToken, getToken, removeToken } from '@/utils'
import { loginAPI, getProfileAPI } from '@/apis/user'
const userStore = createSlice({
name: "user",
// 数据状态
initialState: {
token: getToken() || '',
userInfo: {}
},
// 同步修改方法
reducers: {
setToken (state, action) {
state.token = action.payload
_setToken(action.payload)
},
setUserInfo (state, action) {
state.userInfo = action.payload
},
clearUserInfo (state) {
state.token = ''
state.userInfo = {}
removeToken()
}
}
})
// 解构出actionCreater
const { setToken, setUserInfo, clearUserInfo } = userStore.actions
// 获取reducer函数
const userReducer = userStore.reducer
// 登录获取token异步方法封装
const fetchLogin = (loginForm) => {
return async (dispatch) => {
const res = await loginAPI(loginForm)
dispatch(setToken(res.data.token))
}
}
// 获取个人用户信息异步方法
const fetchUserInfo = () => {
return async (dispatch) => {
const res = await getProfileAPI()
dispatch(setUserInfo(res.data))
}
}
export { fetchLogin, fetchUserInfo, clearUserInfo }
export default userReducer
刷新浏览器,通过Redux调试工具查看token数据
业务背景: Token作为用户的数据标识,在接口层面起到了接口权限控制的作用,也就是说后端有很多接口都需要通过查看当前请求头信息中是否含有token数据,来决定是否正常返回数据
拼接方式:config.headers.Authorization =
Bearer ${token}}
utils/request.js
// 添加请求拦截器
request.interceptors.request.use(config => {
// if not login add token
const token = getToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 添加响应拦截器
// 在响应返回到客户端之前 做拦截 重点处理返回的数据
request.interceptors.response.use((response) => {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response.data
}, (error) => {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
// 监控401 token失效
console.dir(error)
if (error.response.status === 401) {
removeToken()
router.navigate('/login')
window.location.reload()
}
return Promise.reject(error)
})
export { request }
业务背景:封装
AuthRoute
路由鉴权高阶组件,实现未登录拦截,并跳转到登录页面
实现思路:判断本地是否有token,如果有,就返回子组件,否则就重定向到登录Login
**实现步骤**
1. 在 components 目录中,创建 `AuthRoute/index.jsx` 文件
2. 登录时,直接渲染相应页面组件
3. 未登录时,重定向到登录页面
4. 将需要鉴权的页面路由配置,替换为 AuthRoute 组件渲染
代码实现
components/AuthRoute/index.jsx
import { getToken } from '@/utils'
import { Navigate } from 'react-router-dom'
const AuthRoute = ({ children }) => {
const isToken = getToken()
if (isToken) {
return <>{children}>
} else {
return
}
}
export default AuthRoute
src/router/index.jsx
import { createBrowserRouter } from 'react-router-dom'
import Login from '@/pages/Login'
import Layout from '@/pages/Layout'
import AuthRoute from '@/components/Auth'
const router = createBrowserRouter([
{
path: '/',
element: ,
},
{
path: '/login',
element: ,
},
])
export default router
实现步骤
antd/Layout
布局组件文档,找到示例:顶部-侧边布局-通栏代码实现
pages/Layout/index.js
import { Layout, Menu, Popconfirm } from 'antd'
import {
HomeOutlined,
DiffOutlined,
EditOutlined,
LogoutOutlined,
} from '@ant-design/icons'
import './index.scss'
const { Header, Sider } = Layout
const items = [
{
label: '首页',
key: '1',
icon: ,
},
{
label: '文章管理',
key: '2',
icon: ,
},
{
label: '创建文章',
key: '3',
icon: ,
},
]
const GeekLayout = () => {
return (
柴柴老师
退出
内容
)
}
export default GeekLayout
pages/Layout/index.scss
.ant-layout {
height: 100%;
}
.header {
padding: 0;
}
.logo {
width: 200px;
height: 60px;
background: url('~@/assets/logo.png') no-repeat center / 160px auto;
}
.layout-content {
overflow-y: auto;
}
.user-info {
position: absolute;
right: 0;
top: 0;
padding-right: 20px;
color: #fff;
.user-name {
margin-right: 20px;
}
.user-logout {
display: inline-block;
cursor: pointer;
}
}
.ant-layout-header {
padding: 0 !important;
}
npm install normalize.css
html,
body {
margin: 0;
height: 100%;
}
#root {
height: 100%;
}
使用步骤
router/index.js
中配置嵌套子路由,在Layout
中配置二级路由出口代码实现
pages/Home/index.js
const Home = () => {
return Home
}
export default Home
pages/Article/index.js
const Article = () => {
return Article
}
export default Article
pages/Publish/index.js
const Publish = () => {
return Publish
}
export default Publish
router/index.js
import { createBrowserRouter } from 'react-router-dom'
import Login from '@/pages/Login'
import Layout from '@/pages/Layout'
import Publish from '@/pages/Publish'
import Article from '@/pages/Article'
import Home from '@/pages/Home'
import { AuthRoute } from '@/components/Auth'
const router = createBrowserRouter([
{
path: '/',
element: (
),
children: [
{
index: true,
element: ,
},
{
path: 'article',
element: ,
},
{
path: 'publish',
element: ,
},
],
},
{
path: '/login',
element: ,
},
])
export default router
配置二级路由出口
import { Outlet, useNavigate } from 'react-router-dom'
const items = [
{
label: '首页',
key: '/',
icon: ,
},
{
label: '文章管理',
key: '/article',
icon: ,
},
{
label: '创建文章',
key: '/publish',
icon: ,
},
]
const GeekLayout = () => {
const navigate = useNavigate()
const menuClick = (route) => {
navigate(route.key)
}
return (
)
}
export default GeekLayout
const GeekLayout = () => {
// 省略部分代码
const location = useLocation()
const selectedKey = location.pathname
return (
{name}
退出
)
}
实现步骤
代码实现
store/userStore.js
import { createSlice } from '@reduxjs/toolkit'
import { http } from '@/utils/request'
import { getToken, setToken } from '@/utils'
const userStore = createSlice({
name: 'user',
// 数据
initialState: {
token: getToken() || '',
userInfo: {}
},
// 同步修改方法
reducers: {
setUserToken (state, action) {
state.token = action.payload
// 存入本地
setToken(state.token)
},
setUserInfo (state, action) {
state.userInfo = action.payload
}
}
})
// 解构出actionCreater
const { setUserToken, setUserInfo } = userStore.actions
// 获取reducer函数
const userReducer = userStore.reducer
const fetchLogin = (loginForm) => {
return async (dispatch) => {
const res = await http.post('/authorizations', loginForm)
dispatch(setUserToken(res.data.token))
}
}
const fetchUserInfo = () => {
return async (dispatch) => {
const res = await http.get('/user/profile')
dispatch(setUserInfo(res.data))
}
}
export { fetchLogin, fetchUserInfo }
export default userReducer
pages/Layout/index.js
// 省略部分代码
import { fetchUserInfo } from '@/store/modules/user'
import { useDispatch, useSelector } from 'react-redux'
const GeekLayout = () => {
const dispatch = useDispatch()
const name = useSelector(state => state.user.userInfo.name)
useEffect(() => {
dispatch(fetchUserInfo())
}, [dispatch])
return (
{name}
退出
)
}
export default GeekLayout
实现步骤
store/userStore.js
中新增退出登录的action函数,在其中删除token代码实现
store/modules/user.js
import { createSlice } from '@reduxjs/toolkit'
import { http } from '@/utils/request'
import { clearToken, getToken, setToken } from '@/utils'
const userStore = createSlice({
name: 'user',
// 数据
initialState: {
token: getToken() || '',
userInfo: {}
},
// 同步修改方法
reducers: {
setUserToken (state, action) {
state.token = action.payload
// 存入本地
setToken(state.token)
},
setUserInfo (state, action) {
state.userInfo = action.payload
},
clearUserInfo (state) {
state.token = ''
state.userInfo = {}
clearToken()
}
}
})
// 解构出actionCreater
const { setUserToken, setUserInfo, clearUserInfo } = userStore.actions
// 获取reducer函数
const userReducer = userStore.reducer
export { fetchLogin, fetchUserInfo, clearUserInfo }
export default userReducer
pages/Layout/index.js
const GeekLayout = () => {
// 退出登录
const loginOut = () => {
dispatch(clearUserInfo())
navigator('/login')
}
return (
{name}
退出
)
}
业务背景:如果用户一段时间不做任何操作,到时之后应该清除所有过期用户信息跳回到登录
http.interceptors.response.use((response) => {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response.data
}, (error) => {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
console.dir(error)
if (error.response.status === 401) {
clearToken()
router.navigate('/login')
window.location.reload()
}
return Promise.reject(error)
})
图表基础Demo实现
图表类业务渲染,我们可以通过下面的顺序来实现
- 跑通基础DEMO
- 按照实际业务需求进行修改
安装echarts
npm i echarts
实现基础Demo
import { useEffect, useRef } from 'react'
import * as echarts from 'echarts'
const Home = () => {
const chartRef = useRef(null)
useEffect(() => {
// 1. 生成实例
const myChart = echarts.init(chartRef.current)
// 2. 准备图表参数
const option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar'
}
]
}
// 3. 渲染参数
myChart.setOption(option)
}, [])
return (
)
}
export default Home
基础抽象
import { useRef, useEffect } from 'react'
import * as echarts from 'echarts'
const BarChart = () => {
const chartRef = useRef(null)
useEffect(() => {
// 1. 生成实例
const myChart = echarts.init(chartRef.current)
// 2. 准备图表参数
const option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar'
}
]
}
// 3. 渲染参数
myChart.setOption(option)
}, [])
return
}
export { BarChart }
抽象可变参数
import { useRef, useEffect } from 'react'
import * as echarts from 'echarts'
const BarChart = ({ xData, sData, style = { width: '400px', height: '300px' } }) => {
const chartRef = useRef(null)
useEffect(() => {
// 1. 生成实例
const myChart = echarts.init(chartRef.current)
// 2. 准备图表参数
const option = {
xAxis: {
type: 'category',
data: xData
},
yAxis: {
type: 'value'
},
series: [
{
data: sData,
type: 'bar'
}
]
}
// 3. 渲染参数
myChart.setOption(option)
}, [sData, xData])
return
}
export { BarChart }
import { BarChart } from './BarChart'
const Home = () => {
return (
)
}
export default Home
// 用户相关的所有请求
import { request } from "@/utils"
// 1. 登录请求
export function loginAPI (formData) {
return request({
url: '/authorizations',
method: 'POST',
data: formData
})
}
// 2. 获取用户信息
export function getProfileAPI () {
return request({
url: '/user/profile',
method: 'GET'
})
}
创建基础结构
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 (
首页 },
{ title: '发布文章' },
]}
/>
}
>
)
}
export default Publish
pages/Publish/index.scss
.publish {
position: relative;
}
.ant-upload-list {
.ant-upload-list-picture-card-container,
.ant-upload-select {
width: 146px;
height: 146px;
}
}
**实现步骤**
1. 安装富文本编辑器
2. 导入富文本编辑器组件以及样式文件
3. 渲染富文本编辑器组件
4. 调整富文本编辑器的样式
代码落地
1-安装 react-quill
npm i [email protected]
2-导入资源渲染组件
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'
const Publish = () => {
return (
// ...
)
}
.publish-quill {
.ql-editor {
min-height: 300px;
}
}
实现步骤
代码实现
import { http } from '@/utils'
// 频道列表
const [channels, setChannels] = useState([])
// 调用接口
useEffect(() => {
async function fetchChannels() {
const res = await http.get('/channels')
setChannels(res.data.channels)
}
fetchChannels()
}, [])
// 模板渲染
return (
)
// 发布文章
const onFinish = async (formValue) => {
const { channel_id, content, title } = formValue
const params = {
channel_id,
content,
title,
type: 1,
cover: {
type: 1,
images: []
}
}
await http.post('/mp/articles?draft=false', params)
message.success('发布文章成功')
}
单图
三图
无图
实现步骤
action 属性
,配置封面图片上传接口地址name属性
, 接口要求的字段名onChange 属性
,在事件中拿到当前图片数据,并存储到React状态中代码实现
import { useState } from 'react'
const Publish = () => {
// 上传图片
const [imageList, setImageList] = useState([])
const onUploadChange = (info) => {
setImageList(info.fileList)
}
return (
单图
三图
无图
)
}
**实现步骤**
1. 点击单选框时拿到当前的类型value
2. 根据value控制上传组件的显示(大于零时才显示)
const Publish = ()=>{
// 控制图片Type
const [imageType, setImageType] = useState(0)
const onTypeChange = (e) => {
console.log(e)
setImageType(e.target.value)
}
return (
单图
三图
无图
{imageType > 0 &&
}
)
}
实现步骤
{imageType > 0 &&
1}
>
}
业务描述
如果当前为三图模式,已经完成了上传,选择单图只显示一张,再切换到三图继续显示三张,该如何实现?
实现思路
在上传完毕之后通过ref存储所有图片,需要几张就显示几张,其实也就是把ref当仓库,用多少拿多少
实现步骤
代码实现
const Publish = () => {
// 上传图片
const cacheImageList = useRef([])
const [imageList, setImageList] = useState([])
const onUploadChange = (info) => {
setImageList(info.fileList)
cacheImageList.current = info.fileList
}
// 控制图片Type
const [imageType, setImageType] = useState(0)
const onRadioChange = (e) => {
const type = e.target.value
setImageType(type)
if (type === 1) {
// 单图,截取第一张展示
const imgList = cacheImageList.current[0] ? [cacheImageList.current[0]] : []
setImageList(imgList)
} else if (type === 3) {
// 三图,取所有图片展示
setImageList(cacheImageList.current)
}
}
return (
{imageType > 0 &&
1}
fileList={imageList}
>
}
)
}
注意:需要给Upload组件添加fileList属性,达成受控的目的
校验图片类型和数量是否吻合
// 发布文章
const onFinish = async (formValue) => {
if (imageType !== imageList.length) return message.warning('图片类型和数量不一致')
const { channel_id, content, title } = formValue
const params = {
channel_id,
content,
title,
type: imageType,
cover: {
type: imageType,
images: imageList.map(item => item.response.data.url)
}
}
await http.post('/mp/articles?draft=false', params)
message.success('发布文章成功')
}
- 如何让RangePicker日期范围选择框选择中文
- Select组件配合Form.Item使用时,如何配置默认选中项
代码实现
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'
const { Option } = Select
const { RangePicker } = DatePicker
const Article = () => {
return (
首页 },
{ title: '文章列表' },
]} />
}
style={{ marginBottom: 20 }}
>
全部
草稿
审核通过
{/* 传入locale属性 控制中文显示*/}
)
}
export default Article
代码实现
// 导入资源
import { Table, Tag, Space } from 'antd'
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
import img404 from '@/assets/error.png'
const Article = () => {
// 准备列数据
const columns = [
{
title: '封面',
dataIndex: 'cover',
width: 120,
render: cover => {
return
}
},
{
title: '标题',
dataIndex: 'title',
width: 220
},
{
title: '状态',
dataIndex: 'status',
render: data => 审核通过
},
{
title: '发布时间',
dataIndex: 'pubdate'
},
{
title: '阅读数',
dataIndex: 'read_count'
},
{
title: '评论数',
dataIndex: 'comment_count'
},
{
title: '点赞数',
dataIndex: 'like_count'
},
{
title: '操作',
render: data => {
return (
} />
}
/>
)
}
}
]
// 准备表格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 (
{/* */}
)
}
实现步骤
代码实现
pages/Article/index.js
const Article = ()=>{
// 获取频道列表
const [channels, setChannels] = useState([])
useEffect(() => {
async function fetchChannels() {
const res = await http.get('/channels')
setChannels(res.data.channels)
}
fetchChannels()
}, [])
// 渲染模板
return (
)
}
// 封装获取频道列表的逻辑
import { useState, useEffect } from 'react'
import { getChannelAPI } from '@/apis/article'
function useChannel () {
// 1. 获取频道列表所有的逻辑
// 获取频道列表
const [channelList, setChannelList] = useState([])
useEffect(() => {
// 1. 封装函数 在函数体内调用接口
const getChannelList = async () => {
const res = await getChannelAPI()
setChannelList(res.data.channels)
}
// 2. 调用函数
getChannelList()
}, [])
// 2. 把组件中要用到的数据return出去
return {
channelList
}
}
export { useChannel }
pages/Article/index.js
import { useChannel } from '@/hooks/useChannel'
const { channelList } = useChannel()
<Form.Item label="频道" name="channel_id">
<Select placeholder="请选择文章频道" style={{ width: 120 }}>
{channelList.map(item => <Option key={item.id} value={item.id}>{item.name}</Option>)}</Select>
</Form.Item>
实现步骤
代码实现
const Article = ()=>{
// 省略部分代码...
// 文章列表数据管理
const [article, setArticleList] = useState({
list: [],
count: 0
})
const [params, setParams] = useState({
page: 1,
per_page: 4,
begin_pubdate: null,
end_pubdate: null,
status: null,
channel_id: null
})
useEffect(() => {
async function fetchArticleList () {
const res = await http.get('/mp/articles', { params })
const { results, total_count } = res.data
setArticleList({
list: results,
count: total_count
})
}
fetchArticleList()
}, [params])
// 模板渲染
return (
)
}
实现步骤
onFinish
属性监听表单提交事件,获取参数params
参数并重新使用新参数重新请求数据代码实现
// 获取文章列表
const [list, setList] = useState([])
const [count, setCount] = useState(0)
async function getList (reqData = {}) {
const res = await getArticleListAPI(reqData)
setList(res.data.results)
setCount(res.data.total_count)
}
useEffect(() => {
getList()
}, [])
// 筛选文章列表
const onFinish = async (formValue) => {
console.log(formValue)
// 1. 准备参数
const { channel_id, date, status } = formValue
const reqData = {
status,
channel_id,
begin_pubdate: date[0].format('YYYY-MM-DD'),
end_pubdate: date[1].format('YYYY-MM-DD'),
}
// 2. 使用参数获取新的列表
getList(reqData)
}
实现步骤
代码实现
const pageChange = (page) => {
// 拿到当前页参数 修改params 引起接口更新
setParams({
...params,
page
})
}
return (
)
实现步骤
代码实现
// 删除回调
const delArticle = async (data) => {
await http.delete(`/mp/articles/${data.id}`)
// 更新列表
setParams({
page: 1,
per_page: 10
})
}
const columns = [
// ...
{
title: '操作',
render: data => {
return (
} />
delArticle(data)}
okText="确认"
cancelText="取消"
>
}
/>
)
}
]
代码实现
const columns = [
// ...
{
title: '操作',
render: data => (
}
onClick={() => navagite(`/publish?id=${data.id}`)} />
/>
)
}
]