使用React構建精簡版本掘金(四)

rocky191發表於2019-04-03

到目前為止,首頁、文章詳情頁、動態頁、話題頁及小冊頁面內容完成了,看一下效果圖哈,資料不全,見諒哈!

使用React構建精簡版本掘金(四)

實現過程

動態頁

該頁面從佈局來說分左右兩部分,左邊有分為輸入框和已發表內容部分。

  • 輸入框部分

使用React構建精簡版本掘金(四)
我這裡採用了ant-design中的input作為輸入框,而掘金是採用了可編輯的div來實現輸入內容,通過設定contenteditable="true"實現,感興趣的小夥伴可查閱相關資料。
釋出按鈕的disabled狀態將根據輸入框是否有值來決定。功能類似todolist新增功能,可參考文章詳情頁面的釋出評論功能。

<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>
複製程式碼

輸入框內容發生變化事件

handelInputChange=(e)=>{
    if(e.target.value.trim()!==''){
        this.setState({
            disabledBtn:false,
            inputValue:e.target.value
        })
    }else{
        this.setState({
            disabledBtn:true
        })
    }
}
複製程式碼

釋出按鈕事件

handlePressEnter=()=>{
    if(this.state.inputValue!==''){
        console.log('釋出沸點')
    }else{
        message.error('請輸入沸點內容');
    }
}
複製程式碼
  • 新增話題

使用React構建精簡版本掘金(四)
如圖所示,一個搜尋框加一個話題列表。 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>
複製程式碼

搜尋框回車搜尋事件:

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
        })
    }
}
複製程式碼

話題列表點選事件:

handleClick=(val)=>{
    this.props.handleTopic(val);
}
複製程式碼

這裡向父元件傳遞了一個val代表當前點選話題,關於父子元件傳值的相關說明前面文章做了介紹,這裡不再贅述。

  • 已發表的話題列表
    使用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,類元件還有不同的生命週期方法,可以讓我們能夠在元件的不同階段(掛載、更新、解除安裝)對元件做更多的控制,進行不同的操作。但函式元件的使用可以從思想上讓你在設計元件時進行更多思考,更加關注邏輯控制和顯示的分離,設計出更加合理的元件結構。實際操作中,當一個元件不需要管理自身狀態時,可以把它設計成函式元件,當你有足夠的理由發現它需要“升級”為類元件時,再把它改造為類元件。因為函式元件“升級”為類元件是有一定成本的,這樣就會要求你做這個改造前更認真地思考其合理性,而不是僅僅為了一時的方便就使用類元件。

話題頁

使用React構建精簡版本掘金(四)
同樣適用函式式元件

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]
    })
}
複製程式碼

小冊頁

使用React構建精簡版本掘金(四)

  • 頭部的導航
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來構建專案,不足之處還請大家多多包涵。

金三已過,銀四會是什麼樣子呢?

相關文章