V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaoyan2017
V2EX  ›  推广

angular 仿微信聊天室 angular-chatroom 实战项目

  •  1
     
  •   xiaoyan2017 · 2019-07-16 19:12:43 +08:00 · 4525 次点击
    这是一个创建于 1717 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一、项目介绍

    运用 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",
    
      "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",
      },
      "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",
      }
    }
    
    /*
     *  angular 主模块配置
     */ 
    
    import { BrowserModule } from '@angular/platform-browser'
    import { NgModule } from '@angular/core'
    import { FormsModule } from '@angular/forms'
    import { AppRoutingModule } from './app-routing.module'
    
    // 引入状态管理
    import { StoreModule } from '@ngrx/store'
    import { reducer } from '../ngrx'
    
    // 载入公共组件( component )
    import { HeaderComponent } from '../components/header'
    import { TabBarComponent } from '../components/tabbar'
    import { XtnScroll } from '../components/xtnScroll/Scroll'
    import { NotFoundComponent } from '../components/404'
    // 载入页面组件( view )
    import { AppComponent } from './app.component'
    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'
    
    @NgModule({
      declarations: [
        // 公共组件
        HeaderComponent,
        TabBarComponent,
        XtnScroll,
        NotFoundComponent,
    
        // 页面组件
        AppComponent,
        LoginComponent,
        RegisterComponent,
        IndexComponent,
        ContactComponent,
        UinfoComponent,
        UcenterComponent,
        GroupChatComponent,
        GroupInfoComponent,
        SingleChatComponent,
      ],
      imports: [
        BrowserModule,
        AppRoutingModule,
        FormsModule,
    
        StoreModule.forRoot(reducer)
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
    /*
     *  angular 路由守卫(验证 token )
     */
    
    import { Router, CanActivate } from '@angular/router'
    
    declare var wcPop: any;
    
    export class Auth implements CanActivate{
        constructor(private router: Router){}
    
        canActivate(){
            let that = this
            // 验证 token
            const token: boolean = window.sessionStorage.getItem('token') ? true : false
    
            if(!token){
                // 未登录授权
                /*
                wcPop({
                    content: '还未登录授权!', anim: 'shake', style: 'background:#e03b30;color:#fff;', time: 2,
                    end: function () {
                        that.router.navigate(['/login']);
                    }
                });
                */
                that.router.navigate(['/login']);
            }
            return token
        }
    }
    
    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, "\r\n");
        html = html.replace(/<[^img].*?>/ig, "");
        html = html.replace(/&nbsp;/ig, "");
        return html.replace(/\r\n|\n|\r/, "").replace(/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, "") == "";
    }
    

    欢迎大家一起交流学习 Q:282310962 wx:xy190310

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2872 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 15:19 · PVG 23:19 · LAX 08:19 · JFK 11:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.