遇到的問題:
- 點擊列表中的一個字段 , 顯示出一條指定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'] }])
效果圖 :