为了账号安全,请及时绑定邮箱和手机立即绑定

taro+react仿微信App聊天实例|taro聊天室

2019.12.16 13:53 3669浏览

taroChatRoom聊天室是基于taro+react+redux+RN+taropop等技术开发的仿微信App界面聊天IM项目,实现了消息发送、表情动图、预览图片、红包、朋友圈等功能。

Taro多端实践:仿微信聊天室 (H5+小程序+App)

http://img4.mukewang.com/5df7186b0001ff6411460667.jpg

技术实现:

  • 编码/技术:Vscode + react/taro/redux/react-native

  • iconfont图标:阿里字体图标库

  • 自定义导航栏Navigation + 底部Tabbar

  • 弹窗组件:taroPop(基于Taro封装自定义模态框)

  • 支持编译:H5端 + 小程序 + RN端

http://img1.mukewang.com/5df718c10001b00204140688.jpg

http://img1.mukewang.com/5df718c10001f92104140688.jpg

http://img3.mukewang.com/5df718c10001f81e04140688.jpg

http://img.mukewang.com/5df718c20001be7804140688.jpg

http://img2.mukewang.com/5df718c10001fa1104140688.jpg

http://img2.mukewang.com/5df718c20001e92304140688.jpg

http://img3.mukewang.com/5df718c30001965404140688.jpg

http://img.mukewang.com/5df718c30001f5a004140688.jpg

http://img2.mukewang.com/5df718c30001725004140688.jpg

http://img4.mukewang.com/5df718c40001476c04140688.jpg

http://img2.mukewang.com/5df718c30001f64e04140688.jpg

http://img2.mukewang.com/5df718c40001a5de04140688.jpg

http://img1.mukewang.com/5df718c40001112504140688.jpg

http://img4.mukewang.com/5df718c40001b25b04140688.jpg

http://img3.mukewang.com/5df718c4000192d104140688.jpg

项目中顶部导航+tabbar是采用自定义组件模式,具体参看:

Taro自定义Modal对话框组件|taro仿微信、android弹窗

Taro多端自定义导航栏Navbar+Tabbar实例

主页面配置

配置页面路径及全局参数,引入公共样式及状态管理

/**
  * @desc   Taro入口页面 app.jsx
  * @about  Q:282310962  wx:xy190310
  */

import Taro, { Component } from '@tarojs/taro'
import Index from './pages/index'

// 引入状态管理redux
import { Provider } from '@tarojs/redux'
import { store } from './store'

// 引入样式
import './app.scss'
import './styles/fonts/iconfont.css'
import './styles/reset.scss'

class App extends Component {
  config = {
    pages: [
      'pages/auth/login/index',
      'pages/auth/register/index',
      'pages/index/index',
      ...
    ],
    window: {
      backgroundTextStyle: 'light',
      navigationBarBackgroundColor: '#fff',
      navigationBarTitleText: 'TaroChat',
      navigationBarTextStyle: 'black',
      navigationStyle: 'custom'
    }
  }
  
  // 在 App 类中的 render() 函数没有实际作用
  // 请勿修改此函数
  render () {
    return (
      <Provider store={store}>
        <Index />
      </Provider>
    )
  }
}

Taro.render(<App />, document.getElementById('app'))

Taro表单操作|本地存储处理

在taro中获取表单input值也比较简单,直接使用onInput事件即可

<Input placeholder="请输入手机号/昵称" onInput={this.handleInput.bind(this, 'tel')} />
this.state = {
	tel: '',
	pwd: '',
}

handleInput = (key, e) => {
    this.setState({ [key]: e.detail.value })
}

通过上面的方法就有简单的获取input值

return (
    <View className="taro__container flexDC bg-eef1f5">
        <Navigation background='#eef1f5' fixed />
        
        <ScrollView className="taro__scrollview flex1" scrollY>
            <View className="auth-lgreg">
                {/* logo */}
                <View className="auth-lgreg__slogan">
                    <View className="auth-lgreg__slogan-logo">
                        <Image className="auth-lgreg__slogan-logo__img" src={require('../../../assets/taro.png')} mode="aspectFit" />
                    </View>
                    <Text className="auth-lgreg__slogan-text">欢迎来到Taro-Chatroom</Text>
                </View>
                {/* 表单 */}
                <View className="auth-lgreg__forms">
                    <View className="auth-lgreg__forms-wrap">
                        <View className="auth-lgreg__forms-item">
                            <Input className="auth-lgreg__forms-iptxt flex1" placeholder="请输入手机号/昵称" onInput={this.handleInput.bind(this, 'tel')} />
                        </View>
                        <View className="auth-lgreg__forms-item">
                            <Input className="auth-lgreg__forms-iptxt flex1" placeholder="请输入密码" password onInput={this.handleInput.bind(this, 'pwd')} />
                        </View>
                    </View>
                    <View className="auth-lgreg__forms-action">
                        <TouchView onClick={this.handleSubmit}><Text className="auth-lgreg__forms-action__btn">登录</Text></TouchView>
                    </View>
                    <View className="auth-lgreg__forms-link">
                        <Text className="auth-lgreg__forms-link__nav">忘记密码</Text>
                        <Text className="auth-lgreg__forms-link__nav" onClick={this.GoToRegister}>注册账号</Text>
                    </View>
                </View>
            </View>
        </ScrollView>

        <TaroPop ref="taroPop" />
    </View>
)
/**
 * @tpl 登录模板
 */

import Taro from '@tarojs/taro'
import { View, Text, ScrollView, Image, Input, Button } from '@tarojs/components'

import './index.scss'

import { connect } from '@tarojs/redux'
import * as actions from '../../../store/action'...

class Login extends Taro.Component {
    config = {
        navigationBarTitleText: '登录'
    }
    constructor(props) {
        super(props)
        this.state = {
            tel: '',
            pwd: '',
        }
    }
    componentWillMount() {
        // 判断是否登录
        storage.get('hasLogin').then(res => {
            if(res && res.hasLogin) {
                Taro.navigateTo({url: '/pages/index/index'})
            }
        })
    }
    // 提交表单
    handleSubmit = () => {
        let taroPop = this.refs.taroPop
        let { tel, pwd } = this.state

        if(!tel) {
            taroPop.show({content: '手机号不能为空', time: 2})
        }else if(!util.checkTel(tel)) {
            taroPop.show({content: '手机号格式有误', time: 2})
        }else if(!pwd) {
            taroPop.show({content: '密码不能为空', time: 2})
        }else {
            // ...接口数据
            ...
            
            storage.set('hasLogin', { hasLogin: true })
            storage.set('user', { username: tel })
            storage.set('token', { token: util.setToken() })

            taroPop.show({
                skin: 'toast',
                content: '登录成功',
                icon: 'success',
                time: 2
            })
            
            ...
        }
    }
    
    render () {
        ...
    }
}

const mapStateToProps = (state) => {
    return {...state.auth}
}

export default connect(mapStateToProps, {
    ...actions
})(Login)

taro中rn端不支持同步存储,只能改为setStorageSync异步存储了

import Taro from '@tarojs/taro'export default class Storage {
    static get(key) {        return Taro.getStorage({ key }).then(res => res.data).catch(() => '')
    }

    static set(key, data){        return Taro.setStorage({key: key, data: data}).then(res => res)
    }

    ...
}
  • Taro聊天滚动到消息底部

H5/小程序端则可通过获取createSelectorQuery 来实现滚动到聊天底部,由于RN端不支持createSelectorQuery,则只能另外兼容处理

// 滚动聊天底部
scrollMsgBottom = () => {
    let query = Taro.createSelectorQuery()
    query.select('#scrollview').boundingClientRect()
    query.select('#msglistview').boundingClientRect()
    query.exec((res) => {
        // console.log(res)
        if(res[1].height > res[0].height) {
            this.setState({ scrollTop: res[1].height - res[0].height })
        }
    })
}
scrollMsgBottomRN = (t) => {
    let that = this
    this._timer = setTimeout(() => {
        that.refs.ScrollViewRN.scrollToEnd({animated: false})
    }, t ? 16 : 0)
}
componentDidMount() {
    if(process.env.TARO_ENV === 'rn') {
        this.scrollMsgBottomRN()
    }else {
        this.scrollMsgBottom()
    }
}
...

// 聊天消息区域
msgPanelClicked = () => {
	if(!this.state.showFootToolbar) return
	this.setState({ showFootToolbar: false })
}

swtEmojChooseView = (index) => {
	this.setState({ showFootToolbar: true, showFootViewIndex: index })
}

// 表情tab切换
swtEmojTab = (index) => {
	let lists = this.state.emotionJson
	for(var i = 0, len = lists.length; i < len; i++) {
		lists[i].selected = false
	}
	lists[index].selected = true
	this.setState({ emotionJson: lists })
}


/* >>> 编辑器/表情处理------------------------------------- */
bindEditorInput = (e) => {
	this.setState({
		editorText: e.detail.value,
		editorLastCursor: e.detail.cursor
	})
}
bindEditorFocus = (e) => {
	this.setState({ editorLastCursor: e.detail.cursor })
}
bindEditorBlur = (e) => {
	this.setState({ editorLastCursor: e.detail.cursor })
}

handleEmotionTaped = (emoj) => {
	if(emoj == 'del') return
	// 在光标处插入表情
	let { editorText, editorLastCursor } = this.state
	let lastCursor = editorLastCursor ? editorLastCursor : editorText.length
	let startStr = editorText.substr(0, lastCursor)
	let endStr = editorText.substr(lastCursor)
	this.setState({
		editorText: startStr + `${emoj} ` + endStr
	})
}

...

最后附上两个vue实例项目,希望能喜欢~~ 

vue网页版聊天实例:

https://www.cnblogs.com/xiaoyan2017/p/10793728.html

vue+uniapp仿抖音小视频|直播室实例:

https://www.cnblogs.com/xiaoyan2017/p/11835641.html



点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

相关文章推荐

正在加载中
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消