持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
文章编辑页面
PageHeader页头
传送门
引入
import { PageHeader, Button } from 'antd';
复制页头去掉描述
<PageHeader
ghost={false}
onBack={() => window.history.back()}
title="Title"
subTitle="This is a subtitle"
extra={[
<Button key="3">Operation</Button>,
<Button key="2">Operation</Button>,
<Button key="1" type="primary">
Primary
</Button>,
]}
>
</PageHeader>
修改按钮
extra={
<Button key="1" type="primary">
提交文章
</Button>}
设置时间和标题
引入moment
import moment from 'moment'
修改时间
subTitle={"当前日期:" + moment(new Date()).format("YYYY-MM-DD")}
修改标题
title="文章编辑"
- 实现效果
- 使用wangEditor
安装依赖
npm i wangeditor --save
引入对象E
import E from 'wangeditor'
创建对象实例放入div盒子
使用useEffect
import React,{useEffect} from 'react'
// 模拟componentDidMount
useEffect(()=>{
const editor = new E('#div1');
editor.create()
},[])
<div id="div1"></div>
- 富文本编辑器
改为外界声明editor
let editor = null;
书写editor的onChange函数
editor.config.onchange = (newHtml) => {
SVGTextContentElement(newHtml)
}
销毁editor
return () => {
editor.destroy()
}
注意使用useState创建content。
import React, { useEffect, useState } from 'react'
const [content, setContent] = useState('')
为文本编辑框设置边距
style={{ padding: '0 20px 20px', background: '#fff' }}
设置对话框
对话框传输门
引入Modal
import { Modal, Button } from 'antd';
设置button
<Button key="1" type="primary" onClick={showModal}>
提交文章
</Button>
添加方法和模块
const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => {
setIsModalVisible(true);
};
const handleOk = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
<Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
处理层级问题
在Modal设置zIndex={99999}
<Modal zIndex={99999} title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
修改标题
title="填写文章标题"
简化函数代码
onClick={() => setIsModalVisible(true)}
onCancel={() => setIsModalVisible(false)}
Form表单传送门
引入Form、Input
import {Form, Input, PageHeader, Button ,Modal} from 'antd';
//在Model下添加表单代码
<Form
name="basic"
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
autoComplete="off"
>
<Form.Item
label="标题"
name="title"
rules={[{ required: true, message: '请填写标题!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="副标题"
name="subTitle"
>
<Input />
</Form.Item>
</Form>
对话框获取表单的值
Form表单弹出层传送门
const [form] = Form.useForm();
Form表单上添加属性
form={form}
对话框点击了提交
// 对话框点击了提交
const handleOk = () => {
// setIsModalVisible(false); // 关闭对话框
form
.validateFields()
.then(values => {
form.resetFields();
onCreate(values);
})
.catch(info => {
console.log('Validate Failed:', info);
});
};
删除onCreate(values)、修改ok函数
// 对话框点击了提交
const handleOk = () => {
// setIsModalVisible(false); // 关闭对话框
form
.validateFields()
.then(values => {
form.resetFields();
})
.catch(() => {
return ;
});
};
- 实现效果
onOk添加onText、cancelText
okText="提交" cancelText="取消"
发送文章请求
- 添加token
let token = localStorage.getItem('cms-token')
if(token){
config.headers = {
'cms-token': token
}
}
- 书写api
// 添加文章
export const ArticleAddApi = (params) => request.post('/article/add', params)
引入api
import {ArticleAddApi } from '../request/api'
验证content是否取到
// setIsModalVisible(false); // 关闭对话框
form
.validateFields()
.then(values => {
// form.resetFields(); // reset重置
console.log('Received values of form: ', values);
let {title,subTitle} = values
console.log(content)
})
.catch(() => {
return ;
});
editor.config.onchange = (newHtml) => {
setContent(newHtml)
}
-发送请求
// 请求
ArticleAddApi({title,subTitle,content}).then(res => {
console.log(res)
})
实现效果
编辑id控制 实现页面跳转
在ListList中
引入useNavigate
import { useNavigate, } from 'react-router-dom'
const navigate = useNavigate()
在onClick中实现路由跳转
actions={[
<Button type='primary' onClick={()=>navigate('/edit/'+item.id)}>编辑</Button>,
<Button type='danger' onClick={()=>console.log(item.id)}>删除</Button>
]}
- 修改路由文件index
是添加!不是修改
<Route path='/edit' element={<Edit />}></Route>
<Route path='/edit/:id' element={<Edit />}></Route>
- 实现效果
- 实现只有路径带有id值是才会有返回箭头
引入useParams
import { useParams } from 'react-router-dom'
const params = useParams()
书写onBack
onBack={ params.id ? () => window.history.back() : null}
-实现效果
wangeditor 内容渲染
- 书写查看文章api
// 查看文章
export const ArticleSearchApi = (params) => request.get(`/article/${params.id}`)
引入查看文章api
import {ArticleAddApi,ArticleSearchApi } from '../request/api'
根据地址栏id做请求
// 根据地址栏id做请求
if(params.id) {
ArticleSearchApi({
id:params.id
}).then(res=>{
if(res.errCode === 0) {
let {title,subTitle} = res.data;
editor.txt.html(res.data.content) // 重新设置编辑器内容
}
})
}
- 实现效果(点击编辑之后会跳转到编辑页面,并且显示出文本内容)
- 设置title、subTitle
const [title, setTitle] = useState('')
const [subTitle, setSubTitle] = useState('')
setTitle(res.data.title)
setSubTitle(res.data.subTitle)
- 为表单添加初始值
在Form 标签中使用该属性
initialValues={{title:title,subTitle:subTitle}}
- 实现效果
修改更新文章
书写更新api
// 重新编辑文章
export const ArticleUpdateApi = (params) => request.put('/article/update', params)
调用api
// 地址栏有id代表现在想要更新一篇文章
if(params.id) {
ArticleUpdateApi({title,subTitle,content}).then(res => {
console.log(res)
})
}else {
// 添加文章的请求
ArticleAddApi({title,subTitle,content}).then(res => {
console.log(res)
})
}
- 实现效果
- 使用message提示修改成功并且跳转页面
// 地址栏有id代表现在想要更新一篇文章
if(params.id) {
ArticleUpdateApi({title,subTitle,content}).then(res => {
if(res.errCode === 0) {
message.success(res.message);
//跳转到list页面
navigate('/listlist')
}else{
message.error(res.message)
}
setIsModalVisible(false) // 关闭对话框
})
}else {
// 添加文章的请求
ArticleAddApi({title,subTitle,content}).then(res => {
console.log(res)
})
}
解决bug
点击编辑一篇文章之后,再次点击菜单栏,文章编辑。页面的路径更改但是文本框并没有清除。
解决方案:监听路由的变化
引入location
import { useParams, useNavigate, useLocation } from 'react-router-dom'
const location = useLocation()
- Aside监听路由
// 一般加个空数组就是为了模仿componentDidMounted
useEffect(()=>{
let path = location.pathname;
let key = path.split('/')[1];
setDefaultKey(key)
}, [location.pathname])
- 封装函数(使用message提示修改成功并且跳转页面)
删除文章
书写删除api
// 删除文章
export const ArticleDelApi = (params) => request.post('/article/remove', params)
在ListList中引入api
import { ArticleListApi ,ArticleDelApi} from '../request/api';
在button中修改点击事件
<Button type='danger' onClick={()=>delFn(item.id)}>删除</Button>
书写删除函数delFn
重新刷页面,要么重新请求这个列表的数据 window.reload 调用getList(1) 增加变量的检测
// 删除
const delFn = (id) => {
ArticleDelApi({id}).then(res=>{
if(res.errCode===0){
message.success(res.message)
// 重新刷页面,要么重新请求这个列表的数据 window.reload 调用getList(1) 增加变量的检测
setUpdate(update+1)
}else{
message.success(res.message)
}
})
}
监听刷新页面
const [update, setUpdate] = useState(1)
- 为Table添加编辑、删除功能
注意引入的内容和函数(太冗杂不写了,傲娇~)
用户资料表单布局
书写类名,设置样式
import "./less/Means.less"
import React from 'react'
export default function Means() {
return (
<div className='means'>Means</div>
)
}
.means{
background: #fff;
height: 100%;
padding: 20px;
box-sizing: border-box;
}
- 实现效果
- 引入form表单(设置宽度、修改按钮,修改宽高)
import React from 'react'
import { Form, Input,Button} from 'antd';
import "./less/Means.less"
export default function Means() {
return (
<div className='means'>
<Form
style={{width: '400px'}}
name="basic"
initialValues={{
remember: true,
}}
autoComplete="off"
>
<Form.Item
label="修改用户名"
name="username"
rules={[
{
required: true,
message: 'Please input your username!',
},
]}
>
<Input placeholder='请输入新用户名' />
</Form.Item>
<Form.Item
label="修 改 密 码"
name="password"
>
<Input.Password placeholder='请输入新密码' />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" style={{float: 'right'}}>提交</Button>
</Form.Item>
</Form>
</div>
)
}
获取用户请求
- 书写api
// 获取用户资料
export const GetUserDataApi = () => request.get('/info')
import {GetUserDataApi} from '../request/api'
使用useEffect、更新请求用户,设置初始值。
const [username1,setUsername1] = useState("");
const [password1,setPassword1] = useState("")
useEffect(() => {
GetUserDataApi().then(res => {
console.log(res)
if(res.errCode === 0) {
message.success(res.message);
setUsername1(res.data.username);
setPassword1(res.data.Password);
}
})
}, []);
- 实现效果
修改用户资料
- 书写api
// 修改用户资料
export const ChangeUserDataApi = (params) => request.put('/info', params)
- 引入
import {GetUserDataApi, ChangeUserDataApi} from '../request/api'
- 发送请求
// 表单提交的事件
const onFinish = (values) => {
// 如果表单的username有值,并且不等于初始化时拿到的username,同时密码非空
if(values.username && values.username!==sessionStorage.getItem('username') && values.password.trim() !== ""){
// 做表单的提交...
ChangeUserDataApi({
username: values.username,
password: values.password
}).then(res=>{
console.log(res)
// 当你修改成功的时候,不要忘了重新登录
})
}
}
当你修改成功的时候,不要忘了重新登录!
Upload引入
添加标题
<p>点击下方修改头像:</p>
Upload上传
传送门
upload组件中直接书写请求体
书写action接口
action="/api/upload"
上传前 beforeUpload
// 限制图片大小只能是200KB
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 / 1024 < 200;
if (!isLt2M) {
message.error('请上传小于200KB的图!');
}
return isJpgOrPng && isLt2M;
}
- 使用useState改写loading、imageUrl
import React, { useEffect, useState } from 'react'
const [loading, setLoading] = useState(false)
const [imageUrl, setImageUrl] = useState("")
- 添加handleChange,并且修改
// 点击了上传图片
const handleChange = info => {
if (info.file.status === 'uploading') {
setLoading(true);
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl => {
setLoading(false)
setImageUrl(imageUrl)
}
);
}
};
- 函数调用修改
引入base64函数
// 将图片路径改位base64
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
注意引入
import { Form, Input,Button, message,Upload} from 'antd';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
- 实现效果
Upload组件添加请求头
headers={{"cms-token": localStorage.getItem('cms-token')}}
- 存储图片名称
这里需要打印info.file找到上传图片的名称。
// 存储图片名称
localStorage.setItem('avatar', info.file.response.data.filePath)
更新Header组件
使用useState设置mykey
<Layout id='app'>
<Header key={mykey} />
<div className='container'>
<Aside />
<div className='container_box'>
<Bread/>
<div className="container_content">
<Outlet setMyKey={setMyKey} />
</div>
</div>
</div>
<footer>Respect | Copyright © 2022 Author 你单排吧</footer>
</Layout>
Means组件
注意记得函数接收产生props
// 点击了上传图片
const handleChange = info => {
if (info.file.status === 'uploading') {
setLoading(true);
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl => {
setLoading(false)
setImageUrl(imageUrl)
// 存储图片名称
localStorage.setItem('avatar', info.file.response.data.filePath)
// 触发Header组件更新
props.setMyKey(props.myKey+1)
}
);
}
};
添加强制刷新
window.location.reload()
使用react-redux
安装react-redux
yarn add react-redux