Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
foreign
/
customer-service
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
2b02de5c
authored
Jul 16, 2021
by
panjiangyi
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
成员名字
parent
803da04e
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
496 additions
and
478 deletions
components/message.vue
store/index.ts
utils/user-info.ts
components/message.vue
View file @
2b02de5c
<
template
>
<div
class=
"message-con d-flex"
:class=
"
isMyMessage
? 'my-message align-items-start flex-row-reverse'
: 'align-items-start'
"
>
<span
class=
"no-selection"
>
<avatar
:size=
"40"
class=
"msg-avatar"
:shape=
"shape"
:src=
"avatar"
/>
</span>
<div
class=
"msg-content"
>
<div
class=
"msg-name no-selection"
>
{{
username
}}
</div>
<!-- Image -->
<div
class=
"msg-detail image-message"
:class=
"
{ 'image-404': fileFailed2Load }"
v-if="messageType === 'image'"
@dblclick="openFile"
>
<img
v-if=
"messageRealUrl"
:src=
"messageRealUrl"
:title=
"messageBody.msg.name"
:alt=
"messageBody.msg.name"
/>
<file-icon
v-else-if=
"fileFailed2Load"
:value=
"image404"
></file-icon>
</div>
<!-- File -->
<div
class=
"msg-detail file-message d-flex"
v-else-if=
"messageType === 'file'"
@
dblclick=
"openFile"
>
<div
class=
"file-message-info"
>
<div
class=
"text-nowrap text-truncate file-message-name"
:title=
"messageBody.msg.name"
>
{{
messageBody
.
msg
.
name
}}
</div>
<div
class=
"text-hint"
>
{{
messageBody
.
msg
.
size
|
formatSize
}}
</div>
</div>
<file-icon
:value=
"fileIcon"
></file-icon>
</div>
<!-- Audio -->
<div
class=
"msg-detail voice-message d-flex align-items-center"
:class=
"
{ playing: playing, 'can-play': messageRealUrl }"
v-else-if="messageType === 'voice'"
@click.stop="play"
:style="{ width: getVoiceMessageWidth + 'px' }"
>
<div
class=
"d-flex align-items-center"
v-if=
"messageRealUrl"
>
<voice-icon
:loading=
"playing"
></voice-icon>
<audio
ref=
"audio"
@
play=
"onPlay"
@
pause=
"onPause"
>
<source
type=
"audio/aac"
:src=
"messageRealUrl"
/>
</audio>
<span
v-if=
"duration"
class=
"duration text-nowrap text-hint"
>
{{
durationInSecond
}}
s
</span
>
</div>
<i
class=
"el-icon-warning-outline"
v-else-if=
"fileFailed2Load"
title=
"[语音加载失败]"
></i>
</div>
<!-- Video -->
<div
class=
"
<div
class=
"message-con d-flex align-items-center"
:class=
"isMyMessage ? 'my-message flex-row-reverse' : ''"
>
<div
class=
"msg-content"
>
<div
class=
"msg-name no-selection"
>
{{
senderName
}}
</div>
<!-- Image -->
<div
class=
"msg-detail image-message"
:class=
"
{ 'image-404': fileFailed2Load }"
v-if="messageType === 'image'"
@dblclick="openFile"
>
<img
v-if=
"messageRealUrl"
:src=
"messageRealUrl"
:title=
"messageBody.msg.name"
:alt=
"messageBody.msg.name"
/>
<file-icon
v-else-if=
"fileFailed2Load"
:value=
"image404"
></file-icon>
</div>
<!-- File -->
<div
class=
"msg-detail file-message d-flex"
v-else-if=
"messageType === 'file'"
@
dblclick=
"openFile"
>
<div
class=
"file-message-info"
>
<div
class=
"text-nowrap text-truncate file-message-name"
:title=
"messageBody.msg.name"
>
{{
messageBody
.
msg
.
name
}}
</div>
<div
class=
"text-hint"
>
{{
messageBody
.
msg
.
size
|
formatSize
}}
</div>
</div>
<file-icon
:value=
"fileIcon"
></file-icon>
</div>
<!-- Audio -->
<div
class=
"msg-detail voice-message d-flex align-items-center"
:class=
"
{ playing: playing, 'can-play': messageRealUrl }"
v-else-if="messageType === 'voice'"
@click.stop="play"
:style="{ width: getVoiceMessageWidth + 'px' }"
>
<div
class=
"d-flex align-items-center"
v-if=
"messageRealUrl"
>
<voice-icon
:loading=
"playing"
></voice-icon>
<audio
ref=
"audio"
@
play=
"onPlay"
@
pause=
"onPause"
>
<source
type=
"audio/aac"
:src=
"messageRealUrl"
/>
</audio>
<span
v-if=
"duration"
class=
"duration text-nowrap text-hint"
>
{{
durationInSecond
}}
s
</span
>
</div>
<i
class=
"el-icon-warning-outline"
v-else-if=
"fileFailed2Load"
title=
"[语音加载失败]"
></i>
</div>
<!-- Video -->
<div
class=
"
msg-detail
video-message
d-flex
align-items-center
justify-content-center
"
v-else-if=
"messageType === 'video'"
>
<video-player-icon
@
click
.
native=
"openFile"
></video-player-icon>
</div>
<!-- Text -->
<div
class=
"msg-detail inline-text"
v-else
v-html=
"format2Link(messageBody.msg.text || emptyText)"
></div>
</div>
v-else-if=
"messageType === 'video'"
>
<video-player-icon
@
click
.
native=
"openFile"
></video-player-icon>
</div>
<!-- Text -->
<div
class=
"msg-detail inline-text"
v-else
v-html=
"format2Link(messageBody.msg.text || emptyText)"
></div>
</div>
<a
v-if=
"!isSendingMessage && messageType === 'file'"
class=
"
<a
v-if=
"!isSendingMessage && messageType === 'file'"
class=
"
d-flex
align-items-center
justify-content-center
download-icon
"
:href=
"messageRealUrl | downloadUrl(getAttachment)"
:download=
"getAttachment"
title=
"下载文件"
>
<img
src=
"~@/customer-service/imgs/download.png"
alt=
"Download"
/>
</a>
<i
class=
"el-icon-warning text-danger"
v-if=
"failed"
title=
"发送失败"
></i>
<i
class=
"el-icon-loading"
v-else-if=
"isSendingMessage"
></i>
<template
v-if=
"showReadSummary"
>
<div
v-if=
"isMyMessage"
class=
"msg-read pos-rel"
>
<span
@
click=
"readListVisibility = true"
class=
"pointer"
>
<template
v-if=
"isAllRead"
>
全部已读
</
template
>
<
template
v-else-if=
"data.read_count > 0"
>
{{
data
.
read_count
}}
人已读
</
template
>
<
template
v-else
>
未读
</
template
>
</span>
<who-read-list
v-if=
"readListVisibility"
@
blur=
"readListVisibility = false"
:msgId=
"data.id"
/>
</div>
</template>
</div>
:href=
"messageRealUrl | downloadUrl(getAttachment)"
:download=
"getAttachment"
title=
"下载文件"
>
<img
src=
"~@/customer-service/imgs/download.png"
alt=
"Download"
/>
</a>
<i
class=
"el-icon-warning text-danger"
v-if=
"failed"
title=
"发送失败"
></i>
<i
class=
"el-icon-loading"
v-else-if=
"isSendingMessage"
></i>
<template
v-if=
"showReadSummary"
>
<div
v-if=
"isMyMessage"
class=
"msg-read pos-rel"
>
<span
@
click=
"readListVisibility = true"
class=
"pointer"
>
<template
v-if=
"isAllRead"
>
全部已读
</
template
>
<
template
v-else-if=
"data.read_count > 0"
>
{{
data
.
read_count
}}
人已读
</
template
>
<
template
v-else
>
未读
</
template
>
</span>
<who-read-list
v-if=
"readListVisibility"
@
blur=
"readListVisibility = false"
:msgId=
"data.id"
/>
</div>
</template>
</div>
</template>
<
script
lang=
"ts"
>
import
{
Component
,
Inject
,
Mixins
,
Prop
,
Ref
}
from
"vue-property-decorator"
;
import
{
Filters
}
from
'../mixin/filter'
;
import
{
Filters
}
from
"../mixin/filter"
;
import
*
as
dto
from
"../model"
;
import
{
isAccessibleUrl
}
from
"../service/tools"
;
import
{
replaceText2Link
}
from
"../utils"
;
import
{
getUserInfo
}
from
"../utils/user-info"
;
import
chat
from
"./../xim"
;
import
{
FileType
,
getFileType
,
isAudio
,
isImage
,
isVideo
,
MAX_FILE_SIZE
,
MAX_IMAGE_SIZE
,
FileType
,
getFileType
,
isAudio
,
isImage
,
isVideo
,
MAX_FILE_SIZE
,
MAX_IMAGE_SIZE
,
}
from
"./file-controller"
;
import
FileIcon
from
"./file-icon.vue"
;
import
VideoPlayerIcon
from
"./video-player-icon.vue"
;
...
...
@@ -156,460 +156,459 @@ import avatar from "@/customer-service/components/avatar.vue";
import
{
chatStore
,
ChatStore
}
from
"@/customer-service/store/model"
;
@
Component
({
components
:
{
FileIcon
,
VoiceIcon
,
WhoReadList
,
VideoPlayerIcon
,
avatar
},
components
:
{
FileIcon
,
VoiceIcon
,
WhoReadList
,
VideoPlayerIcon
,
avatar
},
})
export
default
class
Message
extends
Mixins
(
Filters
)
{
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_MY_ID
)
private
readonly
chatMyId
!
:
ChatStore
.
STATE_CHAT_MY_ID
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_MY_ID
)
private
readonly
chatMyId
!
:
ChatStore
.
STATE_CHAT_MY_ID
;
@
chatStore
.
Getter
(
ChatStore
.
GETTER_CURRENT_CHAT_PRESENT_MEMBERS
)
private
readonly
chatMembers
!
:
ChatStore
.
GETTER_CURRENT_CHAT_PRESENT_MEMBERS
;
@
chatStore
.
Getter
(
ChatStore
.
GETTER_CURRENT_CHAT_PRESENT_MEMBERS
)
private
readonly
chatMembers
!
:
ChatStore
.
GETTER_CURRENT_CHAT_PRESENT_MEMBERS
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_SOURCE
)
private
readonly
chatSource
!
:
ChatStore
.
STATE_CHAT_SOURCE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_SOURCE
)
private
readonly
chatSource
!
:
ChatStore
.
STATE_CHAT_SOURCE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
)
private
readonly
chatId
!
:
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
)
private
readonly
chatId
!
:
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
;
/**
* tbd: 文件消息所在的域名的url,逻辑需要补充
*/
private
messageRealUrl
=
""
;
private
fileFailed2Load
=
false
;
private
image404
=
FileType
.
Image_404
;
private
loadingRealUrl
=
false
;
/**
* tbd: 文件消息所在的域名的url,逻辑需要补充
*/
private
messageRealUrl
=
""
;
private
fileFailed2Load
=
false
;
private
image404
=
FileType
.
Image_404
;
private
loadingRealUrl
=
false
;
private
senderName
=
""
;
@
Prop
({
type
:
Object
,
default
:
()
=>
Object
.
create
(
null
)
})
private
data
!
:
dto
.
Message
;
@
Prop
({
type
:
Object
,
default
:
()
=>
Object
.
create
(
null
)
})
private
data
!
:
dto
.
Message
;
@
Prop
()
private
isSendingMessage
!
:
boolean
;
@
Prop
()
private
isSendingMessage
!
:
boolean
;
@
Prop
()
private
failed
!
:
boolean
;
@
Prop
()
private
failed
!
:
boolean
;
@
Prop
({
default
:
"circle"
})
private
shape
!
:
string
;
@
Prop
({
default
:
"circle"
})
private
shape
!
:
string
;
@
Ref
(
"audio"
)
private
readonly
audioRef
!
:
HTMLAudioElement
;
@
Ref
(
"audio"
)
private
readonly
audioRef
!
:
HTMLAudioElement
;
private
playing
=
false
;
private
playing
=
false
;
@
Inject
({
default
:
false
})
readonly
showReadSummary
!
:
boolean
;
@
Inject
({
default
:
false
})
readonly
showReadSummary
!
:
boolean
;
private
emptyText
=
" "
;
private
emptyText
=
" "
;
private
readListVisibility
=
false
;
private
readListVisibility
=
false
;
private
org
=
""
;
private
org
=
""
;
private
get
isAllRead
()
{
return
this
.
data
.
read_count
>=
this
.
data
.
total_read_count
;
}
private
get
messageBody
():
{
eid
?:
string
;
oid
?:
string
;
msg
:
any
}
{
if
(
this
.
data
)
{
try
{
return
{
...
this
.
data
,
msg
:
JSON
.
parse
(
this
.
data
.
msg
)
};
}
catch
{
return
{
...
this
.
data
,
msg
:
JSON
.
parse
(
this
.
data
.
msg
.
replace
(
/
\n
/g
,
"\\n"
)),
};
}
private
get
isAllRead
()
{
return
this
.
data
.
read_count
>=
this
.
data
.
total_read_count
;
}
return
{
msg
:
{
text
:
""
}
};
}
private
get
messageBody
():
{
eid
?:
string
;
oid
?:
string
;
msg
:
any
}
{
if
(
this
.
data
)
{
try
{
return
{
...
this
.
data
,
msg
:
JSON
.
parse
(
this
.
data
.
msg
)
};
}
catch
{
return
{
...
this
.
data
,
msg
:
JSON
.
parse
(
this
.
data
.
msg
.
replace
(
/
\n
/g
,
"\\n"
)),
};
}
}
private
get
getAttachment
()
{
if
(
this
.
messageBody
)
{
return
this
.
messageBody
.
msg
.
name
;
return
{
msg
:
{
text
:
""
}
};
}
return
"文件下载"
;
}
private
get
isMyMessage
()
{
if
(
this
.
isSendingMessage
)
{
return
true
;
private
get
getAttachment
()
{
if
(
this
.
messageBody
)
{
return
this
.
messageBody
.
msg
.
name
;
}
return
"文件下载"
;
}
const
senderEid
=
this
.
messageBody
.
eid
;
if
(
this
.
messageBody
)
{
const
msg
=
this
.
messageBody
;
if
(
this
.
chatSource
)
{
const
source
=
msg
.
msg
.
source
;
if
(
source
)
{
return
(
source
===
this
.
chatSource
&&
senderEid
===
this
.
chatMyId
);
private
get
isMyMessage
()
{
if
(
this
.
isSendingMessage
)
{
return
true
;
}
if
(
this
.
org
&&
this
.
messageBody
.
oid
)
{
return
(
this
.
messageBody
.
oid
===
this
.
org
&&
senderEid
===
this
.
chatMyId
);
const
senderEid
=
this
.
messageBody
.
eid
;
if
(
this
.
messageBody
)
{
const
msg
=
this
.
messageBody
;
if
(
this
.
chatSource
)
{
const
source
=
msg
.
msg
.
source
;
if
(
source
)
{
return
(
source
===
this
.
chatSource
&&
senderEid
===
this
.
chatMyId
);
}
if
(
this
.
org
&&
this
.
messageBody
.
oid
)
{
return
(
this
.
messageBody
.
oid
===
this
.
org
&&
senderEid
===
this
.
chatMyId
);
}
return
false
;
}
return
senderEid
===
this
.
chatMyId
;
}
return
false
;
}
}
return
senderEid
===
this
.
chatMyId
;
public
created
()
{
this
.
getUserName
(
this
.
data
.
eid
);
}
return
false
;
}
private
get
username
()
{
const
avatar
:
any
=
chat
.
getUserMapping
();
if
(
this
.
data
==
null
)
return
""
;
if
(
avatar
==
null
)
return
this
.
data
.
id
;
const
value
=
avatar
[
this
.
data
.
eid
];
if
(
value
==
null
)
return
""
;
if
(
value
.
name
==
null
)
return
""
;
return
value
.
name
;
}
private
get
avatar
()
{
const
avatar
=
chat
.
getUserMapping
();
if
(
this
.
isSendingMessage
)
{
if
(
avatar
&&
this
.
chatMyId
)
{
const
user
=
avatar
[
this
.
chatMyId
];
if
(
user
&&
user
.
avatar
)
{
return
user
.
avatar
;
}
}
private
async
getUserName
(
eid
:
string
)
{
const
data
=
await
getUserInfo
(
eid
);
this
.
senderName
=
data
.
name
;
}
if
(
avatar
&&
this
.
data
)
{
const
value
=
avatar
[
this
.
data
.
eid
];
if
(
value
&&
value
.
avatar
)
{
return
value
.
avatar
;
}
private
get
avatar
()
{
const
avatar
=
chat
.
getUserMapping
();
if
(
this
.
isSendingMessage
)
{
if
(
avatar
&&
this
.
chatMyId
)
{
const
user
=
avatar
[
this
.
chatMyId
];
if
(
user
&&
user
.
avatar
)
{
return
user
.
avatar
;
}
}
}
if
(
avatar
&&
this
.
data
)
{
const
value
=
avatar
[
this
.
data
.
eid
];
if
(
value
&&
value
.
avatar
)
{
return
value
.
avatar
;
}
}
return
""
;
}
return
""
;
}
private
get
fileIcon
()
{
if
(
this
.
data
)
{
return
getFileType
(
this
.
messageBody
.
msg
.
name
);
}
private
get
fileIcon
()
{
if
(
this
.
data
)
{
return
getFileType
(
this
.
messageBody
.
msg
.
name
);
return
FileType
.
Others
;
}
return
FileType
.
Others
;
}
private
get
messageType
()
{
const
type
=
this
.
data
?.
type
;
if
(
type
===
"file"
)
{
const
name
=
this
.
messageBody
?.
msg
.
name
;
if
(
name
)
{
const
size
=
this
.
messageBody
?.
msg
.
size
;
if
(
size
)
{
const
outImageSize
=
size
>
MAX_IMAGE_SIZE
;
if
(
!
outImageSize
&&
isImage
(
name
))
{
return
"image"
;
}
const
outSize
=
size
>
MAX_FILE_SIZE
;
if
(
!
outSize
)
{
if
(
isAudi
o
(
name
))
{
return
"voice
"
;
}
if
(
isVideo
(
name
))
{
return
"video"
;
private
get
messageType
()
{
const
type
=
this
.
data
?.
type
;
if
(
type
===
"file"
)
{
const
name
=
this
.
messageBody
?.
msg
.
name
;
if
(
name
)
{
const
size
=
this
.
messageBody
?.
msg
.
size
;
if
(
size
)
{
const
outImageSize
=
size
>
MAX_IMAGE_SIZE
;
if
(
!
outImageSize
&&
isImage
(
name
))
{
return
"image"
;
}
const
outSize
=
size
>
MAX_FILE_SIZE
;
if
(
!
outSize
)
{
if
(
isAudio
(
name
))
{
return
"voice"
;
}
if
(
isVide
o
(
name
))
{
return
"video
"
;
}
}
}
}
}
}
}
}
return
this
.
data
?.
type
;
}
// 图片的样式设置,通过js偶尔会有高度计算不准确, 直接通过css的处理目前看可以达到对应效果
// private get imageStyle() {
// if (this.messageBody == null) return {};
// if (this.messageBody.msg.w == null) return {};
// if (this.messageBody.msg.h == null) return {};
// const w = this.messageBody.msg.w;
// const h = this.messageBody.msg.h;
// const maxWidth = 300;
// const maxHeight = maxWidth * (h / w);
// return { width: `${w}px`, height: `${h}px`, maxHeight: `${maxHeight}px` };
// }
private
get
duration
()
{
const
v
=
this
.
messageBody
.
msg
.
duration
as
number
;
return
v
||
0
;
}
private
get
durationInSecond
()
{
return
Math
.
round
(
this
.
duration
/
1000
);
}
private
get
getVoiceMessageWidth
()
{
if
(
this
.
fileFailed2Load
)
{
return
35
;
return
this
.
data
?.
type
;
}
const
d
=
this
.
duration
/
1000
;
if
(
d
<=
3
)
{
return
60
;
// 图片的样式设置,通过js偶尔会有高度计算不准确, 直接通过css的处理目前看可以达到对应效果
// private get imageStyle() {
// if (this.messageBody == null) return {};
// if (this.messageBody.msg.w == null) return {};
// if (this.messageBody.msg.h == null) return {};
// const w = this.messageBody.msg.w;
// const h = this.messageBody.msg.h;
// const maxWidth = 300;
// const maxHeight = maxWidth * (h / w);
// return { width: `${w}px`, height: `${h}px`, maxHeight: `${maxHeight}px` };
// }
private
get
duration
()
{
const
v
=
this
.
messageBody
.
msg
.
duration
as
number
;
return
v
||
0
;
}
if
(
d
>=
60
)
{
return
200
;
private
get
durationInSecond
(
)
{
return
Math
.
round
(
this
.
duration
/
1000
)
;
}
return
60
+
d
;
}
private
get
getVoiceMessageWidth
()
{
if
(
this
.
fileFailed2Load
)
{
return
35
;
}
const
d
=
this
.
duration
/
1000
;
if
(
d
<=
3
)
{
return
60
;
}
private
isCustomer
(
)
{
return
!
this
.
showReadSummary
;
}
if
(
d
>=
60
)
{
return
200
;
}
private
buildMessageUrl
()
{
if
(
this
.
messageRealUrl
||
this
.
loadingRealUrl
)
{
return
;
}
const
url
=
this
.
messageBody
.
msg
.
url
as
string
;
if
(
url
)
{
// const service = XimService.getInstance()
if
(
isAccessibleUrl
(
url
))
{
return
(
this
.
messageRealUrl
=
url
);
}
this
.
loadingRealUrl
=
true
;
// service
// .getFileUrlById(url, this.chatId || 0, this.isCustomer())
// .then((data) => {
// if (data && data.itemList) {
// this.messageRealUrl = data.itemList[0].url
// } else {
// this.fileFailed2Load = true
// }
// })
// .catch(() => (this.fileFailed2Load = true))
// .finally(() => (this.loadingRealUrl = false))
}
else
{
this
.
fileFailed2Load
=
true
;
return
60
+
d
;
}
}
private
openFile
()
{
if
(
this
.
isSendingMessage
)
{
return
;
private
isCustomer
()
{
return
!
this
.
showReadSummary
;
}
if
(
this
.
failed
||
this
.
fileFailed2Load
)
{
return
;
private
buildMessageUrl
()
{
if
(
this
.
messageRealUrl
||
this
.
loadingRealUrl
)
{
return
;
}
const
url
=
this
.
messageBody
.
msg
.
url
as
string
;
if
(
url
)
{
// const service = XimService.getInstance()
if
(
isAccessibleUrl
(
url
))
{
return
(
this
.
messageRealUrl
=
url
);
}
this
.
loadingRealUrl
=
true
;
// service
// .getFileUrlById(url, this.chatId || 0, this.isCustomer())
// .then((data) => {
// if (data && data.itemList) {
// this.messageRealUrl = data.itemList[0].url
// } else {
// this.fileFailed2Load = true
// }
// })
// .catch(() => (this.fileFailed2Load = true))
// .finally(() => (this.loadingRealUrl = false))
}
else
{
this
.
fileFailed2Load
=
true
;
}
}
const
copy
=
{
...
this
.
messageBody
.
msg
};
if
(
this
.
messageRealUrl
)
{
copy
.
url
=
this
.
messageRealUrl
;
private
openFile
()
{
if
(
this
.
isSendingMessage
)
{
return
;
}
if
(
this
.
failed
||
this
.
fileFailed2Load
)
{
return
;
}
const
copy
=
{
...
this
.
messageBody
.
msg
};
if
(
this
.
messageRealUrl
)
{
copy
.
url
=
this
.
messageRealUrl
;
}
this
.
$emit
(
"open"
,
{
type
:
this
.
messageType
,
msg
:
copy
});
}
this
.
$emit
(
"open"
,
{
type
:
this
.
messageType
,
msg
:
copy
});
}
private
play
()
{
if
(
this
.
audioRef
?.
paused
)
{
this
.
audioRef
?.
load
();
this
.
audioRef
?.
play
();
}
else
{
this
.
audioRef
?.
pause
();
private
play
()
{
if
(
this
.
audioRef
?.
paused
)
{
this
.
audioRef
?.
load
();
this
.
audioRef
?.
play
();
}
else
{
this
.
audioRef
?.
pause
();
}
}
}
private
onPlay
()
{
this
.
playing
=
true
;
}
private
onPlay
()
{
this
.
playing
=
true
;
}
private
onPause
()
{
this
.
playing
=
false
;
}
private
onPause
()
{
this
.
playing
=
false
;
}
private
format2Link
(
text
:
string
)
{
return
replaceText2Link
(
text
);
}
private
format2Link
(
text
:
string
)
{
return
replaceText2Link
(
text
);
}
}
</
script
>
<
style
lang=
"less"
scoped
>
.message-con
{
margin
:
20px
0
;
margin
:
20px
0
;
&.my-message
{
.msg-avatar
{
margin-right
:
0
;
margin-left
:
10px
;
}
&.my-message
{
.msg-avatar
{
margin-right
:
0
;
margin-left
:
10px
;
}
.msg-name
{
display
:
none
;
}
.msg-detail
{
margin-top
:
0
;
background-color
:
#dbf2ff
;
border-radius
:
8px
0
8px
8px
;
.msg-detail
{
margin-top
:
0
;
background-color
:
#dbf2ff
;
border-radius
:
8px
0
8px
8px
;
&.
image-message
:
not
(.
image-404
),
&
.
file-message
{
background-color
:
transparent
;
border-radius
:
4px
;
border
:
1px
solid
#c5d4e5
;
}
&.
image-message
:
not
(.
image-404
),
&
.
file-message
{
background-color
:
transparent
;
border-radius
:
4px
;
border
:
1px
solid
#c5d4e5
;
}
&
.voice-message
{
>
div
{
flex-flow
:
row-reverse
;
}
&
.voice-message
{
>
div
{
flex-flow
:
row-reverse
;
}
svg
{
transform
:
rotateY
(
180deg
);
}
}
svg
{
transform
:
rotateY
(
180deg
);
&
.video-message
{
background-color
:
#000
;
border-radius
:
0
;
}
}
}
&
.video-message
{
background-color
:
#000
;
border-radius
:
0
;
}
}
.msg-read
{
display
:
inline-block
;
color
:
#bfe1ff
;
margin-right
:
15px
;
user-select
:
none
;
flex
:
none
;
}
.msg-read
{
display
:
inline-block
;
color
:
#bfe1ff
;
margin-right
:
15px
;
user-select
:
none
;
flex
:
none
;
.download-icon
{
margin-right
:
15px
;
margin-left
:
0
;
margin-top
:
0
;
}
}
.download-icon
{
margin-right
:
15
px
;
margin-left
:
0
;
margin-top
:
0
;
>
i
{
height
:
16
px
;
font-size
:
16px
;
margin-right
:
10px
;
}
}
>
i
{
height
:
16px
;
font-size
:
16px
;
margin-right
:
10px
;
}
}
.msg-avatar
{
margin-right
:
10px
;
flex
:
40px
0
0
;
margin-right
:
10px
;
flex
:
40px
0
0
;
}
i
.msg-avatar
{
font-size
:
30px
;
background-color
:
#c0c4cc
;
border-radius
:
4px
;
width
:
40px
;
height
:
40px
;
&:before
{
position
:
relative
;
left
:
5px
;
top
:
5px
;
color
:
#fff
;
}
font-size
:
30px
;
background-color
:
#c0c4cc
;
border-radius
:
4px
;
width
:
40px
;
height
:
40px
;
&:before
{
position
:
relative
;
left
:
5px
;
top
:
5px
;
color
:
#fff
;
}
}
.msg-name
{
font-size
:
12px
;
color
:
#888
;
font-size
:
14px
;
color
:
#888
;
text-align
:
right
;
margin-bottom
:
3px
;
}
.msg-detail
{
margin-top
:
10px
;
font-size
:
14px
;
line-height
:
20px
;
background
:
#f5f6fa
;
border-radius
:
0px
8px
8px
;
padding
:
10px
;
word-break
:
break-word
;
&.image-message,
&.file-message
{
background-color
:
transparent
;
border-radius
:
4px
;
border
:
1px
solid
#c5d4e5
;
}
margin-top
:
10px
;
font-size
:
14px
;
line-height
:
20px
;
background
:
#f5f6fa
;
border-radius
:
0px
8px
8px
;
padding
:
10px
;
word-break
:
break-word
;
&.image-message,
&.file-message
{
background-color
:
transparent
;
border-radius
:
4px
;
border
:
1px
solid
#c5d4e5
;
}
&
.image-message
{
line-height
:
1
;
&
.image-message
{
line-height
:
1
;
&.image-404
{
background
:
#f7f8fa
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
&.image-404
{
background
:
#f7f8fa
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
.file-icon
{
margin
:
0
;
}
.file-icon
{
margin
:
0
;
}
}
}
}
&
.voice-message
{
height
:
40px
;
width
:
200px
;
&
.voice-message
{
height
:
40px
;
width
:
200px
;
&.can-play
{
cursor
:
pointer
;
}
&.can-play
{
cursor
:
pointer
;
}
i
{
font-size
:
16px
;
i
{
font-size
:
16px
;
}
}
}
&
.video-message
{
height
:
160px
;
width
:
200px
;
background-color
:
#000
;
border-radius
:
0
;
&
.video-message
{
height
:
160px
;
width
:
200px
;
background-color
:
#000
;
border-radius
:
0
;
svg
{
cursor
:
pointer
;
svg
{
cursor
:
pointer
;
}
}
}
&
.inline-text
{
display
:
inline-block
;
}
&
.inline-text
{
display
:
inline-block
;
}
.file-message-name
{
max-width
:
130px
;
}
.file-message-name
{
max-width
:
130px
;
}
img
{
max-width
:
300px
;
}
img
{
max-width
:
300px
;
}
}
.download-icon
{
cursor
:
pointer
;
text-decoration
:
none
;
margin-left
:
15px
;
margin-top
:
42px
;
cursor
:
pointer
;
text-decoration
:
none
;
margin-left
:
15px
;
margin-top
:
42px
;
i
{
color
:
#fff
;
font-size
:
14px
;
}
i
{
color
:
#fff
;
font-size
:
14px
;
}
}
.no-selection
{
user-select
:
none
;
user-select
:
none
;
}
.image-message
{
max-width
:
300px
;
box-sizing
:
content-box
;
img
{
width
:
100%
;
}
max-width
:
300px
;
box-sizing
:
content-box
;
img
{
width
:
100%
;
}
}
</
style
>
store/index.ts
View file @
2b02de5c
...
...
@@ -5,6 +5,7 @@ import { ChatMember } from "../model";
import
{
isAccessibleUrl
}
from
"../service/tools"
;
import
{
unique
}
from
"../utils"
;
import
{
decode
}
from
"../utils/jwt"
;
import
{
getUserInfo
}
from
"../utils/user-info"
;
import
Chat
from
"../xim"
;
import
chatType
from
"../xim/chat-type"
;
import
xim
,
{
ChatNotifyListener
}
from
"../xim/xim"
;
...
...
@@ -548,14 +549,10 @@ export default {
chatMembers
.
map
(
async
(
member
)
=>
{
let
result
:
NonNullable
<
ChatStore
.
STATE_CURRENT_CHAT_MEMBERS
>
[
number
];
try
{
const
info
=
await
sdk
()
.
model
(
"user"
)
.
detail
(
member
.
eid
)
.
query
();
const
data
=
await
getUserInfo
(
member
.
eid
);
result
=
{
...
member
,
name
:
info
.
row
.
first_name
.
value
as
string
,
phone
:
info
.
row
.
last_name
.
value
as
string
,
...
data
,
};
}
catch
(
error
)
{
// eslint-disable-next-line no-console
...
...
@@ -621,7 +618,7 @@ export default {
});
detailManager
.
done
();
await
action
.
execute
();
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
500
));
await
new
Promise
(
(
resolve
)
=>
setTimeout
(
resolve
,
500
));
await
dispatch
(
ChatStore
.
ACTION_GET_CHAT_MEMBERS
);
},
},
...
...
utils/user-info.ts
0 → 100644
View file @
2b02de5c
import
Chat
from
"../xim"
;
export
type
UserMapping
=
{
[
eid
:
string
]:
{
name
:
string
;
phone
:
string
;
};
};
const
userMapping
:
UserMapping
=
{};
export
const
getUserMapping
=
()
=>
userMapping
;
export
async
function
getUserInfo
(
eid
:
string
)
{
if
(
userMapping
[
eid
]
!=
null
)
return
userMapping
[
eid
];
const
info
=
await
Chat
.
getSdk
().
model
(
"user"
).
detail
(
eid
).
query
();
const
data
=
{
name
:
info
.
row
.
first_name
.
value
as
string
,
phone
:
info
.
row
.
last_name
.
value
as
string
,
};
userMapping
[
eid
]
=
data
;
return
data
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment