一、項目介紹 運用angular+angular-cli+angular-router+ngrx/store+rxjs+webpack+node+wcPop等技術實現開發的仿微信angular版聊天室angular-chatroom實例項目,實現了下拉刷新、聊天消息右鍵菜單、發送消息、表情(動圖), ...
一、項目介紹
運用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, "\r\n"); html = html.replace(/<[^img].*?>/ig, ""); html = html.replace(/ /ig, ""); return html.replace(/\r\n|\n|\r/, "").replace(/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, "") == ""; }