• angular版聊天室|仿微信界面IM聊天|NG2+Node聊天实例


    一、项目介绍

    运用angular+angular-cli+angular-router+ngrx/store+rxjs+webpack+node+wcPop等技术实现开发的仿微信angular版聊天室angular-chatroom实例项目,实现了下拉刷新、聊天消息右键菜单、发送消息、表情(动图),图片、视频预览,红包打赏等功能。

    二、技术实现

    • MVVM框架:angular8.0 / @angular/cli
    • 状态管理:@ngrx/store / rxjs
    • 地址路由:@angular/router
    • 弹窗组件:wcPop
    • 打包工具:webpack 2.0
    • 环境配置:node.js + cnpm
    • 图片预览:previewImage
    • 轮播滑动:swiper
    {
      "name": "angular-chatroom",
      "contact": "QQ:282310962 、 wx:xy190310",
    
      "dependencies": {
        "@angular/animations": "~8.0.1",
        "@angular/common": "~8.0.1",
        "@angular/compiler": "~8.0.1",
        "@angular/core": "~8.0.1",
        "@angular/forms": "~8.0.1",
        "@angular/platform-browser": "~8.0.1",
        "@angular/platform-browser-dynamic": "~8.0.1",
        "@angular/router": "~8.0.1",
        "rxjs": "~6.4.0",
        "tslib": "^1.9.0",
        "zone.js": "~0.9.1"
      },
      "devDependencies": {
        "@angular-devkit/build-angular": "~0.800.0",
        "@angular/cli": "~8.0.3",
        "@angular/compiler-cli": "~8.0.1",
        "@angular/language-service": "~8.0.1",
        "@ngrx/store": "^8.0.1",
        "@types/jasmine": "~3.3.8",
        "@types/jasminewd2": "~2.0.3",
        "@types/node": "~8.9.4",
        "@types/swiper": "^4.4.3",
        "codelyzer": "^5.0.0",
        "jasmine-core": "~3.4.0",
        "jasmine-spec-reporter": "~4.2.1",
        "jquery": "^2.2.3",
        "karma": "~4.1.0",
        "karma-chrome-launcher": "~2.2.0",
        "karma-coverage-istanbul-reporter": "~2.0.1",
        "karma-jasmine": "~2.0.1",
        "karma-jasmine-html-reporter": "^1.4.0",
        "swiper": "^4.5.0",
        "typescript": "~3.4.3"
      }
    }

    ◆ App主页面模板、app-routing路由地址配置

    <div class="weChatIM__panel clearfix">
        <div class="we__chatIM-wrapper flexbox flex__direction-column">
            <!-- 顶部 -->
            <header-bar></header-bar>
            
            <!-- 主页面 -->
            <div class="wcim__container flex1">
                <router-outlet></router-outlet>
            </div>
    
            <!-- 底部 -->
            <tab-bar></tab-bar>
        </div>
    </div>
    /*
     *  angular/router路由配置
     */
    
    import { NgModule } from '@angular/core'
    import { Routes, RouterModule } from '@angular/router'
    
    // 引入路由验证
    import { Auth } from '../views/auth/auth'
    
    // 引入页面组件
    import { NotFoundComponent } from '../components/404'
    import { LoginComponent } from '../views/auth/login'
    import { RegisterComponent } from '../views/auth/register'
    import { IndexComponent } from '../views/index'
    import { ContactComponent } from '../views/contact'
    import { UinfoComponent } from '../views/contact/uinfo'
    import { UcenterComponent } from '../views/ucenter'
    import { GroupChatComponent } from '../views/chat/group-chat'
    import { GroupInfoComponent } from '../views/chat/group-info'
    import { SingleChatComponent } from '../views/chat/single-chat'
    
    
    export const routes: Routes = [
      {
        path: '', redirectTo: 'index', pathMatch: 'full',
        data: { showHeader: true, showTabBar: true },
      },
    
      // 登录、注册
      {
        path: 'login', component: LoginComponent,
      },
      {
        path: 'register', component: RegisterComponent,
      },
    
      // 首页、联系人、我
      {
        path: 'index', component: IndexComponent, canActivate: [Auth],
        data: { showHeader: true, showTabBar: true },
      },
      {
        path: 'contact', component: ContactComponent, canActivate: [Auth],
        data: { showHeader: true, showTabBar: true },
      },
      {
        path: 'contact/uinfo', component: UinfoComponent
      },
      {
        path: 'ucenter', component: UcenterComponent, canActivate: [Auth],
        data: { showHeader: false, showTabBar: true },
      },
    
      // 聊天页面
      {
        path: 'chat/group-chat', component: GroupChatComponent, canActivate: [Auth]
      },
      {
        path: 'chat/single-chat', component: SingleChatComponent, canActivate: [Auth]
      },
      {
        path: 'chat/group-info', component: GroupInfoComponent, canActivate: [Auth]
      },
    
      // 404
      {
        path: '**', component: NotFoundComponent,
      },
    
      // ...
    ];
    
    @NgModule({
      // imports: [RouterModule.forRoot(routes)],
      imports: [RouterModule.forRoot(routes, { useHash: true })],  //开启hash模式
      exports: [RouterModule],
      providers: [Auth]
    })
    export class AppRoutingModule {}

    ◆ angular + ngrx/store页面状态管理

    ◆ angular登录、注册验证

    export class LoginComponent implements OnInit {
        private formField = {
            tel: '',
            pwd: ''
        }
        
        private auth: any
        constructor(
            private router: Router,
            private store: Store<{}>
        ) {
            let that = this
            this.store.select('auth').subscribe(v => {
                console.log(v)
                that.auth = v;
            })
        }
    
        ngOnInit(): void {
            if(this.auth.token){
                this.router.navigate(['/index'])
            }
        }
    
        handleSubmit(){
            let that = this
    
            if(!this.formField.tel){
                wcPop({ content: '手机号不能为空!', style: 'background:#eb5a5c;color:#fff;', time: 2 });
            }else if(!checkTel(this.formField.tel)){
                wcPop({ content: '手机号格式不正确!', style: 'background:#eb5a5c;color:#fff;', time: 2 });
            }else if(!this.formField.pwd){
                wcPop({ content: '密码不能为空!', style: 'background:#eb5a5c;color:#fff;', time: 2 });
            }else{
                this.store.dispatch(new actions.setToken(getToken(64)))
                this.store.dispatch(new actions.setUser(this.formField.tel))
    
                wcPop({
                    content: '登录成功,跳转中...', style: 'background:#378fe7;color:#fff;', time: 2, shadeClose: false,
                    end: function () {
                        that.router.navigate(['/index'])
                    }
                });
            }
        }
    }

    ◆ 编辑器核心消息处理

    function surrounds() {
        setTimeout(function () { //chrome
            var sel = window.getSelection();
            var anchorNode = sel.anchorNode;
            if (!anchorNode) return;
            if (sel.anchorNode === $(".J__wcEditor")[0] ||
                (sel.anchorNode.nodeType === 3 && sel.anchorNode.parentNode === $(".J__wcEditor")[0])) {
    
                var range = sel.getRangeAt(0);
                var p = document.createElement("p");
                range.surroundContents(p);
                range.selectNodeContents(p);
                range.insertNode(document.createElement("br")); //chrome
                sel.collapse(p, 0);
    
                (function clearBr() {
                    var elems = [].slice.call($(".J__wcEditor")[0].children);
                    for (var i = 0, len = elems.length; i < len; i++) {
                        var el = elems[i];
                        if (el.tagName.toLowerCase() == "br") {
                            $(".J__wcEditor")[0].removeChild(el);
                        }
                    }
                    elems.length = 0;
                })();
            }
        }, 10);
    }
    
    // 定义最后光标位置
    var _lastRange = null, _sel = window.getSelection && window.getSelection();
    var _rng = {
        getRange: function () {
            if (_sel && _sel.rangeCount > 0) {
                return _sel.getRangeAt(0);
            }
        },
        addRange: function () {
            if (_lastRange) {
                _sel.removeAllRanges();
                _sel.addRange(_lastRange);
            }
        }
    }
    
    // 消息处理
    function isEmpty() {
        // var html = $editor.html();
        var html = $(".J__wcEditor").html();
        html = html.replace(/<br[s/]{0,2}>/ig, "
    ");
        html = html.replace(/<[^img].*?>/ig, "");
        html = html.replace(/&nbsp;/ig, "");
        return html.replace(/
    |
    |
    /, "").replace(/(?:^[ 	
    
    ]+)|(?:[ 	
    
    ]+$)/g, "") == "";
    }

  • 相关阅读:
    SpringBoot最新教程IDEA版【狂神说Java系列】
    Mybatis最新完整教程IDEA版【通俗易懂2019.11月】
    freemarker 数字格式化(金额格式化)
    idea皮肤插件
    Spring Security 自定义表单登录页
    Spring Security 用户配置
    Spring Security 初体验
    Tomcat 部署 Spring Boot工程
    服务器安装Ngnix
    java读取配置文件
  • 原文地址:https://www.cnblogs.com/xiaoyan2017/p/11194828.html
Copyright © 2020-2023  润新知