Ant Design 学习记录
遇到的问题:
- 点击列表中的一个字段 , 显示出一条指定id(其他筛选条件的)数据
解决这个问题之前,要先了解 Antd的 Table中的 Column 列描述数据对象,是 columns 中的一项,Column 使用相同的 API。 官网地址
从中我们可以知道 : render 生成复杂数据的渲染函数,参数分别为(当前行的值,当前行数据,行索引),@return 里面可以设置表格行/列合并 类型是函数 (text, record, index) => { }
点击后弹出以下列表
解决:
两种写法 :
- 直接给子组件传 props值, 然后子组件渲染this.props.item
- 给子组件传id值,然后子组件通过URL传给后台,后台筛选出满足条件的数据。
1 import React, {Component} from 'react'; 2 import { PageHeader, Table, Input, Card } from "antd"; 3 import HttpUtils from "../../utils/HttpUtils"; 4 import moment from "moment"; 5 import FilterForm from "../../components/Filter"; 6 import Team from './StatisticalTeam'; 7 import Share from './StatisticalShare'; 8 import User from './StatisticalUser'; 9 10 const {TextArea} = Input; 11 const select = [ 12 { 13 name: '时间', 14 type: 'date', 15 dataIndex: ['start_time', 'end_time'], 16 // wrap: 24 17 } 18 ] 19 20 export default class Hello extends Component { 21 constructor(props) { 22 super(props); 23 this.state = { 24 form: { 25 pers: 10, 26 page: 1 27 }, 28 loading:false, 29 values: {}, 30 dataSource: [], 31 value: '', 32 count: '' 33 }; 34 this.columns = [ 35 { 36 width:400, 37 title: '记录创造时间', 38 dataIndex: 'first_day', 39 key: 'first_day', 40 render: (props) => { 41 // return this.timestampToTime(props); 42 const time = this.timestampToTime(props); 43 return moment(time).format('YYYY-MM-DD hh:mm:ss') 44 } 45 }, 46 { 47 title: '分享', 48 dataIndex: 'share_award_real_amount', 49 key: 'share_award_real_amount', 50 render: (text, record) => { 51 return ( 52 <div 53 onClick={() => { 54 this.setState({ 55 visible_pwd: true, 56 user_id: record.id 57 }) 58 }} 59 60 style={{marginRight: 10, cursor: "pointer", color: '#40a9ff'}} 61 > 62 <Share item={record} loadUserList={()=> { 63 this._loadUserList() 64 }}/> 65 </div> 66 ) 67 } 68 }, { 69 title: '团队', 70 // dataIndex: 'share_award_real_amount', 71 // key: 'share_award_real_amount', 72 render: (text, record) => { 73 return ( 74 <div 75 onClick={() => { 76 this.setState({ 77 visible_pwd: true, 78 user_id: record.id 79 }) 80 }} 81 82 style={{marginRight: 10, cursor: "pointer", color: '#40a9ff'}} 83 > 84 <Team item={record} loadUserList={()=> { 85 this._loadUserList() 86 }}/> 87 </div> 88 ) 89 } 90 }, { 91 width: 140, 92 title: '用户', 93 render: (text, record) => { 94 return ( 95 <div 96 onClick={() => { 97 this.setState({ 98 visible_pwd: true, 99 user_id: record.id 100 }) 101 }} 102 103 style={{marginRight: 10, cursor: "pointer", color: '#40a9ff'}} 104 > 105 <User happy={record} item={record} loadUserList={()=> { 106 this._loadUsreList() 107 }}/> 108 </div> 109 ) 110 } 111 112 }, 113 ] 114 }; 115 116 componentDidMount() { 117 this.getUserList() 118 } 119 //处理六位小数 120 toDecimal=(x)=>{ 121 var f = parseFloat(x); 122 if (isNaN(f)) { 123 return; 124 } 125 f = Math.round(x*1000000)/1000000; 126 return f; 127 } 128 //处理时间戳 129 timestampToTime = (timestamp) => { 130 var date = new Date(timestamp * 1000);//时间戳为10位需*1000,时间戳为13位的话不需乘1000 131 var Y = date.getFullYear() + '-'; 132 var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-'; 133 var D = date.getDate() + ' '; 134 var h = date.getHours() + ':'; 135 var m = date.getMinutes() + ':'; 136 var s = date.getSeconds(); 137 return Y+M+D+h+m+s; 138 } 139 140 141 getUserList() { 142 this.setState({loading:true}) 143 HttpUtils.postForm('/api/auex/statistics/award/list', { 144 ...this.state.form, 145 order: "id desc", 146 ...this.state.values 147 }).then(res => { 148 this.setState({loading:false}) 149 if (res.status == 10000) { 150 this.setState({ 151 dataSource: res.data, 152 count: res.count 153 }) 154 } 155 console.log(res); 156 }).catch(err => { 157 this.setState({loading:false}) 158 window.$message.error('通讯失败') 159 }) 160 } 161 162 163 164 onSubmit = (value) => { 165 console.log(value) 166 this.setState({ 167 form: { 168 page: 1, 169 pers: this.state.form.pers, 170 171 }, 172 values: value 173 }, () => { 174 this.getUserList() 175 }) 176 } 177 onReset = () => { 178 this.setState({ 179 values: {} 180 }, () => { 181 this.getUserList(); 182 }) 183 } 184 185 getCurrent = (page) => { 186 console.log(page) 187 this.setState({ 188 form: { 189 page: page, 190 pers: this.state.form.pers 191 } 192 }, () => this.getUserList()) 193 } 194 changePers = (current, size) => { 195 this.setState({ 196 form: { 197 page: current, 198 pers: size 199 } 200 }, () => this.getUserList()) 201 } 202 203 render() { 204 return ( 205 <div> 206 <PageHeader title="用户列表" subTitle="查看用户信息" style={{marginBottom: 20}}/> 207 <FilterForm select={select} onSubmit={this.onSubmit} onReset={this.onReset} /> 208 <Card> 209 <Table 210 loading={this.state.loading} 211 dataSource={this.state.dataSource} 212 columns={this.columns } 213 title={() => `记录条数:${this.state.count}条`} 214 pagination={{ 215 showTotal: (total) => { 216 return <div style={{display: 'flex'}}> 217 <div style={{paddingLeft: 18}}>总共{total}条</div> 218 </div> 219 }, 220 showSizeChanger: true, 221 onShowSizeChange: this.changePers, 222 pageSizeOptions: ['10', '30', '50', '100'], 223 showQuickJumper: true, 224 current: this.state.form.page, 225 total: this.state.count, 226 onChange: this.getCurrent 227 }} 228 /> 229 </Card> 230 </div> 231 ) 232 }; 233 }
补充:
0.给子组件传值 : <Share happy={record} loadUserList={()=> {this._loadUserList()}}/> 子组件接收值
1.设置state中值的时候 不要直接赋值 , 要 使用规范写法
1 this.setState({
2 visible_father: true
3 })
2.子组件接到数后不能展示列表?
1 onClick={() => {
2 this.setState({
3 visible_father: true //修改列表展示为true
4 })
5 }}
3.dataSource中的数据this.props.item 是对象 所以要再外面加一个[] 使其变为数组
4.const {number, visible_father, data, loading,} = this.state; 解构赋值后 , 就不需要在this.state.xxx了
5.<Modal /> 有个visible属性,表示是否显示对话框。
占位
需要掌握的:
1.Form表单
-
Form.create 经过
Form.create
包装的组件将会自带this.props.form
属性,this.props.form
常用API : getFieldDecorator 用于和表单进行双向绑定,详见下方描述
class CustomizedForm extends React.Component {}
CustomizedForm = Form.create({})(CustomizedForm);
this.props.form.getFieldDecorator(要传的值, options)
经过 getFieldDecorator
包装的控件,表单控件会自动添加 value
(或 valuePropName
指定的其他属性) onChange
(或 trigger
指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
-
你不再需要也不应该用
onChange
来做同步,但还是可以继续监听onChange
等事件。 -
你不能用控件的
value
defaultValue
等属性来设置表单域的值,默认值可以用getFieldDecorator
里的initialValue
。 -
你不应该用
setState
,可以使用this.props.form.setFieldsValue
来动态改变表单值(可以设置默认值)。
-
validateFields 校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件 / resetFields 重置一组输入控件的值(为
initialValue
)与状态,如不传入参数,则重置所有组件
handleSubmit = (e) => { e.preventDefault(); this.props.form.validateFields((err, values) => { if (err) { return; } values.rate = values.rate / 100; window.$http.postForm('/api/', {...this.state.item,...values}).then(res => { if (res.status === 10000) { window.$message.success('提交成功!'); this.props.form.resetFields(); this.setState({visible: false}) this.getBonusList() } else { window.$message.error(res.message); } }).catch((err) => { window.$message.error('通讯失败'); }) }); }
- Form.Item 表单域
2. Affix 固钉 https://ant.design/components/affix-cn/#header
注意:Affix
内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 Affix
为绝对定位:
<Affix style={{ position: 'absolute', top: y, left: x }}>...</Affix>
3.Card 卡片 https://ant.design/components/card-cn/#header
4.父子传值 , 传方法
首先父组件调用子组件,写一个item = { recodr(columns表单所有数据) } 写一个 refreshList方法
然后再子组件调用 (使用this.props.XXX)调用父组件的XXX
调用属性 (这里又把父组件的值传给了子组件CustomForm) <CustomForm content={this.state.content} onSubmit={this.onSubmit} url={'/api/backend/distribution/time/category/save'} value={{ id: this.props.item.id }} values={this.props.item} space={86400} /> 调用方法 onSubmit = () => { this.setState({ visible: false }) this.props.refreshList() }
5.弹窗 Modal
6.文本域: const { textArea } = Input 去掉右下角调整大小按键 : CSS resize:none;
7.上传图片 Upload
8.修改状态
{ // width: 150, title: '操作', dataIndex: 'status', //verified key: 'status', render: (text, item) => ( <> {item.status != 1 ? null : <Switch checked={text === 1} onChange={() => { this.changeStatus(text, item.id); }} />} </> ) }
9.删除一行记录
{
title: '操作',
render: (text, item) => {
return (
<div>
<Button size={'small'} type={'primary'} onClick={() => {
this.props.history.push('/content/carousel/edit/' + item.id)
}}>
编辑
</Button>
<Popconfirm
title="确定要删除吗?"
okText="确定"
cancelText="取消"
onConfirm={() => {
window.$http.postForm('/api/web/carousel/delete', {id: item.id}).then(res => {
if (res.status === 10000) {
window.$message.success('删除成功');
this._loadNewsList()
} else if (res.status !== 18888) {
window.$message.error(res.message);
}
})
}}
>
<Button size={'small'} type={'danger'} style={{marginLeft: 20}} onClick={() => {
}}>删除</Button>
</Popconfirm>
</div>
);
}
}
10 徽标微
import {Badge} from '@ant-design/react-native'; <Badge dot> <Touchable style={{position: 'relative'}}> <Image source={message} /> </Touchable> </Badge>
import {Badge} from '@ant-design/react-native'; <Badge dot> <Touchable style={{position: 'relative'}}> <Image source={message} /> </Touchable> </Badge>
11. 从接口获取交易对 / 列表数组
getSymobls() {
HttpUtils.postForm('/api/teacher/trade/exchange/symbols', {}).then(res => {
if (res.status === 10000) {
let arr1 = new Set(res.data.map((item) => item.symbol))
let symbol = Array.from(arr1).map(item => ({id: item, name: item}));
select[2].option = symbol; //添加列表
this.setState({
select: this.state.select,
})
}
}).catch((err) => {
console.log(err);
})
}
12.如何看懂ant的文档
这里提供了2中格式化方式
第一种 告诉你了 value是Date值(日期),date是字符串 ,所以 写法如下
format={(value) => moment(value).format('YYYY.MM')}
13. 图片默认列表显示图片问题 (解决了,是valueProName的问题 应该要用item.dateIndex)
一定要在Upload标签的外面添加一个父标签, 不然filelist里的内容无法显示
14. 解决单个图片显示问题/多个图片显示问题/文件显示问题,保存原有图片/文件 消失问题 , 这里上传图片后 返回了一个图片地址/多个图片地址字符串,
上传组件
<Form.Item labelCol={{ span: item.labelCol }} wrapperCol={{ span: item.wrapperCol }} shouldUpdate key={item.dataIndex} label={item.name} name={item.dataIndex} valuePropName={item.dataIndex} getValueFromEvent={normFile} rules={[ { required: item.required, message: item.message, }, ]} dependencies={[item.dataIndex]} > <Upload fileList={fileList} className='uploadImg' listType='picture-card' headers={{ authorization: sessionStorage.getItem( 'token', ), }} accept='image/jpeg,image/jpg,image/png' action='/api/admin/vote/file/upload' onChange={(value) => { setVariety(true) // 有变化和没变化 传的值是不同的 console.log('让我康康',value.fileList); if (item.images) { let arr = []; value.fileList.map((item) => { let arr_item = Object.keys(item) .length > 0 && item.url?item.url : //因为默认给的是一个对象,url在对象里,但是图片上传以后,返回的url在对象的response里面 ,所以这里要判断 item.response && item.response.url; //默认上传的图片,会把url放在response里面 arr.push(arr_item); console.log( 'item', Object.keys(item) .length > 0 && item.response && item.response.url, ); }); console.log('新arr',arr); setFileList(value.fileList); setImages(JSON.stringify(arr)); } if (item.cover) { let cover = value.fileList.length > 0 && //封面图因为只有一张图,不会出现数据结构不一样的问题,所以不用处理 value.fileList[0] .response && value.fileList[0].response .url; setFileList(value.fileList); setCover(cover); } setChange(Math.random() + 1); }} onPreview={handlePreview} disabled={item.disabled} > {fileList.length >= (item.limit || 1) ? null : uploadButton} </Upload> </Form.Item>
打开编辑Modal进行的初始化处理
useEffect(() => { if (props.values) { console.log('所有的数据',props.values); form.setFieldsValue({ ...props.values });if (props.date) { form.setFieldsValue({ vote_start: moment(props.values.vote_start * 1000), }); form.setFieldsValue({ vote_end: moment(props.values.vote_end * 1000), }); if (props.values.vote_end === 0) { form.setFieldsValue({ vote_end: null }); } if (props.values.vote_start === 0) { form.setFieldsValue({ vote_start: null }); } } if(props.values.name === '封面图'){ setFileList([ { uid: '-1', //只有一张图,不用管uid name: 'xxx.png', status: 'done', type: 'image/png', url: props.values['value'], }, ]); } if(props.values.name === '轮播图' && props.values.value.length > 0 ){ //只长度大于0才会解析,防止报错 console.log('abc',); let arr = [] let images = JSON.parse(props.values.value); //这里是JSON字符串,所以要解析 images.map((item,index)=>{ arr.push({ uid:index, //uid必须不同,不然会点删除按钮,会把所有的图片都删除 name: 'xxx.png', status: 'done', type: 'image/png', url: item }) }) setFileList(arr); } } }, []);
提交处理
const onSubmit = (values) => { if (values.name === '封面图' ) { //没有更好的判断方法 if(variety){ //如果发生了变化,传自己修改的JSON字符串 values.value = cover; }else { //没有发生变化,就返回原来的 values.value = props.values.value; } } if (values.name === '轮播图' && images.length > 0) { if(variety){ values.value = images; }else { values.value = props.values.value; } } values = onCheckValues(values); setLoading(true); Http.postForm(props.url, { ...props.value, ...values, }) .then((res) => { setLoading(false); if (res.status === 10000) { message.success('提交成功'); props.onSubmit(); } else { message.error(res.message); } }) .catch((err) => { setLoading(false); message.error('通讯失败'); }); };
上传文件的组件代码
case 'upload': return ( <Form.Item shouldUpdate key={item.dataIndex} label={item.name} labelCol={{ span: item.labelCol }} wrapperCol={{ span: item.wrapperCol }} name={item.dataIndex} valuePropName={item.dataIndex} getValueFromEvent={normFile} rules={[ { required: item.required, message: item.message, }, ]} dependencies={[item.dataIndex]} > <Upload fileList={fileList2} action='/api/admin/vote/file/upload' headers={{ authorization: sessionStorage.getItem( 'token', ), }} onChange={(value) => { setVariety2(true); if (item.file) { let file = value.fileList.length > 0 && value.fileList[0] .response && value.fileList[0].response .url; setFile(file); } setFileList2(value.fileList); setChange(Math.random() + 1); }} disabled={item.disabled} > {fileList2.length >= 1 ? null : <Button> <UploadOutlined /> 点击上传 </Button>} </Upload> </Form.Item> );
压缩文件显示初始化
props.values.download_address.length > 0 && setFileList2([{ uid: '-1', name: `${props.values.company_name}.zip`, status: 'done', type: 'application/zip', url: props.values['download_address'] }])
效果图 :