到目前爲止,首頁、文章詳情頁、動態頁、話題頁及小冊頁面內容完成了,看一下效果圖哈,數據不全,見諒哈!
vue
該頁面從佈局來講分左右兩部分,左邊有分爲輸入框和已發表內容部分。react
我這裏採用了ant-design中的input做爲輸入框,而掘金是採用了可編輯的div來實現輸入內容,經過設置contenteditable="true"實現,感興趣的小夥伴可查閱相關資料。
發佈按鈕的disabled狀態將根據輸入框是否有值來決定。功能相似todolist添加功能,可參考文章詳情頁面的發佈評論功能。git
<div style={{background:'#fff',padding:'15px',position:'relative'}}> <TextArea placeholder="告訴你個小祕密,發沸點時添加話題會被更多小夥伴看見呦~" style={{paddingBottom:'30px'}} autosize={{ minRows: 3}} onChange={this.handelInputChange.bind(this)}/> {this.state.selectedTopic!==''?<span style={{position:'absolute',bottom:'60px',left:'28px',padding:'0 12px',border:'1px solid #007fff',userSelect:'none',height:'22px',borderRadius:'14px',color:'#007fff'}}>{this.state.selectedTopic}</span>:''} <div style={{marginTop:'5px',display:'flex',justifyContent:'space-between'}}> <ul style={sy1}> <li style={sy2} onClick={this.handleBtn.bind(this,'exp')}> <img alt='' src='//b-gold-cdn.xitu.io/v3/static/img/emoji.5594dbb.svg' />表情 </li> <li style={sy2} onClick={this.handleBtn.bind(this,'pic')}> <img alt='' src='//b-gold-cdn.xitu.io/v3/static/img/active_file.d265f4e.svg' />圖片 </li> <li style={sy2} onClick={this.handleBtn.bind(this,'link')}> <img alt='' src='//b-gold-cdn.xitu.io/v3/static/img/active_link.b1a6832.svg' />連接 </li> <Popover placement="bottom" content={<TopicList handleTopic={this.handleTopic.bind(this)} />} trigger="click"> <li style={sy2} onClick={this.handleBtn.bind(this,'topic')}> <img alt='' src='//b-gold-cdn.xitu.io/v3/static/img/topic.6a87bb7.svg' />話題 </li> </Popover> </ul> <Button type="primary" disabled={this.state.disabledBtn} onClick={this.handlePressEnter.bind(this)}>發佈</Button> </div> </div>
輸入框內容發生變化事件github
handelInputChange=(e)=>{ if(e.target.value.trim()!==''){ this.setState({ disabledBtn:false, inputValue:e.target.value }) }else{ this.setState({ disabledBtn:true }) } }
發佈按鈕事件segmentfault
handlePressEnter=()=>{ if(this.state.inputValue!==''){ console.log('發佈沸點') }else{ message.error('請輸入沸點內容'); } }
如圖所示,一個搜索框加一個話題列表。
dom結構以下:數組
<div style={{background:'#fff'}}> <Search placeholder="搜索話題" onSearch={value => this.handleSearch(value)} style={{ width: '100%' }} /> <List itemLayout="horizontal" dataSource={this.state.topicListData} style={{height:'300px',overflow:'auto'}} renderItem={item => ( <List.Item style={{cursor:'pointer'}} onClick={this.handleClick.bind(this,item.title)}> <List.Item.Meta avatar={<Avatar size={42} shape="square" src={item.img} />} title={item.title} description={<div>{item.followers}關注 {item.num}沸點</div>} /> </List.Item> )} /> </div>
搜索框回車搜索事件:dom
handleSearch=(value)=>{ const list=[...this.state.storeListData]; if(value!==''){ const searchList=list.filter(item=>item.title.indexOf(value)>-1); this.setState({ topicListData:searchList }) }else{ this.setState({ topicListData:list }) } }
話題列表點擊事件:ide
handleClick=(val)=>{ this.props.handleTopic(val); }
這裏向父組件傳遞了一個val表明當前點擊話題,關於父子組件傳值的相關說明前面文章作了介紹,這裏再也不贅述。svg
使用ant-design的list組件實現函數
render() { const IconText = ({ type, text,tag }) => ( <span onClick={this.handleClick.bind(this,tag)}> <Icon type={type} style={{ marginRight: 8 }} /> {text} </span> ); const PopoverContent=(id)=>{ return <p style={{cursor:'pointer'}} onClick={this.handleReport.bind(this,id)}>舉報</p> } return ( <div> <List itemLayout="vertical" size="large" dataSource={this.state.listData} renderItem={item => ( <List.Item key={item.author} actions={ [ <IconText type="like" text={item.likeNum===0?'贊':item.likeNum} tag='like' />, <IconText type="message" text={item.commentNum===0?'評論':item.commentNum} tag='comment' />, <IconText type="share-alt" text="分享" tag='share' /> ] } extra={ <div> {!item.isFollowed && <Button style={{borderColor:'#6cbd45',color:'#6cbd45'}} onClick={()=>this.handleFollow(item.id)}>{item.isFollowed?'已關注':'關注'}</Button>} <Popover placement="bottom" content={<PopoverContent author={item.author} />} trigger="click"> <span style={{cursor:'pointer',margin:'10px'}}>...</span> </Popover> </div> } > <List.Item.Meta // avatar={<Avatar size={45} src={item.avatar} />} avatar={<Popover placement="top" content={<PersonalPop info={item} handleFollow={(id)=>this.handleFollow(id)} />}> <Avatar size={45} src={item.avatar} /> </Popover>} title={<Popover placement="top" content={<PersonalPop info={item} handleFollow={(id)=>this.handleFollow(id)} />}> <span style={{cursor:'pointer'}}>{item.author}</span> </Popover>} description={<div><span>{item.description}</span><span style={{margin:'0 5px'}}>·</span><span>{timeUtil.getTimeAgo(item.editTime)}</span></div>} /> {item.content} </List.Item> )} /> </div> ); }
用戶頭像鼠標滑過會出現詳情,使用了popover組件,具體內容是單獨抽離的一個PersonalPop組件。每一項根據isFollowed值判斷是否顯示關注按鈕,若是沒有關注,點擊可關注。
右側內容統一使用card組件實現,利用Redux獲取登陸用戶信息。
render() { const { Meta } = Card; return ( <div className='dynamicSide'> <Card style={{ width: '100%' }} className='card1'> <Meta avatar={<Avatar size={62} src={this.props.userImage} />} title={this.props.userId} description={this.props.userDesc} /> <div> <ul> <li> <p className='liTitle'>沸點</p> <p className='liNum'>{this.state.userInfo.topNum}</p> </li> <li> <p className='liTitle'>關注</p> <p className='liNum'>{this.state.userInfo.following}</p> </li> <li> <p className='liTitle'>關注者</p> <p className='liNum'>{this.state.userInfo.follower}</p> </li> </ul> </div> </Card> <Card title="你可能感興趣的人" style={{ width: '100%' ,marginTop:'10px'}} className='card2' > <List itemLayout="horizontal" dataSource={this.state.interestList} renderItem={item => ( <List.Item actions={[<Button>關注</Button>]}> <List.Item.Meta avatar={<Avatar size={40} src={item.userImage} />} title={item.user} description={item.desc} /> </List.Item> )} /> <div className='user-recommend-footer' onClick={this.changeInterestList.bind(this)}> <Icon type="sync" />換一批 </div> </Card> <div style={{marginTop:'10px'}}> <TopicCard title='關注的話題' link='/topics' list={this.state.attentionTopic} /> </div> <div style={{marginTop:'10px'}}> <TopicCard title='更多話題' link='/topics' list={this.state.allTopic} /> </div> </div> ); }
鑑於關注的話題和更多話題內容結構相似,故抽離爲一個公用組件,且爲函數式組件。
function TopicCard({ title,list,link }){ return ( <Card title={title} extra={<a href={link} style={{color:'#007fff'}}>所有></a>} style={{ width: '100%' }} className='topicCard' > <List itemLayout="horizontal" dataSource={list} renderItem={item => ( <List.Item> <List.Item.Meta avatar={<Avatar shape="square" size={40} src={item.userImage} />} title={item.title} description={<div style={{color:'#8a9aa9'}}><span>{item.followNum}關注</span>·<span>{item.hotNews}沸點</span></div>} /> </List.Item> )} /> </Card> ); }
React組件可分爲函數組件(Functional Component )和類組件(Class Component),劃分依據是根據組件的定義方式。函數組件使用函數定義組件,類組件使用ES6 class定義組件。
函數組件的寫法要比類組件簡潔,不過類組件比函數組件功能更強大。類組件能夠維護自身的狀態變量,即組件的state,類組件還有不一樣的生命週期方法,可讓咱們可以在組件的不一樣階段(掛載、更新、卸載)對組件作更多的控制,進行不一樣的操做。但函數組件的使用能夠從思想上讓你在設計組件時進行更多思考,更加關注邏輯控制和顯示的分離,設計出更加合理的組件結構。實際操做中,當一個組件不須要管理自身狀態時,能夠把它設計成函數組件,當你有足夠的理由發現它須要「升級」爲類組件時,再把它改造爲類組件。由於函數組件「升級」爲類組件是有必定成本的,這樣就會要求你作這個改造前更認真地思考其合理性,而不是僅僅爲了一時的方便就使用類組件。
一樣適用函數式組件
function TopicItem({item,showCount}){ const {id,topicName,topicImage,topicCount,followedNum,topicNum,isFollowed}=item; return ( <div className='topicItem'> <Badge count={showCount?topicCount:0} overflowCount={999}> <Avatar shape="square" size={72} src={topicImage} /> </Badge> <div style={{marginLeft:'15px'}}> <div> <NavLink to={`/topic/${id}`}>{topicName}</NavLink> </div> <div style={{color:'#8a9aa9',marginTop:'5px'}}> <span>{followedNum}關注</span>· <span>{topicNum}沸點</span> </div> <div className={isFollowed?'hasFollowed':'noFollow'}> {isFollowed?'已關注':'+關注'} </div> </div> </div> ); }
關注的話題數據實際上是所有話題的子集,在父頁面調用的時候根據數據中的isFollowed屬性進行一次篩選。
componentDidMount(){ const list=[...this.state.allTopicList]; const followedTopicList=list.filter(item=>item.isFollowed); this.setState({ followedTopic:[...followedTopicList] }) }
function TopNav({tags,changeLink}){ return ( <ul> {tags.map((item,index)=>{ return <li key={item.path} onClick={()=>changeLink(item.path)}> <NavLink to={`/books${item.path}`}>{item.text}</NavLink> </li> })} </ul> ) }
使用ant-design中的list組件進行基本佈局
render() { return ( <div> <List size="large" bordered dataSource={this.state.bookList} renderItem={item => ( <List.Item className='bookList' onClick={()=>this.showInfo(item.bookId)}> <img alt='books' src={item.img} /> <div> <div style={{color:'#000',fontSize:'20px',fontWeight:400}}> {item.isPresell && <span className="presale">預售</span> } <span className='bookName'>{item.name}</span> </div> <div className='bookDesc'>{item.desc}</div> <div className='bookAuthor'> <NavLink to={`/user/:${item.userId}`}> <Avatar size={26} src={item.userImage} /> <span style={{color:'#000',marginLeft:'5px'}}>{item.author}</span> </NavLink> <span style={{color:'#71777c',margin:'0 10px',whiteSpace:'nowrap',overflow:'hidden'}}>{item.selfDesc}</span> </div> <div className='other'> {item.isBuy?<span className='bought'>已購買</span>:<span className='price'>¥{item.price}</span>} <span className='message'>{item.chapterNum}小節</span> {item.isBuy && <span className='message'>閱讀時長{item.readTime}分</span>} <span className='message'>{item.purchaseNum}購買</span> </div> </div> </List.Item> )} /> </div> ); }
會根據屬性是否預售isPresell和是否已經購買isBuy來判斷顯示不一樣內容。
相關詳細代碼可查看github,不要忘了star哦!工做中主要是以vue做爲主要技術棧,這是第一次使用React+React-router+Redux來構建項目,不足之處還請你們多多包涵。
金三已過,銀四會是什麼樣子呢?