Browse Source

instant-message

pull/116/head
cKey 5 years ago
parent
commit
ce2c123caf
  1. 10
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreMessageRepository.cs
  2. BIN
      aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db
  3. 8
      aspnet-core/services/identity-server/LINGYUN.Abp.IdentityServer4.HttpApi.Host/AbpIdentityServerAdminHttpApiHostModule.cs
  4. 187
      vueJs/package-lock.json
  5. 1
      vueJs/package.json
  6. 44
      vueJs/src/api/instant-message.ts
  7. 308
      vueJs/src/components/InstantMessage/index.vue
  8. 54
      vueJs/src/components/Lemon-IMUI/components/Avatar.vue
  9. 0
      vueJs/src/components/Lemon-IMUI/index.vue
  10. 5
      vueJs/src/mixins/DataListMiXin.ts
  11. 7
      vueJs/src/mixins/EventBusMiXin.ts
  12. 18
      vueJs/src/views/dashboard/admin/index.vue
  13. 18
      vueJs/src/views/dashboard/index.vue

10
aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Chat/EfCoreMessageRepository.cs

@ -161,12 +161,12 @@ namespace LINGYUN.Abp.MessageService.Chat
{
sorting = reverse ? sorting + " desc" : sorting;
var userMessages = await DbContext.Set<UserMessage>()
.Distinct()
.Where(x => x.CreatorId.Equals(sendUserId) && x.ReceiveUserId.Equals(receiveUserId))
.Where(x => (x.CreatorId.Equals(sendUserId) && x.ReceiveUserId.Equals(receiveUserId)) |
x.CreatorId.Equals(receiveUserId) && x.ReceiveUserId.Equals(sendUserId))
.WhereIf(type != null, x => x.Type.Equals(type))
.WhereIf(!filter.IsNullOrWhiteSpace(), x => x.Content.Contains(filter) || x.SendUserName.Contains(filter))
.OrderBy(sorting ?? nameof(GroupMessage.MessageId))
.Page(skipCount, maxResultCount)
.PageBy(skipCount, maxResultCount)
.AsNoTracking()
.ToListAsync(GetCancellationToken(cancellationToken));
@ -181,8 +181,8 @@ namespace LINGYUN.Abp.MessageService.Chat
CancellationToken cancellationToken = default)
{
var userMessagesCount = await DbContext.Set<UserMessage>()
.Distinct()
.Where(x => x.CreatorId.Equals(sendUserId) && x.ReceiveUserId.Equals(receiveUserId))
.Where(x => (x.CreatorId.Equals(sendUserId) && x.ReceiveUserId.Equals(receiveUserId)) |
x.CreatorId.Equals(receiveUserId) && x.ReceiveUserId.Equals(sendUserId))
.WhereIf(type != null, x => x.Type.Equals(type))
.WhereIf(!filter.IsNullOrWhiteSpace(), x => x.Content.Contains(filter) || x.SendUserName.Contains(filter))
.LongCountAsync(GetCancellationToken(cancellationToken));

BIN
aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db

Binary file not shown.

8
aspnet-core/services/identity-server/LINGYUN.Abp.IdentityServer4.HttpApi.Host/AbpIdentityServerAdminHttpApiHostModule.cs

@ -104,6 +104,14 @@ namespace LINGYUN.Abp.IdentityServer4
Configure<AbpDbContextOptions>(options =>
{
options.UseMySQL();
//if (hostingEnvironment.IsDevelopment())
//{
// options.PreConfigure(ctx =>
// {
// ctx.DbContextOptions.EnableDetailedErrors();
// ctx.DbContextOptions.EnableSensitiveDataLogging();
// });
//}
});
// 加解密

187
vueJs/package-lock.json

@ -2019,12 +2019,37 @@
"glob-to-regexp": "^0.3.0"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npm.taobao.org/@nodelib/fs.scandir/download/@nodelib/fs.scandir-2.1.3.tgz",
"integrity": "sha1-Olgr21OATGum0UZXnEblITDPSjs=",
"requires": {
"@nodelib/fs.stat": "2.0.3",
"run-parallel": "^1.1.9"
},
"dependencies": {
"@nodelib/fs.stat": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/@nodelib/fs.stat/download/@nodelib/fs.stat-2.0.3.tgz",
"integrity": "sha1-NNxfTKu8cg9OYPdadH5+zWwXW9M="
}
}
},
"@nodelib/fs.stat": {
"version": "1.1.3",
"resolved": "https://registry.npm.taobao.org/@nodelib/fs.stat/download/@nodelib/fs.stat-1.1.3.tgz",
"integrity": "sha1-K1o6s/kYzKSKjHVMCBaOPwPrphs=",
"dev": true
},
"@nodelib/fs.walk": {
"version": "1.2.4",
"resolved": "https://registry.npm.taobao.org/@nodelib/fs.walk/download/@nodelib/fs.walk-1.2.4.tgz",
"integrity": "sha1-ARuSAqcKY2bkNspcBlhEUoqwSXY=",
"requires": {
"@nodelib/fs.scandir": "2.1.3",
"fastq": "^1.6.0"
}
},
"@samverschueren/stream-to-observable": {
"version": "0.3.0",
"resolved": "https://registry.npm.taobao.org/@samverschueren/stream-to-observable/download/@samverschueren/stream-to-observable-0.3.0.tgz",
@ -4663,8 +4688,7 @@
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz",
"integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=",
"dev": true
"integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg="
},
"binary-extensions": {
"version": "1.13.1",
@ -7563,8 +7587,7 @@
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/emojis-list/download/emojis-list-3.0.0.tgz",
"integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=",
"dev": true
"integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang="
},
"encodeurl": {
"version": "1.0.2",
@ -8800,6 +8823,14 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
"fastq": {
"version": "1.8.0",
"resolved": "https://registry.npm.taobao.org/fastq/download/fastq-1.8.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffastq%2Fdownload%2Ffastq-1.8.0.tgz",
"integrity": "sha1-VQ4fn1m7xl/hhctqm02VNXEH9IE=",
"requires": {
"reusify": "^1.0.4"
}
},
"faye-websocket": {
"version": "0.10.0",
"resolved": "https://registry.npm.taobao.org/faye-websocket/download/faye-websocket-0.10.0.tgz",
@ -10971,8 +11002,7 @@
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"is-finite": {
"version": "1.1.0",
@ -10995,7 +11025,6 @@
"version": "4.0.1",
"resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz",
"integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
@ -14465,7 +14494,6 @@
"version": "2.1.3",
"resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz",
"integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
@ -14534,6 +14562,11 @@
"integrity": "sha1-p5yezIbuHOP6YgbRIWxQHxR/wH4=",
"dev": true
},
"klona": {
"version": "2.0.4",
"resolved": "https://registry.npm.taobao.org/klona/download/klona-2.0.4.tgz?cache=0&sync_timestamp=1600226641291&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-2.0.4.tgz",
"integrity": "sha1-e7Hjr/sMuGJFR+9+j2cI6i4538A="
},
"launch-editor": {
"version": "2.2.1",
"resolved": "https://registry.npm.taobao.org/launch-editor/download/launch-editor-2.2.1.tgz",
@ -16149,8 +16182,7 @@
"merge2": {
"version": "1.3.0",
"resolved": "https://registry.npm.taobao.org/merge2/download/merge2-1.3.0.tgz",
"integrity": "sha1-WzZu6DsvFYLEj4fkfPGpNSEDyoE=",
"dev": true
"integrity": "sha1-WzZu6DsvFYLEj4fkfPGpNSEDyoE="
},
"methods": {
"version": "1.1.2",
@ -16649,8 +16681,7 @@
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz",
"integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=",
"dev": true
"integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU="
},
"normalize-range": {
"version": "0.1.2",
@ -17256,8 +17287,7 @@
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz",
"integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=",
"dev": true
"integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0="
},
"pify": {
"version": "4.0.1",
@ -18772,6 +18802,11 @@
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"dev": true
},
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/reusify/download/reusify-1.0.4.tgz",
"integrity": "sha1-kNo4Kx4SbvwCFG6QhFqI2xKSXXY="
},
"rgb-regex": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/rgb-regex/download/rgb-regex-1.0.1.tgz",
@ -18814,6 +18849,11 @@
"integrity": "sha1-hEDsz5nqPnC9QJ1JqriOEMGJpFU=",
"dev": true
},
"run-parallel": {
"version": "1.1.9",
"resolved": "https://registry.npm.taobao.org/run-parallel/download/run-parallel-1.1.9.tgz",
"integrity": "sha1-yd06fPn0ssS2JE4XOm7YZuYd1nk="
},
"run-queue": {
"version": "1.0.3",
"resolved": "https://registry.npm.taobao.org/run-queue/download/run-queue-1.0.3.tgz",
@ -19939,6 +19979,125 @@
}
}
},
"stylus-loader": {
"version": "4.1.1",
"resolved": "https://registry.npm.taobao.org/stylus-loader/download/stylus-loader-4.1.1.tgz",
"integrity": "sha1-DpT11idJMqLa0FTRpzazIUasepk=",
"requires": {
"fast-glob": "^3.2.4",
"klona": "^2.0.4",
"loader-utils": "^2.0.0",
"normalize-path": "^3.0.0",
"schema-utils": "^3.0.0"
},
"dependencies": {
"@nodelib/fs.stat": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/@nodelib/fs.stat/download/@nodelib/fs.stat-2.0.3.tgz",
"integrity": "sha1-NNxfTKu8cg9OYPdadH5+zWwXW9M="
},
"@types/json-schema": {
"version": "7.0.6",
"resolved": "https://registry.npm.taobao.org/@types/json-schema/download/@types/json-schema-7.0.6.tgz",
"integrity": "sha1-9MfsQ+gbMZqYFRFQMXCfJph4kfA="
},
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1603561543180&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.6.tgz",
"integrity": "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-3.5.2.tgz?cache=0&sync_timestamp=1603565974467&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz",
"integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0="
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz",
"integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=",
"requires": {
"fill-range": "^7.0.1"
}
},
"fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npm.taobao.org/fast-glob/download/fast-glob-3.2.4.tgz?cache=0&sync_timestamp=1592290276588&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-glob%2Fdownload%2Ffast-glob-3.2.4.tgz",
"integrity": "sha1-0grvv5lXk4Pn88xmUpFYybmFVNM=",
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.0",
"merge2": "^1.3.0",
"micromatch": "^4.0.2",
"picomatch": "^2.2.1"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz",
"integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=",
"requires": {
"to-regex-range": "^5.0.1"
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.1.tgz",
"integrity": "sha1-tsHvQXxOVmPqSY8cRa+saRa7wik=",
"requires": {
"is-glob": "^4.0.1"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz",
"integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss="
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz?cache=0&sync_timestamp=1584445172927&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-4.0.2.tgz",
"integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=",
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"schema-utils": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-3.0.0.tgz",
"integrity": "sha1-Z1AvaqK2ai1AMrQnmilEl4oJE+8=",
"requires": {
"@types/json-schema": "^7.0.6",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz",
"integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=",
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-5.5.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz",

1
vueJs/package.json

@ -42,6 +42,7 @@
"script-loader": "^0.7.2",
"simple-progress-webpack-plugin": "^1.1.2",
"sortablejs": "^1.10.2",
"stylus-loader": "^4.1.1",
"tinymce": "^5.2.1",
"tui-editor": "^1.4.10",
"view-design": "^4.2.0",

44
vueJs/src/api/instant-message.ts

@ -5,7 +5,7 @@ const serviceUrl = process.env.VUE_APP_BASE_API
export default class ImApiService {
public static getMyFriends(payload: MyFriendGetByPaged) {
let _url = 'api/im/my-friends'
let _url = '/api/im/my-friends'
_url += '?filter=' + payload.filter
_url += '&sorting=' + payload.sorting
_url += '&reverse=' + payload.reverse
@ -13,6 +13,18 @@ export default class ImApiService {
_url += '&maxResultCount=' + payload.maxResultCount
return ApiService.Get<PagedResultDto<UserFriend>>(_url, serviceUrl)
}
public static getMyChatMessages(payload: UserMessageGetByPaged) {
let _url = '/api/im/chat/messages/me'
_url += '?receiveUserId=' + payload.receiveUserId
_url += '&messageType=' + payload.messageType
_url += '&filter=' + payload.filter
_url += '&sorting=' + payload.sorting
_url += '&reverse=' + payload.reverse
_url += '&skipCount=' + payload.skipCount
_url += '&maxResultCount=' + payload.maxResultCount
return ApiService.Get<PagedResultDto<ChatMessage>>(_url, serviceUrl)
}
}
export enum Sex
@ -50,11 +62,21 @@ export class MyFriendGetByPaged extends PagedAndSortedResultRequestDto {
reverse = false
}
export class UserMessageGetByPaged extends PagedAndSortedResultRequestDto {
filter = ''
messageType = MessageType.Text
sorting = 'CreationTime'
reverse = true
receiveUserId = ''
}
export enum MessageType {
Text = 0,
Image = 10,
Link = 20,
Video = 30
Video = 30,
Voice = 40,
File = 50
}
export class ChatMessage {
@ -68,4 +90,22 @@ export class ChatMessage {
sendTime = new Date()
isAnonymous = false
messageType = MessageType.Text
public static getType(messageType: MessageType) {
switch(messageType) {
case MessageType.Text :
case MessageType.Link :
return 'text'
case MessageType.Video :
return 'video'
case MessageType.Image :
return 'image'
case MessageType.Voice :
return 'voice'
case MessageType.File :
return 'file'
default :
return 'text'
}
}
}

308
vueJs/src/components/InstantMessage/index.vue

@ -1,11 +1,29 @@
<template>
<div>
<div class="imui-center">
<lemon-imui
v-show="showDialog"
ref="IMUI"
:user="currentUser"
@send="handleSendMessage"
/>
@pull-messages="onPullMessages"
@change-menu="onChangeMenu"
@change-contact="onChangeContract"
@message-click="handleMessageClick"
@menu-avatar-click="handleMenuAvatarClick"
>
<template #cover>
<div class="cover">
<i class="lemon-icon-message" />
<p><b>Lemon</b> IMUI</p>
</div>
</template>
<template #contact-title="contact">
<span>{{ contact.displayName }}</span>
<small
class="more"
>&#8230;</small>
</template>
</lemon-imui>
</div>
</template>
@ -13,9 +31,16 @@
import EventBusMiXin from '@/mixins/EventBusMiXin'
import Component, { mixins } from 'vue-class-component'
import { abpPagerFormat } from '@/utils'
import { UserModule } from '@/store/modules/user'
import ImApiService, { MyFriendGetByPaged, UserFriend, ChatMessage } from '@/api/instant-message'
import ImApiService, {
MyFriendGetByPaged,
UserMessageGetByPaged,
UserFriend,
ChatMessage
} from '@/api/instant-message'
import { HubConnection, HubConnectionBuilder, HubConnectionState } from '@microsoft/signalr'
@ -26,8 +51,58 @@ class MyContract {
type = ''
index = 'A'
unread = 0
lastSendTime = 0
lastContent = '0'
lastSendTime = 1345209465000
lastContent = ''
readMsgPage = 1
}
class ChatMenu {
name = ''
title = ''
unread = 0
render?: Function
renderContainer?: Function
isBottom = false
constructor(
name: string,
title: string,
unread = 0,
isBottom = false
) {
this.name = name
this.title = title
this.unread = unread
this.isBottom = isBottom
}
}
class FromUser {
id = ''
displayName = ''
avatar = ''
}
class Message {
id = ''
status = ''
type = 'text'
sendTime = 0
content = ''
fileSize = 0
fileName = ''
toContactId = ''
fromUser = new FromUser()
public fromChatMessage(chatMessage: ChatMessage) {
this.id = chatMessage.messageId
this.content = chatMessage.content
this.fromUser.id = chatMessage.formUserId
this.fromUser.displayName = chatMessage.formUserName
this.toContactId = chatMessage.toUserId
this.type = ChatMessage.getType(chatMessage.messageType)
this.sendTime = new Date(chatMessage.sendTime).getTime()
}
}
@Component({
@ -40,6 +115,9 @@ export default class InstantMessage extends mixins(EventBusMiXin) {
private myFriends = new Array<UserFriend>()
private connection!: HubConnection
private chatMenus = new Array<ChatMenu>()
private getChatMessageFilter = new UserMessageGetByPaged()
get currentUser() {
return {
@ -50,7 +128,9 @@ export default class InstantMessage extends mixins(EventBusMiXin) {
}
mounted() {
this.unSubscribeAll()
this.subscribe('onShowImDialog', this.onShowImDialog)
this.handleInitDefaultMenus()
this.handleStartConnection()
}
@ -58,6 +138,40 @@ export default class InstantMessage extends mixins(EventBusMiXin) {
this.unSubscribe('onShowImDialog')
}
private handleInitIMUI() {
const imui = this.$refs.IMUI as any
ImApiService
.getMyFriends(this.dataFilter)
.then(res => {
this.myFriends = res.items
this.myFriendCount = res.totalCount
this.handleInitContracts(imui)
})
}
private handleInitContracts(imui: any) {
const myContracts = new Array<MyContract>()
this.myFriends
.forEach(friend => {
const myContract = new MyContract()
myContract.id = friend.friendId
myContract.displayName = friend.remarkName ?? friend.userName
myContract.unread = 0
myContract.lastSendTime = new Date().getTime()
myContract.lastContent = '1524545'
myContract.type = 'many'
myContracts.push(myContract)
})
imui.initContacts(myContracts)
imui.initMenus(this.chatMenus)
}
private handleInitDefaultMenus() {
const lastMsgMenu = new ChatMenu('lastMessages', '历史消息')
const contractsMenu = new ChatMenu('contacts', '联系人')
this.chatMenus.push(lastMsgMenu, contractsMenu)
}
private handleStartConnection() {
if (!this.connection) {
const builder = new HubConnectionBuilder()
@ -66,6 +180,10 @@ export default class InstantMessage extends mixins(EventBusMiXin) {
.withUrl('/signalr-hubs/signalr-hubs/messages', { accessTokenFactory: () => userToken })
.withAutomaticReconnect({ nextRetryDelayInMilliseconds: () => 60000 })
.build()
this.connection.on('getChatMessage', this.handleReceiveMessage)
this.connection.onreconnected(() => {
this.handleInitIMUI()
})
this.connection.onclose(error => {
console.log('signalr connection has closed, error:')
console.log(error)
@ -75,36 +193,83 @@ export default class InstantMessage extends mixins(EventBusMiXin) {
this.connection
.start()
.then(() => {
this.handleInitIMUI()
})
}
}
private onShowImDialog() {
this.showDialog = !this.showDialog
}
private onChangeMenu() {
console.log('Event:change-menu')
}
private onPullMessages(contact: any, next: any) {
console.log(contact)
const imui = this.$refs.IMUI as any
if (this.getChatMessageFilter.receiveUserId !== contact.id) {
this.getChatMessageFilter.receiveUserId = contact.id
} else {
contact.readMsgPage += 1
}
this.getChatMessageFilter.skipCount = abpPagerFormat(contact.readMsgPage, this.getChatMessageFilter.maxResultCount)
ImApiService
.getMyFriends(this.dataFilter)
.getMyChatMessages(this.getChatMessageFilter)
.then(res => {
this.myFriends = res.items
this.myFriendCount = res.totalCount
this.handleInitContracts()
const messages = res.items
.sort((last, next) => {
return next.sendTime < last.sendTime ? 1 : -1
})
.map(msg => {
const message = new Message()
message.fromChatMessage(msg)
return message
})
let isEnd = true
if (imui.getMessages(contact.id).length < res.totalCount) {
isEnd = false
}
next(messages, isEnd)
})
.catch(() => {
next(new Array<Message>(), true)
imui.messageViewToBottom()
})
}
private handleInitContracts() {
const myContracts = new Array<MyContract>()
this.myFriends
.forEach(friend => {
const myContract = new MyContract()
myContract.id = friend.friendId
myContract.displayName = friend.remarkName ?? friend.userName
myContracts.push(myContract)
})
const imui = this.$refs.IMUI as any
imui.initContacts(myContracts)
private handleMenuAvatarClick() {
console.log('Event:menu-avatar-click')
}
private onShowImDialog() {
this.showDialog = !this.showDialog
private handleMessageClick(e: any, key: any, message: any) {
console.log(e)
console.log(key)
console.log(message)
}
private handleReceiveMessage(chatMessage: ChatMessage) {
const message = new Message()
message.fromChatMessage(chatMessage)
const imui = this.$refs.IMUI as any
imui.appendMessage(message)
const currentContact = imui.currentContact
if (currentContact && currentContact.id === chatMessage.formUserId) {
currentContact.lastContent = chatMessage.content
currentContact.lastSendTime = new Date(chatMessage.sendTime).getTime()
} else {
imui.updateContact(chatMessage.formUserId, {
unread: '+1',
lastSendTime: new Date(chatMessage.sendTime).getTime(),
lastContent: chatMessage.content
})
}
}
private handleSendMessage(message: any, next: any, file: any) {
private handleSendMessage(message: Message, next: any, file: any) {
console.log(message, next, file)
const imui = this.$refs.IMUI as any
const chatMessage = new ChatMessage()
chatMessage.formUserId = message.fromUser.id
chatMessage.formUserName = message.fromUser.displayName
@ -117,6 +282,103 @@ export default class InstantMessage extends mixins(EventBusMiXin) {
next()
}, 1000)
})
.catch(() => {
imui
.updateMessage(message.id, message.toContactId, {
status: 'failed'
})
})
}
private onChangeContract(contract: any) {
const imui = this.$refs.IMUI as any
imui.updateContact(contract.id, {
unread: 0
})
imui.closeDrawer()
}
}
</script>
<style lang="scss" scoped>
.body {
background: #3d495c !important
}
.link {
padding: 15px 0
}
.link >>> .a {
display: inline-block;
font-size: 16px;
color: #ccd3dc;
text-decoration: none;
border-radius: 5px;
margin-right: 15px;
&:hover {
color: #85acda;
}
}
.action {
margin-top: 30px;
.button {
margin-right: 10px
}
}
.imui-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.drawer-content {
padding: 15px
}
.more {
font-size: 32px;
line-height: 18px;
height: 32px;
position: absolute;
top: 6px;
right: 14px;
cursor: pointer;
user-select: none;
color: #999;
&:active {
color: #000;
}
}
.bar {
text-align: center;
line-height: 30px;
background: #fff;
margin: 15px;
color: #666;
user-select: none;
font-size: 12px;
}
.cover {
text-align: center;
user-select: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
i {
font-size: 84px;
color: #e6e6e6;
}
p {
font-size: 18px;
color: #ddd;
line-height: 50px;
}
}
.article-item {
line-height: 34px;
cursor: pointer;
&:hover {
text-decoration: underline;
color: #318efd;
}
}
</style>

54
vueJs/src/components/Lemon-IMUI/components/Avatar.vue

@ -0,0 +1,54 @@
<template>
<span
:style="style"
class="lemon-avatar"
@click="onClick"
>
<i
v-if="imageFinishLoad"
:class="icon"
/>
<img
:src="src"
@load="onLoad"
>
</span>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
@Component({
name: 'LemonAvatar'
})
export default class extends Vue {
@Prop({ default: '' })
private src!: string
@Prop({ default: 'lemon-icon-people' })
private icon!: string
@Prop({ default: 32 })
private size!: number
get style() {
const size = `${this.size}px`
return {
width: size,
height: size,
lineHeight: size,
fontSize: `${this.size / 2}px`
}
}
private imageFinishLoad = true
private onClick(e: any) {
this.$emit('click', e)
}
private onLoad() {
this.imageFinishLoad = false
}
}
</script>

0
vueJs/src/components/Lemon-IMUI/index.vue

5
vueJs/src/mixins/DataListMiXin.ts

@ -41,6 +41,7 @@ export default class DataListMiXin extends Vue {
.then(res => {
this.dataList = res.items
this.dataTotal = res.items.length
this.onDataLoadCompleted()
})
.finally(() => {
this.dataLoading = false
@ -110,7 +111,9 @@ export default class DataListMiXin extends Vue {
}
/** 数据加载完毕事件 */
protected onDataLoadCompleted() {}
protected onDataLoadCompleted() {
this.dataLoading = false
}
/**
*

7
vueJs/src/mixins/EventBusMiXin.ts

@ -48,6 +48,13 @@ export default class EventBusMiXin extends Vue {
this.$events.off(name, undefined)
}
/**
*
*/
protected unSubscribeAll() {
this.$events.removeAll()
}
/**
*
* @param name

18
vueJs/src/views/dashboard/admin/index.vue

@ -1,29 +1,15 @@
<template>
<div class="dashboard-editor-container">
IM测试页
<el-button @click="onShowImDialogClick">
打开IM
</el-button>
<instant-message />
</div>
<div class="dashboard-editor-container" />
</template>
<script lang="ts">
import EventBusMiXin from '@/mixins/EventBusMiXin'
import Component, { mixins } from 'vue-class-component'
import InstantMessage from '@/components/InstantMessage/index.vue'
@Component({
name: 'DashboardAdmin',
components: {
InstantMessage
}
name: 'DashboardAdmin'
})
export default class extends mixins(EventBusMiXin) {
private onShowImDialogClick() {
this.trigger('onShowImDialog')
}
}
</script>

18
vueJs/src/views/dashboard/index.vue

@ -1,23 +1,31 @@
<template>
<div class="dashboard-container">
<component :is="currentRole" />
IM测试页
<el-button @click="onShowImDialogClick">
打开IM
</el-button>
<instant-message />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import EventBusMiXin from '@/mixins/EventBusMiXin'
import Component, { mixins } from 'vue-class-component'
import { UserModule } from '@/store/modules/user'
import AdminDashboard from './admin/index.vue'
import EditorDashboard from './editor/index.vue'
import InstantMessage from '@/components/InstantMessage/index.vue'
@Component({
name: 'Dashboard',
components: {
AdminDashboard,
EditorDashboard
EditorDashboard,
InstantMessage
}
})
export default class extends Vue {
export default class extends mixins(EventBusMiXin) {
private currentRole = 'admin-dashboard'
get roles() {
@ -29,5 +37,9 @@ export default class extends Vue {
this.currentRole = 'editor-dashboard'
}
}
private onShowImDialogClick() {
this.trigger('onShowImDialog')
}
}
</script>

Loading…
Cancel
Save