/src/App/Admin/Headercss
布局 import {Row, Col} from "antd"html
<div className="header_box">react
<Row className="header_top">git
<span>欢迎</span>github
<a>退出</a>web
</Row>ajax
<Row className="header_bottom">json
<Col span={4}></Col>跨域
<Col span={20}>数组
<span className="weather_time">时间</span>
<span className="weather_img">图片</span>
<span className="weather_detail">晴天</span>
</Col>
</Row>
</div>
sysTime = Date.now() / DidMount 中计时器 时间动态显示
5
import jsonp from jsonp;
MyTools.weather(city){
return new Promise((resolve, reject)=>{
0
})
}
antd 界面 ---- Button、Icon、Card、Table(包含分页)
parentId 一级分类的 parentId 为 0
_id 自动生成
name 新分类的名字
__v 自动生成
注意其第二参数是一个回调函数,在状态更新完成之后当即执行
若是不指定 dataIndex,则第一个参数为整个对象
若是指定了 dataIndex,则第二个参数为整个对象,第一个参数为指定的数据
5
优化: 保存每次请求的页码即数据
关键: 数据结构;通常分页/搜索分页
能够在 "新页面" 获取到传递的数据
传递的数据 = this.props.location.state || {} // 优化: 保证 数据 始终是一个存在的对象
RichTextEditor.js
import React, { Component } from 'react'; import { EditorState, convertToRaw } from 'draft-js'; import { Editor } from 'react-draft-wysiwyg'; import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'; import draftToHtml from 'draftjs-to-html'; import htmlToDraft from 'html-to-draftjs'; // import {Card, Button, Icon, Table, message, Modal, Form, Input, Select} from "antd"; import "./css/RichTextEditor.css"; /* 富文本编辑器组件 */ export default class RichTextEditor extends Component { constructor(props){ super(props); this.state = { editorState: EditorState.createEmpty() } } onEditorStateChange = (editorState) => { this.setState({ editorState, }); }; render() { const { editorState } = this.state; return ( <div> <Editor editorState={editorState} wrapperClassName="demo-wrapper" /* 包裹区类名 */ editorClassName="demo-editor" /* 编辑区类名 */ onEditorStateChange={this.onEditorStateChange} /> {/*<textarea // draft2html */} {/*disabled*/} {/*value={draftToHtml(convertToRaw(editorState.getCurrentContent()))}*/} {/*/>*/} </div> ); } }
.product_edit .rdw-dropdown-selectedtext{ min-width: 54px; font-size: 16px; font-weight: 700; letter-spacing: 0; } .rdw-editor-toolbar { margin-bottom: 0; } .demo-editor { height: 120px; padding: 4px; font-size: 14px; line-height: 1.2em; letter-spacing: 0; background-color: #74878f; }
使用:(组件对象,就是标签对象,意味着可使用 ref 去关联获取组件中的属性和方法。)
getRichTextEditor = ()=>{ const {editorState} = this.state;
return draftToHtml(convertToRaw(editorState.getCurrentContent()); };
<div className="rich_text_editor"> <div className="product_detail_edit">商品详情:</div> <div><RichTextEditor ref="RichTextEditor" detail={product.detail}/></div> </div>
constructor(props){ super(props); this.state = { editorState: EditorState.createEmpty() // 默认建立一个空的文本内容 } }
componentWillMount(){ const detail = this.props.detail; if(detail){ const blocksFromHtml = htmlToDraft(detail); // 将传过来的 detail 转换 const { contentBlocks, entityMap } = blocksFromHtml; const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap); const editorState = EditorState.createWithContent(contentState); this.setState({ editorState // 更新界面相关内容 }) } }
import React, { Component } from 'react'; import { EditorState, convertToRaw, ContentState} from 'draft-js'; import { Editor } from 'react-draft-wysiwyg'; import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'; import draftToHtml from 'draftjs-to-html'; import htmlToDraft from 'html-to-draftjs'; // 将已有的 html 标签内容,生成一个 draft 内容 import "./css/RichTextEditor.css"; /* 富文本编辑器组件 */ export default class RichTextEditor extends Component { constructor(props){ super(props); this.state = { editorState: EditorState.createEmpty() // 默认建立一个空的文本内容 } } onEditorStateChange = (editorState) => { this.setState({ editorState, }); }; getRichTextEditor = ()=>{ // 让父组件调 const {editorState} = this.state; return draftToHtml(convertToRaw(editorState.getCurrentContent())) }; componentWillMount(){ const detail = this.props.detail; if(detail){ const blocksFromHtml = htmlToDraft(detail); // 将传过来的 detail 转换 const { contentBlocks, entityMap } = blocksFromHtml; const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap); const editorState = EditorState.createWithContent(contentState); this.setState({ editorState // 更新界面相关内容 }) } } render() { const { editorState } = this.state; return ( <div> <Editor editorState={editorState} /* 初始显示文本的内容 */ wrapperClassName="demo-wrapper" /* 包裹区类名 */ editorClassName="demo-editor" /* 编辑区类名 */ onEditorStateChange={this.onEditorStateChange} /* 监听,实时获取最新的输入内容 */ /> {/*<textarea // draft2html */} {/*disabled*/} {/*value={draftToHtml(convertToRaw(editorState.getCurrentContent()))}*/} {/*/>*/} </div> ); } }
由于标签对象就是组件对象,因此 ref 就能够获得组件对象
意味着,父组件调用子组件的方法,能够 this.refs.ref关联的名字.方法
5
返回一个 data 包含了 name 和 服务器资源路径url
componentWillMount(){ const imgs = this.props.imgs; if(imgs && imgs.length>0){ const fileList = imgs.map((img, index)=>({ uid: -index, name: img, url: "http://localhost:5000/upload/"+img, status: "done", })); this.setState({ fileList }) } } // 此时,能够看到已有的图片了
5
难点: 当用户 上传图片/删除图片 后,不进行保存,而是返回时,上传使得后台多了冗余数据,删除使得数据的丢失
解决:
1. 点击删除时,不是真的删除,而是保存删除的图片的名字 到数组 deleteNames 中
---- 当点击 提交保存 时,再将以前保存的 deleteNames 发送请求,发送 ajax 请求真正地删除
2. 点击上传时,真的上传了,同时保存下上传的图片的名字 到数组 fackNames 中
---- 当点击 返回 ← 时, 发送 ajax 请求 删除 以前保存的 fackNames
难点: 添加一条角色到后台,而后如何不发送获取列表的请求,来刷新页面
解决: 经过 name 添加到角色到, 后台返回一个角色的相关信息,能够将这个 role 角色对象,存入 roles 状态中
如此的问题在于,优化: 是 Component 始终在 setState 后 render
仍是用 PureComponent + [...this.state.状态]去 浅拷贝 生成新状态来 激活 render
5
5
5
5
5
5
5