Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
提交反馈
为 GitLab 提交贡献
登录
切换导航
P
pica-admin-consultation
项目
项目
详情
动态
版本
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
收起侧边栏
Close sidebar
动态
分支图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
com.pica.cloud.education.frontend
pica-admin-consultation
提交
9fd3e21c
提交
9fd3e21c
编写于
7月 28, 2021
作者:
fusheng.liu
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'develop' into feature/fs
上级
75af5556
87b89396
变更
10
隐藏空白字符变更
内嵌
并排
正在显示
10 个修改的文件
包含
2629 行增加
和
35 行删除
+2629
-35
App.vue
src/App.vue
+12
-3
chat.scss
src/components/IM/chat.scss
+447
-0
chat.vue
src/components/IM/chat.vue
+887
-0
diagnosis-live.vue
src/components/IM/diagnosis-live.vue
+781
-0
user-info1.vue
src/components/IM/user-info1.vue
+0
-0
RtcClient.js
src/utils/RtcClient.js
+429
-0
RtcClient.js
src/utils/live/RtcClient.js
+0
-0
utils.js
src/utils/utils.js
+47
-9
diagnosis-live.vue
src/views/IM/diagnosis-admin/diagnosis-live.vue
+14
-15
workbench.vue
src/views/IM/diagnosis-admin/workbench.vue
+12
-8
未找到文件。
src/App.vue
浏览文件 @
9fd3e21c
...
@@ -15,17 +15,20 @@
...
@@ -15,17 +15,20 @@
<v-footer></v-footer>
<v-footer></v-footer>
</div>
</div>
</el-container>
</el-container>
<chat
:showChat=
"showChat"
:currentUser=
"
{}">
</chat>
</div>
</div>
</
template
>
</
template
>
<
script
>
<
script
>
import
VHeader
from
"./views/layout/header.vue"
;
import
VHeader
from
"./views/layout/header.vue"
;
import
VSlidebar
from
"./views/layout/slidebar.vue"
;
import
VSlidebar
from
"./views/layout/slidebar.vue"
;
import
VFooter
from
"./views/layout/footer.vue"
;
import
VFooter
from
"./views/layout/footer.vue"
;
import
chat
from
'./components/IM/chat'
import
{
import
{
base64decode
,
base64decode
,
isNotEmptyUtils
,
isNotEmptyUtils
,
getUrlParamsMap
,
getUrlParamsMap
,
ssoLogin
ssoLogin
,
bindDragHeader
}
from
"./utils/utils.js"
;
}
from
"./utils/utils.js"
;
import
{
mapActions
,
mapGetters
}
from
"vuex"
;
import
{
mapActions
,
mapGetters
}
from
"vuex"
;
import
{
getLoginUrl
,
getInnerLoginUrl
}
from
"./utils/index.js"
;
import
{
getLoginUrl
,
getInnerLoginUrl
}
from
"./utils/index.js"
;
...
@@ -34,7 +37,8 @@ export default {
...
@@ -34,7 +37,8 @@ export default {
components
:
{
components
:
{
VHeader
,
VHeader
,
VSlidebar
,
VSlidebar
,
VFooter
VFooter
,
chat
},
},
data
()
{
data
()
{
return
{
return
{
...
@@ -42,6 +46,7 @@ export default {
...
@@ -42,6 +46,7 @@ export default {
userName
:
""
,
userName
:
""
,
authList
:
[],
authList
:
[],
systemType
:
0
,
systemType
:
0
,
showChat
:
false
};
};
},
},
computed
:
{
computed
:
{
...
@@ -51,7 +56,11 @@ export default {
...
@@ -51,7 +56,11 @@ export default {
vm
=
this
;
vm
=
this
;
vm
.
getToken
();
vm
.
getToken
();
},
},
mounted
()
{},
mounted
()
{
setTimeout
(
function
()
{
bindDragHeader
(
'.c-header'
,
'.chat-wrap'
);
},
1000
)
},
methods
:
{
methods
:
{
// 解密token
// 解密token
getToken
()
{
getToken
()
{
...
...
src/components/IM/chat.scss
0 → 100644
浏览文件 @
9fd3e21c
.chat-wrap
{
position
:
absolute
;
top
:
20px
;
right
:
20px
;
//min-height: 900px;
min-width
:
700px
;
z-index
:
1000
;
border
:
1px
solid
#8fa4ac
;
.component-content
{
display
:
flex
;
flex-direction
:
row
;
// min-width: 1200px;
background
:
none
!
important
;
font-size
:
14px
;
height
:
100%
;
.center
{
//margin-left: 10%;
//margin-right: 10%;
flex
:
1
;
background
:
#fff
;
.c-header
{
padding
:
0
25px
;
height
:
56px
;
display
:
flex
;
flex-direction
:
row
;
justify-content
:
space-between
;
align-items
:
center
;
border-bottom
:
2px
solid
#f5f5f5
;
&
>
.c-header-l
{
flex
:
1
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
&
>
img
{
display
:
inline-block
;
width
:
24px
;
height
:
24px
;
border-radius
:
12px
;
margin-right
:
8px
;
}
&
>
.name
{
margin-right
:
25px
;
color
:
#333333
;
}
&
>
.time-tip
{
font-size
:
12px
;
color
:
#999999
;
}
}
.close-btn
{
width
:
100px
;
color
:
#c7c8c9
;
cursor
:
pointer
;
}
}
.msg-content
{
// width: 100%;
// height: 650px;
word-break
:
break-all
;
overflow
:
scroll
;
display
:
flex
;
flex-direction
:
column
;
margin
:
10px
20px
;
.msg-item
{
display
:
flex
;
flex-direction
:
row
;
margin
:
12px
0
;
.msg-item-img
{
width
:
36px
;
height
:
36px
;
margin-right
:
12px
;
&
>
img
{
width
:
100%
;
height
:
100%
;
border-radius
:
22px
;
}
}
.msg-item-detail
{
display
:
flex
;
flex-direction
:
column
;
text-align
:
left
;
font-size
:
13px
;
&
>
:first-child
{
color
:
#999999
;
margin-bottom
:
8px
;
}
.send-warpper
{
// display: flex;
// flex-direction: row;
// align-items: center;
&
>
.icon
{
float
:
left
;
width
:
20px
;
height
:
20px
;
margin-right
:
10px
;
margin-top
:
10px
;
}
.mid-text-wrapper
{
display
:
inline-block
;
// display: flex;
// flex-direction: row;
// align-items: center;
// & > img {
// width: 18px;
// height: 18px;
// margin-right: 8px;
// cursor: pointer;
// }
}
.mid-text
{
padding
:
12px
16px
;
display
:
inline-block
;
max-width
:
520px
;
border-radius
:
8px
;
background
:
#ebf5fc
;
text-align
:
justify
;
color
:
#333333
;
&
.no-support
{
display
:
flex
;
align-items
:
center
;
&
>
img
{
width
:
14px
;
height
:
14px
;
margin-right
:
3px
;
}
}
}
.link
{
color
:
#2f86f6
;
}
&
>
.mid-pdf
{
width
:
260px
;
display
:
flex
;
flex-direction
:
row
;
justify-content
:
space-between
;
text-align
:
left
;
padding
:
10px
15px
;
background
:
#f0f1f2
;
border-radius
:
8px
;
min-height
:
80px
;
cursor
:
pointer
;
.midp-left
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-around
;
margin-right
:
10px
;
.name
{
font-size
:
13px
;
color
:
#333333
;
word-break
:
break-word
;
}
.size
{
font-size
:
12px
;
color
:
#999999
;
}
}
.midp-icon
{
width
:
36px
;
height
:
44px
;
&
>
img
{
width
:
36px
;
height
:
100%
;
border-radius
:
3px
;
}
}
}
&
>
.mid-img
{
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
cursor
:
pointer
;
// & > .icon {
// width: 20px;
// height: 20px;
// }
// & > .img {
// width: 170px;
// height: 108px;
// border-radius: 8px;
// }
.img-box
{
max-width
:
192px
;
max-height
:
320px
;
overflow
:
hidden
;
}
}
&
>
.diagnosis-box
{
width
:
260px
;
text-align
:
left
;
padding
:
10px
15px
;
background
:
#f0f1f2
;
border-radius
:
8px
;
min-height
:
80px
;
.title
{
padding-bottom
:
10px
;
font-size
:
14px
;
font-weight
:
700
;
}
.dia-text
{
font-size
:
12px
;
color
:
#7C7C7C
;
.label
{
color
:
#B4B4B4
;
}
}
}
&
>
.time-box
{
width
:
260px
;
text-align
:
left
;
padding
:
10px
15px
;
background
:
#f0f1f2
;
border-radius
:
8px
;
min-height
:
80px
;
.title
{
padding-bottom
:
10px
;
font-size
:
14px
;
font-weight
:
700
;
}
.time
{
padding-bottom
:
10px
;
font-size
:
12px
;
font-weight
:
700
;
}
.time-text
{
font-size
:
12px
;
color
:
#7C7C7C
;
}
}
&
>
.suggession-box
{
width
:
260px
;
text-align
:
left
;
padding
:
10px
15px
;
background
:
#f0f1f2
;
border-radius
:
8px
;
min-height
:
80px
;
.title
{
padding-bottom
:
10px
;
font-size
:
14px
;
font-weight
:
700
;
}
.suggession-text
{
font-size
:
12px
;
color
:
#7C7C7C
;
.label
{
color
:
#B4B4B4
;
}
}
}
&
>
.audio-box
audio
{
width
:
242px
;
height
:
44px
;
background
:
#EBF5FC
;
border-radius
:
20px
;
}
&
>
.live-box
{
width
:
202px
;
height
:
44px
;
line-height
:
44px
;
background
:
#f0f1f2
;
border-radius
:
8px
;
text-align
:
center
;
}
&
>
.diagnosis-end
{
width
:
100%
;
.split-line
{
width
:
100%
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
margin
:
15px
0
;
font-size
:
14px
;
color
:
#999999
;
&
:
:
before
{
flex
:
1
;
content
:
''
;
height
:
1px
;
margin-right
:
18px
;
background
:
#f0f1f2
;
}
&
:
:
after
{
flex
:
1
;
content
:
''
;
height
:
1px
;
margin-left
:
18px
;
background
:
#f0f1f2
;
}
}
}
}
}
.msg-item-detail.line
{
width
:
100%
;
}
&
.cr
{
flex-direction
:
row-reverse
;
//justify-content: flex-end;
.msg-item-img
{
margin-right
:
0
;
margin-left
:
8px
;
}
.msg-item-detail
{
text-align
:
right
;
&
>
.mid-text
{
background
:
#f0f1f2
;
}
}
}
}
.split-line
{
width
:
100%
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
margin
:
15px
0
;
font-size
:
14px
;
color
:
#999999
;
&
:
:
before
{
flex
:
1
;
content
:
''
;
height
:
1px
;
margin-right
:
18px
;
background
:
#f0f1f2
;
}
&
:
:
after
{
flex
:
1
;
content
:
''
;
height
:
1px
;
margin-left
:
18px
;
background
:
#f0f1f2
;
}
}
.error-mg
{
display
:
inline-block
;
max-width
:
520px
;
// height: 24px;
max-width
:
520px
;
line-height
:
16px
;
text-align
:
left
;
border-radius
:
20px
;
opacity
:
0
.45
;
margin
:
-2px
0
10px
;
padding
:
4px
12px
;
color
:
#FFFFFF
;
background
:
#000000
;
margin-left
:
50px
;
&
.mr
{
margin-left
:
0
;
margin-right
:
50px
;
}
}
}
.
msg-content
:
:-
webkit-scrollbar
{
width
:
0px
;
height
:
0px
;
background-color
:
#fff
;
}
.c-bottom
{
position
:
relative
;
top
:
-8px
;
left
:
0
;
display
:
flex
;
flex-direction
:
row
;
margin
:
16px
12px
16px
25px
;
.cb-icon-wrapper
{
position
:
absolute
;
top
:
0
;
right
:
70px
;
display
:
flex
;
align-items
:
center
;
height
:
44px
;
img
{
width
:
20px
;
height
:
20px
;
margin-right
:
16px
;
cursor
:
pointer
;
}
}
.send-btn
{
display
:
flex
;
flex-direction
:
row
;
justify-content
:
center
;
align-items
:
center
;
width
:
60px
;
height
:
44px
;
background
:
#f3f6f7
;
margin-left
:
12px
;
border-radius
:
8px
;
cursor
:
pointer
;
&
>
img
{
width
:
24px
;
height
:
24px
;
}
&
.active
{
background
:
#0d9078
;
}
}
}
&
.no-content
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
img
{
display
:
block
;
width
:
120px
;
height
:
100px
;
}
p
{
margin-top
:
10px
;
font-size
:
14px
;
color
:
rgba
(
0
,
0
,
0
,
0
.65
);
}
}
}
background
:
#fff
;
.time
{
color
:
#999999
;
}
.small
{
font-size
:
12px
;
}
.ld
{
-webkit-transition-property
:
-
webkit-transform
;
-webkit-transition-duration
:
1s
;
-moz-transition-property
:
-
moz-transform
;
-moz-transition-duration
:
1s
;
-webkit-animation
:
rotate
3s
linear
infinite
;
-moz-animation
:
rotate
3s
linear
infinite
;
-o-animation
:
rotate
3s
linear
infinite
;
animation
:
rotate
3s
linear
infinite
;
}
@-webkit-keyframes
rotate
{
from
{
-webkit-transform
:
rotate
(
0deg
)}
to
{
-webkit-transform
:
rotate
(
360deg
)}
}
@-moz-keyframes
rotate
{
from
{
-moz-transform
:
rotate
(
0deg
)}
to
{
-moz-transform
:
rotate
(
359deg
)}
}
@-o-keyframes
rotate
{
from
{
-o-transform
:
rotate
(
0deg
)}
to
{
-o-transform
:
rotate
(
359deg
)}
}
@keyframes
rotate
{
from
{
transform
:
rotate
(
0deg
)}
to
{
transform
:
rotate
(
359deg
)}
}
}
}
src/components/IM/chat.vue
0 → 100644
浏览文件 @
9fd3e21c
<
template
>
<div
class=
"chat-wrap"
v-show=
"showChat"
>
<section
class=
"component-content"
id=
"screenSet"
>
<article
class=
"center"
v-loadmore=
"getOldMSGHistory"
>
<section
class=
"c-header"
>
<div
class=
"c-header-l"
>
<img
:src=
"doctorImg"
alt
/>
<span
class=
"name"
>
{{
doctorName
}}
医生的问诊群聊
</span>
</div>
</section>
<p
class=
"refreshText"
></p>
<diagnosis-Live></diagnosis-Live>
<section
id=
"msgContentId"
class=
"msg-content scroll-box"
>
<article
v-for=
"(item, index) in messageList"
:key=
"index"
>
<!-- sendOrReceive 为true在右边 -->
<div
class=
"msg-item"
:class=
"
{'cr': item.sendOrReceive}">
<div
v-if=
"item.showType != 10"
class=
"msg-item-img"
>
<img
:src=
"item.avatarImg"
alt
/>
</div>
<div
class=
"msg-item-detail"
:class=
"
{'line': item.showType == 10}">
<span
v-if=
"item.showType != 10"
class=
"mid-time"
>
{{
item
.
name
}}
{{
item
.
timestampStr
}}
</span>
<div
class=
"send-warpper"
>
<img
v-if=
"item.sendOrReceive && item.isShowErrorIcon"
class=
"icon"
src=
"../../assets/image/IM/icon-no-send.png"
alt
/>
<img
v-if=
"item.sendOrReceive && item.isShowLoadingIcon"
class=
"icon ld"
src=
"../../assets/image/IM/loading-icon-new.png"
alt
/>
<div
v-if=
"item.showType == 1"
class=
"mid-text-wrapper"
style=
"max-width: 520px;"
>
<div
class=
"mid-text"
>
{{
item
.
text
}}
</div>
</div>
<div
v-if=
"item.showType == 2"
class=
"mid-img"
v-viewer
>
<div
class=
"img-box"
>
<img
class=
"img"
:src=
"item.url"
:style=
"
{width: item.newW + 'px', height: item.newH + 'px' }"
alt
/>
</div>
</div>
<div
v-if=
"item.showType == 3"
class=
"mid-pdf"
@
click=
"openPDF(item)"
>
<div
class=
"midp-left"
>
<span
class=
"name"
>
{{
item
.
text
|
shortName
(
23
)
}}
</span>
<span
class=
"size"
>
{{
fileSizeChange
(
item
.
size
)
}}
</span>
</div>
<div
class=
"midp-icon"
>
<img
src=
"../../assets/image/IM/icon-pdf.png"
alt
/>
</div>
</div>
<div
v-if=
"item.showType == 4"
class=
"mid-text"
>
{{
item
.
text
}}
<span
class=
"link"
>
{{
item
.
suffix
}}
</span>
</div>
<div
v-if=
"item.showType == 5"
class=
"mid-text no-support"
>
<img
src=
"../../assets/image/IM/icon-warning-circle.png"
alt
/>
<span>
该消息类型PC端暂不支持
</span>
</div>
<!-- 语音消息 -->
<div
v-if=
"item.showType == 6"
class=
"audio-box"
>
<audio
controls
>
<source
:src=
"item.url"
type=
"audio/mpeg"
>
</audio>
</div>
<!-- 问诊开始与病例模块 -->
<div
v-if=
"item.showType == 7"
class=
"diagnosis-box"
>
<div
class=
"title"
>
{{
item
.
title
}}
</div>
<div
class=
"dia-text"
>
<span
class=
"label"
>
患者:
</span>
<span>
{{
item
.
text
.
patientName
}}
{{
item
.
text
.
sex
==
1
?
'男'
:
'女'
}}
{{
item
.
text
.
age
}}
岁
</span>
</div>
<div
class=
"dia-text"
>
<span
class=
"label"
>
病情描述:
</span>
<span>
{{
item
.
text
.
illnessDetail
}}
</span>
</div>
</div>
<!-- 预约时间模块 -->
<div
v-if=
"item.showType == 8"
class=
"time-box"
>
<div
class=
"title"
>
{{
item
.
title
}}
</div>
<div
class=
"time"
>
{{
item
.
text
.
timeStr
}}
</div>
<div
class=
"time-text"
>
{{
item
.
text
.
tips
}}
</div>
</div>
<!-- 音视频与IM的交互 -->
<div
v-if=
"item.showType == 9"
class=
"live-box"
>
<div
class=
"live-notice"
>
{{
item
.
text
}}
</div>
</div>
<!-- 问诊结束 -->
<div
v-if=
"item.showType == 10"
class=
"diagnosis-end"
>
<div
class=
"split-line"
>
{{
item
.
text
}}
</div>
</div>
<!-- 医生建议模块 -->
<div
v-if=
"item.showType == 11"
class=
"suggession-box"
>
<div
class=
"title"
>
{{
item
.
title
}}
</div>
<div
class=
"suggession-text"
>
{{
item
.
text
}}
</div>
</div>
</div>
</div>
</div>
<span
v-if=
"item.sendOrReceive && item.isShowErrorMsg"
class=
"error-mg"
:class=
"
{'mr': item.sendOrReceive}"
>
{{
item
.
errorMsg
}}
</span>
</article>
</section>
<section
class=
"c-bottom"
>
<el-input
type=
"textarea"
placeholder=
"请输入内容"
v-model=
"sendText"
maxlength=
"499"
></el-input>
<div
class=
"cb-icon-wrapper"
>
<el-upload
class=
"bg-uploader"
action=
"#"
accept=
".jpg, .png, .pdf"
:show-file-list=
"false"
:before-upload=
"beforeUploadFile"
>
<img
src=
"../../assets/image/IM/icon-folder-open.png"
alt
/>
</el-upload>
<img
src=
"../../assets/image/IM/icon-link.png"
@
click=
"preSendLinkMsg"
alt
/>
</div>
<div
class=
"send-btn"
:class=
"
{'active': canSend}" @click="sendTextMsg">
<img
v-show=
"canSend"
src=
"../../assets/image/IM/send-yes.png"
alt
/>
<img
v-show=
"!canSend"
src=
"../../assets/image/IM/send-no.png"
alt
/>
</div>
</section>
</article>
</section>
<!-- 选择链接弹窗 -->
<el-dialog
title=
"选择链接"
:show-close=
"true"
:visible
.
sync=
"showSelectDialog"
:close-on-click-modal=
"false"
width=
"500px"
class=
"link-form"
>
<el-form
ref=
"linkFormRef"
:rules=
"rules"
:model=
"linkForm"
label-width=
"100px"
>
<el-form-item
label=
"普通文本"
>
<el-col
:span=
"20"
>
<el-input
v-model=
"linkForm.remark"
size=
"small"
maxlength=
"300"
></el-input>
</el-col>
</el-form-item>
<el-form-item
label=
"链接文案"
prop=
"info"
>
<el-col
:span=
"20"
>
<el-input
v-model=
"linkForm.info"
size=
"small"
maxlength=
"100"
></el-input>
</el-col>
</el-form-item>
<el-form-item
label=
"链接地址"
prop=
"url"
>
<el-col
:span=
"20"
>
<el-select
size=
"small"
style=
"width: 300px"
clearable
filterable
v-model=
"linkForm.url"
placeholder=
"请选择"
>
<el-option
v-for=
"item in linkList"
:key=
"item.id"
:label=
"item.title"
:value=
"item.id"
></el-option>
</el-select>
</el-col>
</el-form-item>
</el-form>
<div
slot=
"footer"
class=
"dialog-footer"
style=
"text-align: right;"
>
<el-button
size=
"small"
@
click=
"showSelectDialog = false"
>
取 消
</el-button>
<el-button
size=
"small"
type=
"primary"
@
click=
"sendLinkMsg"
>
确 定
</el-button>
</div>
</el-dialog>
</div>
</
template
>
<
script
>
import
BreadCrumb
from
"@/components/breadcrumb.vue"
;
import
diagnosisLive
from
"./diagnosis-live"
;
import
{
doUpload
,
getFilePath
}
from
"@/utils/qiniu-util"
;
import
{
openLoading
,
closeLoading
,
betaHandle
,
bindDragHeader
}
from
"@/utils/utils"
;
import
{
getPicaKFAccid
,
getPhomeDemain
}
from
"@/utils"
;
const
CONTAINER_HEIGHT
=
700
;
let
forwardMsgIntervalId
=
null
,
continueIntervalId
=
null
,
autoCompletionIntervalId
=
null
,
cacheMap
=
{};
let
_this
=
null
;
export
default
{
components
:
{
BreadCrumb
,
diagnosisLive
},
props
:
{
showChat
:
{
type
:
Boolean
,
default
:
false
,
},
currentUser
:
{
type
:
Object
,
default
:
{},
}
},
data
()
{
return
{
curmbFirst
:
"云鹊客服"
,
curmbSecond
:
"当前会话"
,
sendText
:
""
,
sessionListData
:
{
currentTimestamp
:
0
,
myTaskCount
:
0
,
sessionList
:
[],
waitingTaskCount
:
0
},
currentContinueTimes
:
0
,
currentSessionIndex
:
0
,
// 当前会话序号
currentSession
:
{},
currentTaskLogId
:
""
,
// 当前会话ID
picakfAccId
:
""
,
historyTimestamp
:
0
,
realTimestamp
:
0
,
containerHeight
:
CONTAINER_HEIGHT
,
showSelectDialog
:
false
,
linkForm
:
{
remark
:
""
,
info
:
""
,
url
:
""
},
linkList
:
[],
// 消息列表 showType 1: 文本; 2: 图片; 3: PDF; 4: 链接; 5: 不支持类型; 6: 语音;7: 病例;
messageList
:
[],
rules
:
{
info
:
[
{
required
:
true
,
message
:
"请填写链接显示文案"
,
trigger
:
"blur"
}
],
url
:
[{
required
:
true
,
message
:
"请选择链接"
,
trigger
:
"change"
}]
},
imgWidth
:
0
,
imgHeight
:
0
,
imgProportion
:
0
,
hasNoHistoryData
:
false
,
tid
:
''
,
teamMemberList
:
[],
doctorName
:
''
,
doctorImg
:
''
};
},
computed
:
{
canSend
()
{
return
!!
this
.
sendText
;
}
},
watch
:
{
// 监听消息列表的变化,添加sessionFlag(会话结束标志)
messageList
:
{
handler
(
newMsgList
)
{
let
l
=
newMsgList
.
length
;
if
(
l
>=
2
)
{
for
(
let
i
=
1
;
i
<
l
;
i
++
)
{
if
(
newMsgList
[
i
-
1
].
taskLogId
!==
newMsgList
[
i
].
taskLogId
)
{
newMsgList
[
i
].
sessionFlag
=
true
;
}
}
}
},
deep
:
true
},
// 监听到有变化,就缓存一下
sendText
(
newText
)
{
cacheMap
[
this
.
currentSession
.
id
]
=
newText
||
""
;
}
},
created
()
{
_this
=
this
;
this
.
picakfAccId
=
getPicaKFAccid
();
this
.
tid
=
this
.
$route
.
query
.
tid
||
'3868439091'
;
this
.
getFiveContentList
();
autoCompletionIntervalId
&&
clearInterval
(
autoCompletionIntervalId
);
autoCompletionIntervalId
=
setInterval
(()
=>
{
this
.
autoCompletionInterval
();
},
2000
);
// 监听键盘的回车按键(回车时发送消息,并阻止其在textarea中的回车换行行为)
document
.
onkeydown
=
function
(
ev
)
{
var
event
=
ev
||
event
;
if
(
event
.
keyCode
==
13
)
{
_this
.
sendTextMsg
();
event
.
preventDefault
();
}
};
this
.
getMembersList
();
// 获取群聊成员信息
},
mounted
()
{
cacheMap
=
{};
this
.
$nextTick
(()
=>
{
_this
.
containerHeight
=
document
.
body
.
clientHeight
-
80
;
// _this.getElmByID("screenSet").style.height = _this.containerHeight - 76 + "px";
_this
.
getElmByID
(
"msgContentId"
).
style
.
height
=
_this
.
containerHeight
-
211
+
"px"
;
});
},
methods
:
{
// 含有敏感信息的消息,自行补全提示文案
// 每30秒监测敏感信息
autoCompletionInterval
()
{
if
(
!
this
.
messageList
.
length
)
return
;
// 将带有loading的消息转成失败的
this
.
messageList
.
forEach
(
item
=>
{
if
(
item
.
isShowLoadingIcon
)
{
item
.
isShowLoadingIcon
=
false
;
item
.
isShowErrorIcon
=
true
;
}
});
let
flag
=
false
,
msg
;
for
(
let
i
=
0
;
i
<
this
.
messageList
.
length
;
i
++
)
{
msg
=
this
.
messageList
[
i
];
if
(
msg
.
isShowErrorIcon
&&
!
msg
.
isShowErrorMsg
)
{
flag
=
true
;
break
;
}
}
if
(
flag
)
{
this
.
getMSGForwardForAC
(
msg
);
}
},
// 查询群组成员列表
getMembersList
()
{
this
.
GET
(
"/im/team/member/list"
,
{
tid
:
this
.
tid
}).
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
this
.
teamMemberList
=
res
.
data
// 1 问诊医生 2接诊医生 3居民 4其他
this
.
doctorName
=
this
.
teamMemberList
[
0
].
name
this
.
doctorImg
=
this
.
teamMemberList
[
0
].
avatarImageUrl
this
.
getMSGHistory
()
// 查询群聊历史消息
}
});
},
// 查询医生和居民的消息 - 向前查找
getMSGForwardForAC
(
msg
)
{
let
params
=
{
includeFlag
:
0
,
lastMsgTimestamp
:
msg
.
realTimestamp
,
limit
:
50
,
tid
:
this
.
tid
};
this
.
POST
(
"/im/team/message/forward"
,
params
).
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
this
.
contactForwardMessage
(
res
.
data
,
false
);
}
});
},
// 获取元素
getElmByID
(
elmId
)
{
return
document
.
getElementById
(
elmId
);
},
// 查询医生和居民的消息历史(下拉刷新时调用)
getOldMSGHistory
()
{
if
(
this
.
hasNoHistoryData
)
return
let
params
=
{
includeFlag
:
0
,
// 不带本条消息
lastMsgTimestamp
:
this
.
historyTimestamp
,
limit
:
20
,
tid
:
this
.
tid
}
this
.
POST
(
"/im/team/message/history"
,
params
).
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
// 将新消息合并到之前的消息中, 并且重置最后一条消息
if
(
res
.
data
&&
res
.
data
.
length
>
0
)
{
this
.
convertMessageList
(
res
.
data
,
3
)
}
else
{
this
.
hasNoHistoryData
=
true
}
}
else
{
this
.
$message
({
message
:
res
.
message
,
type
:
"error"
});
}
});
},
// 查询医生和居民的消息历史(第一次进来时就调用)
getMSGHistory
(
session
)
{
let
params
=
{
includeFlag
:
1
,
// 带本条消息
limit
:
20
,
tid
:
this
.
tid
};
this
.
POST
(
"/im/team/message/history"
,
params
).
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
this
.
convertMessageList
(
res
.
data
,
1
)
}
else
{
this
.
$message
({
message
:
res
.
message
,
type
:
"error"
});
}
});
},
// 查询医生和居民的消息 - 向前查找
// 将获取消息列表插入到当前消息列表的最后
getMSGForward
()
{
let
params
=
{
includeFlag
:
0
,
lastMsgTimestamp
:
this
.
realTimestamp
,
limit
:
20
,
tid
:
this
.
tid
};
this
.
POST
(
"/im/team/message/forward"
,
params
).
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
this
.
convertMessageList
(
res
.
data
,
2
)
}
else
{
this
.
$message
({
message
:
res
.
message
,
type
:
"error"
});
}
});
},
/* 转换消息格式
处理的消息类型只有5种:
type == TEXT(showType:1)
type == image 或 picture(showType:2)
type == custom时,18: PDF文件(showType:3); 19: 链接信息(showType:4); 1 ~ 17: 不支持的消息类型(showType:5);
directFlag 1: 第一次取数据; 2: 拼接实时消息(push); 3: 拼接历史消息(unshift);
*/
convertMessageList
(
messageList
,
directFlag
=
1
)
{
messageList
.
sort
((
a
,
b
)
=>
{
return
a
.
timestamp
-
b
.
timestamp
;
})
let
msg
=
null
,
content
=
null
,
text
=
""
,
suffix
=
""
,
showType
=
1
,
size
=
0
,
url
=
""
,
title
=
""
,
cMessageList
=
[];
messageList
.
forEach
((
rawMsg
,
index
)
=>
{
text
=
""
;
suffix
=
""
;
showType
=
1
;
size
=
0
;
url
=
""
;
title
=
""
;
msg
=
Object
.
assign
({},
rawMsg
);
msg
.
sendOrReceive
=
rawMsg
.
fromAccId
===
this
.
picakfAccId
;
// 判断消息是显示在左边还是右边, true 右
//msg.avatarImg = msg.sendOrReceive ? this.kfAvatar : this.currentSession.avatarImageUrl;
for
(
let
i
=
0
;
i
<
this
.
teamMemberList
.
length
;
i
++
)
{
if
(
rawMsg
.
fromAccId
==
this
.
teamMemberList
[
i
].
accId
)
{
msg
.
avatarImg
=
this
.
teamMemberList
[
i
].
avatarImageUrl
;
msg
.
name
=
this
.
teamMemberList
[
i
].
name
;
break
}
}
if
(
msg
.
type
.
toLowerCase
()
==
"custom"
)
{
content
=
JSON
.
parse
(
msg
.
content
)
text
=
content
.
content
if
(
content
.
bizType
==
-
1
)
{
// 系统消息:消息由于违规未发送成功(可以不处理)
showType
=
-
1
text
=
content
.
content
}
else
if
(
content
.
bizType
==
18
)
{
// PDF
showType
=
3
text
=
content
.
name
size
=
content
.
size
url
=
content
.
url
}
else
if
(
content
.
bizType
==
19
)
{
// 链接
showType
=
4
text
=
content
.
content
suffix
=
content
.
suffix
}
else
if
(
content
.
bizType
==
22
){
// 病例
showType
=
7
title
=
content
.
title
text
=
JSON
.
parse
(
content
.
content
)
}
else
if
(
content
.
bizType
==
23
)
{
// 预约时间
showType
=
8
title
=
content
.
title
text
=
JSON
.
parse
(
content
.
content
)
}
else
if
(
content
.
bizType
==
24
){
// 音视频与IM交互
showType
=
9
text
=
content
.
content
}
else
if
(
content
.
bizType
==
25
){
// 本次问诊结束
showType
=
10
text
=
content
.
content
}
else
if
(
content
.
bizType
==
26
){
// 医生建议
showType
=
11
title
=
content
.
title
text
=
content
.
content
}
else
{
showType
=
5
}
}
else
if
(
msg
.
type
.
toLowerCase
()
==
"image"
||
msg
.
type
.
toLowerCase
()
==
"picture"
)
{
// 图片
content
=
JSON
.
parse
(
msg
.
content
);
url
=
content
.
url
;
text
=
content
.
name
;
showType
=
2
;
this
.
imgSizeHandleNew
(
msg
,
content
.
w
,
content
.
h
);
}
else
if
(
msg
.
type
.
toLowerCase
()
==
"audio"
)
{
// 语音
content
=
JSON
.
parse
(
msg
.
content
)
url
=
content
.
url
showType
=
6
}
else
{
showType
=
1
// 文本
text
=
msg
.
content
}
msg
.
title
=
title
msg
.
text
=
text
msg
.
showType
=
showType
msg
.
suffix
=
suffix
msg
.
size
=
size
msg
.
url
=
url
msg
.
sessionFlag
=
false
cMessageList
.
push
(
msg
)
});
if
(
directFlag
===
1
)
{
this
.
messageList
=
[]
this
.
$forceUpdate
()
forwardMsgIntervalId
&&
clearInterval
(
forwardMsgIntervalId
)
this
.
messageList
=
cMessageList
this
.
$forceUpdate
()
setTimeout
(()
=>
{
this
.
$nextTick
(()
=>
{
const
scrollBoxDom
=
document
.
querySelector
(
".scroll-box"
)
scrollBoxDom
.
scrollTop
=
scrollBoxDom
.
scrollHeight
});
},
100
);
this
.
currentContinueTimes
=
this
.
sessionListData
.
currentTimestamp
-
this
.
currentSession
.
handleStartTime
continueIntervalId
&&
clearInterval
(
continueIntervalId
)
continueIntervalId
=
setInterval
(()
=>
{
this
.
currentContinueTimes
+=
1000
},
1000
);
// 最新消息,要合并CUSTOM类型中,bizType是-1的数据(系统消息)
}
else
if
(
directFlag
===
2
)
{
this
.
contactForwardMessage
(
cMessageList
)
}
else
{
if
(
cMessageList
.
length
)
{
this
.
messageList
.
unshift
(...
cMessageList
)
this
.
$nextTick
(()
=>
{
const
scrollBoxDom
=
document
.
querySelector
(
".scroll-box"
)
scrollBoxDom
.
scrollTop
=
1000
});
}
}
// 重新设置历史与实时的时间戳
if
(
this
.
messageList
.
length
)
{
this
.
historyTimestamp
=
this
.
messageList
[
0
].
timestamp
let
timestamp
=
this
.
messageList
[
this
.
messageList
.
length
-
1
].
timestamp
if
(
timestamp
)
{
this
.
realTimestamp
=
timestamp
}
}
// 自己发送消息时,暂时不刷新消息
if
(
directFlag
==
1
)
{
forwardMsgIntervalId
=
setInterval
(()
=>
{
this
.
getMSGForward
()
},
3000
)
}
this
.
$forceUpdate
()
},
// 接接数据
contactForwardMessage
(
cMessageList
,
canPush
=
true
)
{
let
content
=
{},
signature
=
""
,
msgIndex
=
-
1
,
newMsgList
=
[],
flag
=
false
;
cMessageList
.
forEach
(
item
=>
{
content
=
{};
signature
=
""
;
msgIndex
=
-
1
;
newMsgList
=
[];
if
(
item
.
type
.
toLowerCase
()
==
"custom"
)
{
content
=
JSON
.
parse
(
item
.
content
);
if
(
content
.
bizType
==
-
1
)
{
signature
=
content
.
signature
;
msgIndex
=
this
.
messageList
.
findIndex
(
m
=>
{
return
m
.
signature
==
signature
&&
!
m
.
isShowErrorMsg
;
});
if
(
msgIndex
>
-
1
)
{
flag
=
true
;
this
.
messageList
[
msgIndex
].
errorMsg
=
content
.
content
;
this
.
messageList
[
msgIndex
].
isShowErrorMsg
=
true
;
}
this
.
$forceUpdate
();
}
else
{
flag
=
true
;
canPush
&&
this
.
messageList
.
push
(
item
);
}
}
else
{
flag
=
true
;
canPush
&&
this
.
messageList
.
push
(
item
);
}
});
if
(
flag
)
{
this
.
$nextTick
(()
=>
{
var
element
=
document
.
querySelector
(
".scroll-box"
);
if
(
element
.
scrollTop
>=
element
.
scrollHeight
-
element
.
offsetHeight
-
400
)
{
element
.
scrollTop
=
element
.
scrollHeight
-
element
.
offsetHeight
;
}
});
}
},
// 打开PDF
openPDF
(
item
)
{
window
.
open
(
item
.
url
,
"__blank"
);
},
// 根据字段名及其值,从数组中查找对象
findItemByKeyAndVal
(
arr
,
key
,
value
,
flag
=
0
)
{
if
(
flag
==
0
)
{
return
arr
.
find
(
item
=>
{
return
item
[
key
]
==
value
;
});
}
else
if
(
flag
==
1
)
{
return
arr
.
findIndex
(
item
=>
{
return
item
[
key
]
==
value
;
});
}
},
// 获取积木列表
getFiveContentList
()
{
this
.
GET
(
"/contents/admin/template/queryTemplate?publishFlag=5&pageNo=1&pageSize=99999"
).
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
this
.
linkList
=
res
.
data
.
templateList
||
[];
}
else
{
this
.
$message
({
message
:
res
.
message
,
type
:
"error"
});
}
});
},
// 上传文件
beforeUploadFile
(
file
)
{
console
.
log
(
"file"
,
file
);
let
fileSize
=
file
.
size
/
(
1024
*
1024
);
if
(
fileSize
>
5
)
{
this
.
$message
({
message
:
"请上传小于5M的文件"
,
type
:
"warning"
});
return
;
}
openLoading
(
_this
);
doUpload
(
_this
,
file
,
getFilePath
(
file
,
null
),
"preview4"
,
"progress"
,
""
).
then
(
function
(
resData
)
{
closeLoading
(
_this
);
let
params
=
{};
params
.
fileSize
=
resData
.
size
;
params
.
fileExt
=
resData
.
ext
;
params
.
url
=
resData
.
fullPath
;
params
.
info
=
resData
.
name
;
params
.
type
=
2
;
// 如果是图片,则要获取其宽与高
if
(
params
.
fileExt
.
toLowerCase
()
===
".jpg"
||
params
.
fileExt
.
toLowerCase
()
===
".jpeg"
||
params
.
fileExt
.
toLowerCase
()
===
".png"
)
{
params
.
type
=
1
;
let
image
=
new
Image
();
image
.
src
=
params
.
url
;
image
.
onload
=
function
()
{
let
_img
=
this
;
params
.
width
=
_img
.
width
;
params
.
height
=
_img
.
height
;
_this
.
sendCommonMsg
(
params
);
};
}
else
{
_this
.
sendCommonMsg
(
params
);
}
});
},
// 打开发送链接弹框
preSendLinkMsg
()
{
this
.
linkForm
=
{
remark
:
""
,
info
:
""
,
url
:
""
};
this
.
showSelectDialog
=
true
;
},
// 发送带链接消息
sendLinkMsg
()
{
this
.
$refs
[
"linkFormRef"
].
validate
(
valid
=>
{
if
(
valid
)
{
let
params
=
Object
.
assign
({},
this
.
linkForm
);
params
.
url
=
getPhomeDemain
()
+
`/template_v2/?id=
${
params
.
url
}
`
;
params
.
type
=
3
;
this
.
sendCommonMsg
(
params
);
this
.
showSelectDialog
=
false
;
}
});
},
// 发送文本消息
sendTextMsg
()
{
if
(
!
this
.
canSend
)
return
;
this
.
sendCommonMsg
({
info
:
this
.
sendText
});
this
.
sendText
=
""
;
},
/* 处理发送消息
1: 先将消息体直接显示在对话框中
2: 设置一个时间戳,以便再次找回
3: 保存再次发送的数据
4: 设置各种状态(1:isShowErrorIcon; 2:isShowLoadingIcon; 3:isShowErrorMsg)
*/
handleSendMsg
(
params
,
sendId
)
{
let
text
=
""
;
let
msg
=
Object
.
assign
({},
params
);
msg
.
fromAccount
=
this
.
tid
;
msg
.
toAccount
=
this
.
picakfAccId
;
// type: 0, // 类型 0文本 1图片 2pdf 3链接
msg
.
text
=
params
.
info
||
""
;
msg
.
suffix
=
params
.
remark
||
""
;
if
(
msg
.
type
==
3
)
{
msg
.
suffix
=
params
.
info
;
msg
.
text
=
params
.
remark
||
""
;
}
msg
.
size
=
params
.
fileSize
;
msg
.
url
=
params
.
url
;
msg
.
showType
=
params
.
type
-
0
+
1
;
msg
.
sessionFlag
=
false
;
msg
.
isShowLoadingIcon
=
true
;
msg
.
isShowErrorIcon
=
false
;
msg
.
isShowErrorMsg
=
false
;
// 只有在下次拉取新数据时才有可能是为ture
msg
.
extData
=
Object
.
assign
({},
params
);
// 再将发送时的数据
msg
.
sendId
=
sendId
;
msg
.
sendOrReceive
=
true
;
msg
.
timestampStr
=
new
Date
().
format
(
"hh:mm"
);
msg
.
realTimestamp
=
this
.
realTimestamp
;
msg
.
taskLogId
=
this
.
currentTaskLogId
;
this
.
teamMemberList
.
forEach
(
item
=>
{
if
(
this
.
picakfAccId
=
item
.
accId
)
{
msg
.
avatarImg
=
item
.
avatarImageUrl
// 运营头像
}
})
if
(
msg
.
type
==
1
)
{
this
.
imgSizeHandleNew
(
msg
,
msg
.
width
,
msg
.
height
)
}
this
.
messageList
.
push
(
msg
)
this
.
$nextTick
(()
=>
{
var
element
=
document
.
querySelector
(
".scroll-box"
)
element
.
scrollTop
=
element
.
scrollHeight
});
},
// 发送通用消息
async
sendCommonMsg
(
params
)
{
let
sendMsgParams
=
{
fromAccount
:
this
.
picakfAccId
,
toAccount
:
this
.
tid
,
fileExt
:
""
,
// 文件扩展名称图片或PDF文件)
fileSize
:
0
,
// 文件大小(图片或PDF文件)
height
:
0
,
// 图片高度(仅图片)
width
:
0
,
// 图片宽度(仅图片)
info
:
""
,
// 文本内容,图片的名称,pdf的名称,链接显示内容
md5
:
""
,
// 图片或文件MD5 暂时由后台生成
remark
:
""
,
// 其他信息(链接中的前缀文案)
type
:
0
,
// 类型 0文本 1图片 2pdf 3链接
url
:
""
// url地址(图片、pdf,链接)
}
params
=
Object
.
assign
(
sendMsgParams
,
params
)
// 将获取新数据的定时器关闭
forwardMsgIntervalId
&&
clearInterval
(
forwardMsgIntervalId
)
let
sendId
=
new
Date
().
getTime
()
this
.
handleSendMsg
(
params
,
sendId
)
await
this
.
POST
(
"/im/team/op/message/send"
,
params
)
.
then
(
res
=>
{
if
(
res
.
code
===
"000000"
)
{
// 校验结果:1校验通过 2校验不通过
let
msg
=
this
.
messageList
[
this
.
messageList
.
length
-
1
];
if
(
msg
.
sendId
!==
sendId
)
{
msg
=
this
.
getMsgBySendId
(
sendId
);
}
if
(
res
.
data
.
checkFlag
==
1
)
{
this
.
realTimestamp
=
res
.
data
.
timetag
msg
.
isShowLoadingIcon
=
false
msg
.
isShowErrorIcon
=
false
msg
.
isShowErrorMsg
=
false
// 只有在下次拉取新数据时才有可能是为ture
}
else
{
msg
.
signature
=
res
.
data
.
signature
msg
.
isShowLoadingIcon
=
false
msg
.
isShowErrorIcon
=
true
msg
.
isShowErrorMsg
=
false
// 只有在下次拉取新数据时才有可能是为ture
}
this
.
teamMemberList
.
forEach
((
item
,
index
)
=>
{
if
(
params
.
fromAccount
==
item
.
accId
)
{
msg
.
name
=
item
.
name
}
})
}
else
{
this
.
$message
({
message
:
res
.
message
,
type
:
"error"
});
}
})
.
catch
(
error
=>
{
let
msg
=
this
.
messageList
[
this
.
messageList
.
length
-
1
]
if
(
msg
.
sendId
!==
sendId
)
{
msg
=
this
.
getMsgBySendId
(
sendId
)
}
msg
.
isShowLoadingIcon
=
false
msg
.
isShowErrorIcon
=
true
msg
.
isShowErrorMsg
=
false
// 只有在下次拉取新数据时才有可能是为ture
msg
.
canRepeatSend
=
true
});
this
.
$forceUpdate
()
// 重新开启定时器
forwardMsgIntervalId
=
setInterval
(()
=>
{
this
.
getMSGForward
()
},
3000
)
},
// 根据sendId,查找到对应的消息
getMsgBySendId
(
sendId
)
{
console
.
log
(
"------------getMsgBySendId------------"
);
let
l
=
this
.
messageList
.
length
,
i
=
l
-
1
;
for
(;
i
>
0
;
i
--
)
{
if
(
this
.
messageList
[
i
].
sendId
==
sendId
)
{
break
;
}
}
return
this
.
messageList
[
i
];
},
// 文件大小单位转换
fileSizeChange
(
val
)
{
return
betaHandle
(
val
);
},
},
beforeDestroy
()
{
forwardMsgIntervalId
&&
clearInterval
(
forwardMsgIntervalId
);
autoCompletionIntervalId
&&
clearInterval
(
autoCompletionIntervalId
);
}
};
</
script
>
<
style
lang=
"scss"
scoped
>
@import
"./chat.scss"
;
</
style
>
src/components/IM/diagnosis-live.vue
0 → 100644
浏览文件 @
9fd3e21c
<
template
>
<div
class=
"livebox"
>
<div
class=
"top"
>
<div
class=
"top-left"
>
<h1
class=
"title"
>
{{
title
}}
</h1>
<div
class=
"time-message"
>
<p>
设定时长:
{{
time
}}
分钟
</p>
<p
v-if=
"startTime && endTime"
>
总时长:
{{
useTime
}}
</p>
<p
v-if=
"startTime && endTime"
>
剩余时长:
{{
loseTime
}}
</p>
</div>
</div>
<div
class=
"top-right"
>
<img
:src=
"isMicOn ? voiceSmallImg : voiceCloseImg"
alt=
""
srcset=
""
class=
"icon"
@
click=
"taggleM"
/>
<div
class=
"close"
@
click=
"overFn"
>
结束会话
</div>
</div>
</div>
<div
class=
"main"
>
<div
class=
"viedo-wrapper"
v-for=
"(item, index) of memberList"
:key=
"index"
>
<div
class=
"text"
>
<p>
{{
item
.
name
}}
</p>
<p>
{{
item
.
role
==
1
?
"问诊医生"
:
"接诊医生"
}}
:
</p>
</div>
<div
class=
"viedo"
>
<div
:id=
"rtc.viewslist[index].id"
v-if=
"rtc && rtc.viewslist[index]"
></div>
<!-- 用户声音icon -->
<img
:src=
"
rtc && rtc.viewslist[index] && rtc.viewslist[index].vioce
? voiceSmallImg
: voiceCloseImg
"
alt=
""
srcset=
""
class=
"user-icon"
v-if=
"item.status == 2"
/>
<img
:src=
"setImg(item.status)"
alt=
""
srcset=
""
class=
"video-icon"
v-else
/>
<div
class=
"mask"
v-if=
"rtc && rtc.viewslist[index] && rtc.viewslist[index].mask"
>
<img
:src=
"type == 1 ? voiceImg : noCameraImg"
alt=
""
:class=
"
{ vocie: type == 1 }"
/>
</div>
</div>
<div
class=
"time"
>
<!-- //等待连接还是已经下麦 1为等待 2为进行中 3为下麦 -->
<div
class=
"time-content"
>
<el-button
class=
"call-btn"
size=
"mini"
:disabled=
"item.status == 2 && item.timeleft == 0"
type=
"primary"
:loading=
"item.timeleft > 0"
@
click=
"drivingCall(item)"
>
<span
v-if=
"item.timeleft == 0"
>
呼叫
{{
item
.
role
==
1
?
"问诊医生"
:
"接诊医生"
}}
</span
>
<time-left
@
setItem=
"setItem($event, item)"
v-else
:timeleft=
"item.timeleft"
></time-left>
</el-button>
<span>
{{
showText
(
item
.
status
,
item
.
role
,
item
)
}}
</span>
</div>
</div>
</div>
</div>
<div
class=
"out"
@
click=
"leave"
>
退出
</div>
<alert
ref=
"alert"
></alert>
</div>
</
template
>
<
script
>
import
{
getLiveTimeText
,
countDown
,
getBroswer
,
laseTime
,
lastm
,
}
from
"@/utils/live"
;
import
RtcClient
from
"../../utils/RtcClient.js"
;
import
{
openLoading
,
closeLoading
}
from
"@/utils/utils"
;
import
alert
from
"@/components/common/alert.vue"
;
import
timeLeft
from
"@/components/timeLeft"
;
export
default
{
components
:
{
alert
,
timeLeft
,
},
data
()
{
return
{
closeCallImg
:
require
(
"../../assets/image/live/close-call.png"
),
voiceCloseImg
:
require
(
"../../assets/image/live/voice-close.png"
),
voiceSmallImg
:
require
(
"../../assets/image/live/voice-small.png"
),
voiceImg
:
require
(
"../../assets/image/live/voice.png"
),
waitingCallImg
:
require
(
"../../assets/image/live/waiting-call.png"
),
noCameraImg
:
require
(
"../../assets/image/live/no-camera.png"
),
roleAnchor
:
"anchor"
,
// 主播
rtc
:
null
,
roomId
:
""
,
sdkAppId
:
""
,
userSig
:
""
,
userId
:
""
,
viedoParams
:
null
,
isMicOn
:
false
,
askTime
:
0
,
// 问诊医生接入时长
answerTime
:
0
,
// 接诊医生接入时长
useTime
:
0
,
// 问诊时长
loseTime
:
0
,
// 问诊剩余时长
askTimeFn
:
null
,
answerTimeFn
:
null
,
useTimeFn
:
null
,
loseTimeFn
:
null
,
type
:
2
,
// 1: 语音 2: 视频
startTime
:
0
,
endTime
:
0
,
time
:
30
,
//总时长
memberList
:
[],
tid
:
""
,
// 群id
diagnoseLogId
:
""
,
//问诊id
};
},
created
()
{
this
.
tid
=
this
.
$route
.
query
.
tid
||
"3868439091"
;
this
.
diagnoseLogId
=
this
.
$route
.
query
.
diagnoseLogId
||
"38"
;
// openLoading(this);
this
.
init
();
},
computed
:
{
title
()
{
return
this
.
type
==
1
?
"音频问诊"
:
"视频问诊"
;
},
},
methods
:
{
init
()
{
if
(
this
.
checkChrome
())
{
this
.
getViedoParams
();
this
.
getInfo
();
}
else
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
alert
.
init
({
confirmTxt
:
"我知道了"
,
title
:
`请下载新版Chrome浏览器`
,
})
.
then
(()
=>
{
closeLoading
(
this
);
this
.
$router
.
go
(
-
1
);
})
.
catch
((
err
)
=>
{
closeLoading
(
this
);
this
.
$router
.
go
(
-
1
);
});
});
}
},
// 设置item
setItem
(
data
,
item
)
{
item
.
timeleft
=
0
;
},
// 主动呼叫
drivingCall
(
data
)
{
// /team/call/direct/{imAccId}呼叫
let
url
=
`/im/team/call/direct/`
;
let
params
=
{
imAccId
:
data
.
accId
,
};
this
.
POST
(
url
,
params
).
then
((
res
)
=>
{
if
(
res
.
code
===
"000000"
)
{
// this.tid = this.$route.query.tid || "";
// this.diagnoseLogId = this.$route.query.diagnoseLogId || "";
this
.
getInfo
(
data
);
}
else
{
this
.
$message
({
message
:
res
.
message
,
type
:
"warning"
,
});
}
});
},
// 获取相关信息
getInfo
(
data
)
{
let
role
=
data
?
data
.
role
:
""
;
let
url
=
`/im/team/detail?tid=
${
this
.
tid
}
`
;
this
.
GET
(
url
)
.
then
((
res
)
=>
{
if
(
res
.
code
==
"000000"
)
{
let
{
liveInfo
,
memberList
}
=
res
.
data
;
if
(
liveInfo
)
{
this
.
startTime
=
liveInfo
.
startTimestamp
;
this
.
endTime
=
liveInfo
.
endTimestamp
;
this
.
time
=
lastm
(
this
.
startTime
,
this
.
endTime
);
this
.
roomId
=
Number
(
liveInfo
.
roomId
);
this
.
type
=
liveInfo
.
liveType
;
}
if
(
memberList
&&
memberList
.
length
)
{
memberList
.
forEach
((
item
)
=>
{
if
(
role
)
{
if
(
item
.
role
==
1
)
{
if
(
role
==
1
)
{
this
.
memberList
[
0
]
=
Object
.
assign
(
item
,
{
status
:
1
,
timeleft
:
60
,
});
}
//等待连接还是已经下麦 1为等待 2为进行中 3为下麦
}
if
(
item
.
role
==
2
)
{
if
(
role
==
2
)
{
this
.
memberList
[
1
]
=
Object
.
assign
(
item
,
{
status
:
1
,
timeleft
:
60
,
});
}
}
}
else
{
if
(
item
.
role
==
1
)
{
this
.
memberList
[
0
]
=
Object
.
assign
(
item
,
{
status
:
1
,
timeleft
:
0
,
});
//等待连接还是已经下麦 1为等待 2为进行中 3为下麦
}
if
(
item
.
role
==
2
)
{
this
.
memberList
[
1
]
=
Object
.
assign
(
item
,
{
status
:
1
,
timeleft
:
0
,
});
}
}
//在当前群组中的角色 1: 问诊医生 2: 接诊医生 3: 居民 4: 其他
if
(
item
.
role
==
4
)
{
this
.
userId
=
item
.
liveUserId
;
}
});
}
this
.
getAppId
();
}
else
{
this
.
getErr
();
}
})
.
catch
(()
=>
{
this
.
getErr
();
});
},
// 获取AppId
getAppId
()
{
let
req
=
{};
this
.
GET
(
"/coupler/app/trtc/sdkappid"
,
req
)
.
then
((
res
)
=>
{
if
(
res
.
code
==
"000000"
)
{
this
.
sdkAppId
=
res
.
data
.
sdkAppId
;
this
.
getSing
();
}
else
if
(
res
.
code
==
"200006"
||
res
.
code
==
"200000"
)
{
if
(
this
.
rtc
)
{
this
.
leave
();
}
this
.
getErr
();
}
})
.
catch
((
err
)
=>
{
this
.
getErr
();
});
},
// 获取签名
getSing
()
{
let
req
=
{
sdkAppId
:
this
.
sdkAppId
,
userId
:
this
.
userId
,
};
this
.
POST
(
"/coupler/usersig/trtc"
,
req
)
.
then
((
res
)
=>
{
if
(
res
.
code
==
"000000"
)
{
this
.
userSig
=
res
.
data
.
userSig
;
this
.
clientLogin
();
}
else
if
(
res
.
code
==
"200006"
||
res
.
code
==
"200000"
)
{
if
(
this
.
rtc
)
{
this
.
leave
();
}
this
.
getErr
();
}
})
.
catch
((
err
)
=>
{
this
.
getErr
();
});
},
// 创建trtcClient
clientLogin
()
{
let
obj
=
{
roomId
:
this
.
roomId
,
role
:
this
.
roleAnchor
,
sdkAppId
:
this
.
sdkAppId
,
userId
:
this
.
userId
,
userSig
:
this
.
userSig
,
vueInstance
:
this
,
};
console
.
log
(
'--=-123123-'
,
obj
,
RtcClient
);
this
.
rtc
=
new
RtcClient
(
obj
);
this
.
$nextTick
(()
=>
{
this
.
$refs
.
alert
.
init
({
confirmTxt
:
"我知道了"
,
title
:
`为了更好的体验,请保证您输出设备的正常使用`
,
})
.
then
(()
=>
{
Promise
.
all
([
this
.
rtc
.
join
()]).
then
((
res
)
=>
{
this
.
ispending
();
let
t
=
setTimeout
(()
=>
{
closeLoading
(
this
);
this
.
muteLocalAudio
();
clearTimeout
(
t
);
},
1000
);
});
})
.
catch
((
err
)
=>
{
Promise
.
all
([
this
.
rtc
.
join
()]).
then
((
res
)
=>
{
this
.
ispending
();
let
t
=
setTimeout
(()
=>
{
closeLoading
(
this
);
this
.
muteLocalAudio
();
clearTimeout
(
t
);
},
1000
);
});
});
});
},
// 获取视频参数
getViedoParams
()
{
this
.
GET
(
"/coupler/app/config/push/stream/params"
)
.
then
((
res
)
=>
{
if
(
res
.
code
==
"000000"
)
{
this
.
viedoParams
=
res
.
data
;
}
else
if
(
res
.
code
==
"200006"
||
res
.
code
==
"200000"
)
{
closeLoading
(
this
);
if
(
this
.
rtc
)
{
this
.
leave
();
}
}
})
.
catch
((
err
)
=>
{
console
.
log
(
"获取视频参数数据失败"
);
});
},
// 切换话筒
taggleM
()
{
if
(
this
.
isMicOn
)
{
this
.
muteLocalAudio
();
}
else
{
this
.
unmuteLocalAudio
();
}
},
// 关闭mc
muteLocalAudio
()
{
this
.
isMicOn
=
false
;
this
.
rtc
.
muteLocalAudio
();
},
// 打开mc
unmuteLocalAudio
()
{
this
.
isMicOn
=
true
;
this
.
rtc
.
unmuteLocalAudio
();
},
// 显示文案
showText
(
status
,
role
,
item
)
{
// 1 呼叫中 2 接入 3离线
if
(
item
.
status
==
2
)
{
item
.
timeleft
=
0
;
}
let
text
=
""
;
switch
(
status
)
{
case
1
:
text
=
"呼叫中"
;
break
;
case
3
:
text
=
"已离线"
;
break
;
default
:
text
=
`已接入:
${
role
==
1
?
this
.
askTime
:
this
.
answerTime
}
`
;
break
;
}
return
text
;
},
//设置图像 1为等待 2为进行中 3为下麦
setImg
(
status
)
{
let
img
=
null
;
switch
(
status
)
{
case
1
:
img
=
this
.
waitingCallImg
;
break
;
case
3
:
img
=
this
.
closeCallImg
;
break
;
default
:
img
=
this
.
closeCallImg
;
break
;
}
return
img
;
},
// 用户上线
addNewMember
(
id
)
{
this
.
memberList
.
forEach
((
item
)
=>
{
if
(
item
.
liveUserId
==
id
)
{
item
.
status
=
2
;
this
.
setTime
(
item
.
role
);
}
});
},
// 用户下线
removeMember
(
id
)
{
this
.
memberList
.
forEach
((
item
)
=>
{
if
(
item
.
liveUserId
==
id
)
{
item
.
status
=
3
;
}
});
},
//设置进行时长 1表示为问诊 2为接诊 3为问诊开始 4为问诊结束
setTime
(
flag
)
{
let
text
=
""
;
switch
(
flag
)
{
case
1
:
text
=
"askTime"
;
break
;
case
2
:
text
=
"answerTime"
;
break
;
case
3
:
text
=
"useTime"
;
break
;
case
4
:
text
=
"loseTime"
;
break
;
default
:
break
;
}
let
t
=
0
;
let
liveTime
=
Number
(
sessionStorage
.
getItem
(
`TIME_
${
text
}
`
)
||
t
);
this
[
`
${
text
}
Fn`
]
=
setInterval
(()
=>
{
liveTime
+=
1000
;
this
[
text
]
=
getLiveTimeText
(
liveTime
,
text
);
},
1000
);
},
// 问诊是否进行中
ispending
()
{
if
(
this
.
startTime
<
new
Date
()
<
this
.
endTime
)
{
this
.
setTime
(
3
);
this
.
loseTimeFn
=
setInterval
(()
=>
{
this
.
loseTime
=
countDown
(
this
.
endTime
);
let
timeObj
=
laseTime
(
this
.
endTime
);
if
(
(
timeObj
.
leftm
==
5
||
timeObj
.
leftm
==
3
||
timeObj
.
leftm
==
1
)
&&
timeObj
.
lefts
==
0
&&
timeObj
.
lefth
==
0
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
alert
.
init
({
confirmTxt
:
"我知道了"
,
title
:
`距离会诊结束还剩不足
${
timeObj
.
leftm
}
分钟`
,
})
.
then
(()
=>
{})
.
catch
((
err
)
=>
{});
});
}
},
1000
);
}
else
if
(
new
Date
()
<
this
.
startTime
)
{
let
t
=
setInterval
(()
=>
{
if
(
new
Date
()
>
this
.
startTime
)
{
clearInterval
(
t
);
this
.
ispending
();
}
},
1000
);
}
},
// 检查是否为chrome
checkChrome
()
{
return
getBroswer
().
broswer
==
"Chrome"
;
},
// client离开房间
leave
()
{
this
.
rtc
.
leave
();
this
.
clearSession
();
window
.
location
.
href
=
"about:blank"
;
window
.
close
();
},
// 结束会话
overFn
()
{
this
.
$refs
.
alert
.
init
({
cancleTxt
:
"取消"
,
confirmTxt
:
"我知道了"
,
title
:
`确定要结束会话么?`
,
})
.
then
(()
=>
{
this
.
out
();
})
.
catch
((
err
)
=>
{});
},
out
()
{
let
url
=
`/diagnose/admin/diagnose/endCall/
${
this
.
diagnoseLogId
}
`
;
let
params
=
{};
this
.
POST
(
url
,
params
)
.
then
((
res
)
=>
{
if
(
res
.
code
==
"000000"
)
{
this
.
leave
();
}
else
{
this
.
$refs
.
alert
.
init
({
confirmTxt
:
"我知道了"
,
title
:
`操作失败,请稍后重试`
,
})
.
then
(()
=>
{})
.
catch
((
err
)
=>
{});
}
})
.
catch
(()
=>
{
this
.
$refs
.
alert
.
init
({
confirmTxt
:
"我知道了"
,
title
:
`操作失败,请稍后重试`
,
})
.
then
(()
=>
{})
.
catch
((
err
)
=>
{});
});
},
// 获取信息失败
getErr
()
{
closeLoading
(
this
);
// this.$nextTick(() => {
// this.$refs.alert
// .init({
// confirmTxt: "我知道了",
// title: `获取信息失败,请稍后重试`,
// })
// .then(() => {
// // this.$router.go(-1);
// })
// .catch((err) => {
// // this.$router.go(-1);
// });
// });
},
reloadfn
(
msg
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
alert
.
init
({
confirmTxt
:
"我知道了"
,
title
:
`加入房间失败,重新加入`
,
})
.
then
(()
=>
{
// location.reload();
})
.
catch
((
err
)
=>
{
// location.reload();
});
});
},
clearTime
()
{
clearInterval
(
this
.
askTimeFn
);
clearInterval
(
this
.
answerTimeFn
);
clearInterval
(
this
.
useTimeFn
);
clearInterval
(
this
.
loseTimeFn
);
},
// 清除直播时间相关的seession
clearSession
()
{
sessionStorage
.
removeItem
(
"TIME_askTime"
);
sessionStorage
.
removeItem
(
"TIME_answerTime"
);
sessionStorage
.
removeItem
(
"TIME_useTime"
);
sessionStorage
.
removeItem
(
"TIME_loseTime"
);
},
},
beforeDestroy
()
{
this
.
clearTime
();
},
};
</
script
>
<
style
lang=
"less"
scoped
>
.livebox {
display: flex;
flex-direction: column;
//position: absolute;
//top: 0;
//left: 0;
//right: 0;
//bottom: 0;
background: #000000;
.top {
display: flex;
margin-top: 5%;
justify-content: space-between;
.top-left {
margin-left: 24px;
.title {
width: 72px;
height: 22px;
font-size: 18px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 700;
color: #ffffff;
line-height: 22px;
}
.time-message {
margin-top: 20px;
display: flex;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
line-height: 20px;
p {
margin-right: 20px;
}
}
}
.top-right {
margin: 15px 25px 0 0;
display: flex;
align-items: center;
justify-content: center;
.icon {
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
}
.close {
width: 102px;
height: 32px;
background: #ff4d4f;
border-radius: 2px;
font-size: 14px;
color: #ffffff;
line-height: 32px;
text-align: center;
margin-left: 20px;
cursor: pointer;
}
}
}
.main {
margin-top: 24px;
display: flex;
flex: 1;
.viedo-wrapper {
display: flex;
width: 49%;
height: 50vh;
background: #2d2d2d;
&:first-of-type {
margin-right: 2%;
}
.text {
display: flex;
flex-direction: column-reverse;
width: 112px;
margin-left: 24px;
color: #fff;
padding-bottom: 20px;
}
.time {
display: flex;
flex-direction: column-reverse;
padding-bottom: 20px;
width: 100px;
margin-right: 24px;
margin-left: 5px;
color: #fff;
.time-content {
.call-btn {
display: flex;
justify-content: center;
align-items: center;
min-width: 100px;
margin-bottom: 5px;
}
font-size: 12px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
}
}
.viedo {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.user-icon {
position: absolute;
right: 0;
bottom: 20px;
display: block;
width: 32px;
height: 32px;
z-index: 100;
}
& > div {
height: 100%;
width: 100%;
video {
width: 100%;
height: 100%;
}
}
.video-icon {
width: 96px;
height: 96px;
}
.mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #2d2d2d;
display: flex;
justify-content: center;
align-items: center;
img {
display: block;
width: 93px;
height: 125px;
}
.vocie {
width: 93px;
height: 93px;
}
}
}
}
}
.out {
width: 102px;
height: 32px;
border-radius: 2px;
border: 1px solid #ffffff;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
text-align: center;
line-height: 32px;
cursor: pointer;
margin: 15px auto 6%;
}
}
</
style
>
src/components/IM/user-info.vue
→
src/components/IM/user-info
1
.vue
浏览文件 @
9fd3e21c
文件已移动
src/utils/RtcClient.js
0 → 100644
浏览文件 @
9fd3e21c
class
RtcClient
{
constructor
(
options
)
{
this
.
sdkAppId_
=
options
.
sdkAppId
;
this
.
userId_
=
options
.
userId
;
this
.
userSig_
=
options
.
userSig
;
this
.
roomId_
=
options
.
roomId
;
this
.
role
=
options
.
role
;
// 用户的角色主播还是观众(这里的指的是trtc定义的anchor和audience)
this
.
nick
=
options
.
nick
;
this
.
vueInstance
=
options
.
vueInstance
;
this
.
isJoined_
=
false
;
this
.
isPublished_
=
false
;
this
.
isAudioMuted
=
false
;
this
.
isVideoMuted
=
false
;
this
.
localStream_
=
null
;
this
.
remoteStreams_
=
[];
this
.
members_
=
new
Map
();
this
.
inmembers_
=
new
Map
();
this
.
viewslist
=
[];
this
.
isPushing
=
0
;
try
{
this
.
client_
=
TRTC
.
createClient
({
mode
:
'live'
,
sdkAppId
:
this
.
sdkAppId_
,
userId
:
this
.
userId_
,
userSig
:
this
.
userSig_
});
this
.
handleEvents
();
}
catch
(
error
)
{
console
.
log
(
'创建client失败'
,
error
)
this
.
vueInstance
.
reloadfn
()
}
}
// 加入房间
async
join
()
{
if
(
this
.
isJoined_
)
{
// alert('抱歉,您已经在房间里面为了')
return
;
}
try
{
await
this
.
client_
.
join
({
roomId
:
this
.
roomId_
,
role
:
this
.
role
});
console
.
log
(
'加入房间trtc成功'
);
this
.
isJoined_
=
true
;
// 不要指定cameraId/microphoneId以避免过度约束
this
.
localStream_
=
TRTC
.
createStream
({
audio
:
true
,
video
:
false
,
userId
:
this
.
userId_
,
mirror
:
true
});
this
.
startRTC
()
}
catch
(
e
)
{
console
.
error
(
'加入房间失败 '
+
e
);
this
.
vueInstance
.
reloadfn
()
}
}
// 打开摄像头
async
startRTC
()
{
// 连麦观众角色切换为主播,开始推流
// 设置视频分辨率等参数
this
.
localStream_
.
setVideoProfile
({
width
:
this
.
vueInstance
.
viedoParams
.
webVideoWidth
,
height
:
this
.
vueInstance
.
viedoParams
.
webVideoHeight
,
frameRate
:
this
.
vueInstance
.
viedoParams
.
webVideoFramerate
,
bitrate
:
this
.
vueInstance
.
viedoParams
.
webVideoBitrate
/* kpbs */
});
// 避免重复开摄像头
this
.
stopPush
()
}
// 设置本地流
async
setLocalVideo
()
{
if
(
!
this
.
isJoined_
)
{
console
.
warn
(
'请先加入房间'
);
return
;
}
if
(
this
.
isPublished_
)
{
console
.
warn
(
'您已经发布过了'
);
return
;
}
try
{
this
.
localStream_
.
initialize
().
catch
(
error
=>
{
this
.
vueInstance
.
$message
({
message
:
'打开设备失败,请检查您的设备!'
,
type
:
'error'
});
console
.
error
(
'failed initialize localStream '
+
error
);
}).
then
(()
=>
{
// 本地流在主播放器上播放,并且插入到一个关联的box中
var
localVideoWrapEl
=
document
.
getElementById
(
'ask'
);
this
.
localStream_
.
play
(
localVideoWrapEl
,
{
muted
:
true
});
//主播直接推流
if
(
this
.
role
==
'anchor'
)
{
this
.
publish
()
}
}).
catch
(
error
=>
{
this
.
vueInstance
.
$message
({
message
:
'麦克风打开失败!'
,
type
:
'error'
});
});
}
catch
(
e
)
{
this
.
isPublished_
=
false
;
}
this
.
isPublished_
=
true
;
}
//发布本地流
async
publish
()
{
this
.
client_
&&
this
.
client_
.
publish
(
this
.
localStream_
).
then
(()
=>
{
console
.
log
(
'本地流发布成功'
)
this
.
isPublished_
=
true
;
// 手动将麦克风打开
this
.
unmuteLocalAudio
()
this
.
vueInstance
.
isMicOn
=
true
}).
catch
(
error
=>
{
console
.
log
(
'本地流发布失败'
)
this
.
isPublished_
=
false
;
this
.
vueInstance
.
reloadfn
()
});
}
/**
* 为避免重复推流,先结束当前推流
*/
async
stopPush
()
{
if
(
this
.
localStream_
&&
this
.
isPublished_
&&
this
.
isJoined_
)
{
this
.
client_
.
unpublish
(
this
.
localStream_
).
then
(()
=>
{
this
.
isPublished_
=
false
;
this
.
localStream_
.
stop
();
this
.
setLocalVideo
()
});
}
else
{
this
.
setLocalVideo
()
}
}
// 退出
async
leave
()
{
if
(
!
this
.
isJoined_
)
{
console
.
warn
(
'leave() - please join() firstly'
);
return
;
}
// 如果是正在发布流,就先停止流
await
this
.
unpublish
();
// 离开房间
await
this
.
client_
.
leave
();
if
(
this
.
localStream_
)
{
this
.
muteLocalAudio
()
this
.
localStream_
.
stop
();
this
.
localStream_
.
close
();
this
.
localStream_
=
null
;
}
this
.
isJoined_
=
false
;
}
// 退出本地流
async
unpublish
()
{
if
(
!
this
.
isJoined_
)
{
console
.
warn
(
'unpublish() - please join() firstly'
);
return
;
}
if
(
!
this
.
isPublished_
)
{
console
.
warn
(
'RtcClient.unpublish() called but not published yet'
);
return
;
}
await
this
.
client_
.
unpublish
(
this
.
localStream_
);
this
.
isPublished_
=
false
;
}
//禁用音频轨道
//对于本地流,调用该方法会停止发送音频,远端会触发 Client.on('mute-audio') 事件。
//对于远端流,调用该方法会停止播放音频,但是仍然接收音频数据。
muteLocalAudio
()
{
this
.
localStream_
.
muteAudio
();
}
//启用音频轨道
//对于本地流,调用该方法会触发远端 Client.on('unmute-audio') 事件。
//音频轨道默认是开启的,若你调用 muteAudio() 后可用该方法重新启用音频。
unmuteLocalAudio
()
{
this
.
localStream_
.
unmuteAudio
();
}
// 禁用视频轨道
// 对于本地流,调用该方法会停止发送视频,远端会触发 Client.on('mute-video') 事件。
// 如果视频是从摄像头采集,此时摄像头灯仍然是亮着的。若想完全禁用视频轨道(即关闭摄像头) ,
// 可以使用 removeTrack() 删除视频轨道然后调用 MediaStreamTrack.stop() 关闭视频轨道(关闭摄像头)。
// 对于远端流,调用该方法会停止播放视频,但是仍然接收视频数据.
muteLocalVideo
()
{
this
.
localStream_
.
muteVideo
();
}
//恢复播放音视频
//在某些版本浏览器上移动传入 play() 的 div 容器可能会导致音视频播放器进入 ‘PAUSED’ 状态,此时 需要调用该接口恢复播放。
//由于浏览器自动播放策略的限制,在 play() 返回 PLAY_NOT_ALLOWED 错误后需要引导用户通过手势 调用该接口恢复播放
resumeStreams
()
{
this
.
localStream_
.
resume
();
for
(
let
stream
of
this
.
remoteStreams_
)
{
stream
.
resume
();
}
}
// client的一些事件监听
handleEvents
()
{
// 报错
this
.
client_
.
on
(
'error'
,
err
=>
{
console
.
log
(
'client 报错了--------------------------------------------------------------'
)
console
.
log
(
err
)
// alert(err);
window
.
onbeforeunload
=
null
location
.
reload
();
});
// 房间被解散了
this
.
client_
.
on
(
'client-banned'
,
err
=>
{
console
.
log
(
'房间被解散了'
);
});
// 当一个远程同伴(必须推流)进入房间时触发
this
.
client_
.
on
(
'peer-join'
,
evt
=>
{
const
userId
=
evt
.
userId
;
console
.
log
(
'有远程同伴进入房间:'
,
userId
)
});
// 当远处的同伴离开房间时触发(删减好友列表)
this
.
client_
.
on
(
'peer-leave'
,
evt
=>
{
const
userId
=
evt
.
userId
;
console
.
log
(
'有远程同伴离开房间:'
+
userId
);
this
.
remove
(
userId
)
});
// 在添加远程流时触发
this
.
client_
.
on
(
'stream-added'
,
evt
=>
{
const
remoteStream
=
evt
.
stream
;
// 获取流的StreamId
const
id
=
remoteStream
.
getId
();
// 获取UserId
const
userId
=
remoteStream
.
getUserId
();
this
.
members_
.
set
(
userId
,
remoteStream
);
this
.
inmembers_
.
set
(
userId
,
remoteStream
);
console
.
log
(
`remote stream added: [
${
userId
}
] ID:
${
id
}
type:
${
remoteStream
.
getType
()}
`
);
// 我们订阅远端的流
console
.
log
(
'subscribe to this remote stream'
);
this
.
client_
.
subscribe
(
remoteStream
);
});
// 在订阅远程流时触发
this
.
client_
.
on
(
'stream-subscribed'
,
evt
=>
{
const
remoteStream
=
evt
.
stream
;
const
id
=
remoteStream
.
getId
();
const
uid
=
remoteStream
.
userId_
;
this
.
remoteStreams_
.
push
(
remoteStream
);
remoteStream
.
on
(
'player-state-changed'
,
event
=>
{
console
.
log
(
`
${
event
.
type
}
player is
${
event
.
state
}
`
);
// 远端流是播放还是暂停状态(显示按钮等画面不同)
if
(
event
.
type
==
'video'
&&
event
.
state
==
'STOPPED'
)
{
console
.
log
(
`
${
uid
}
----------远端流为停止`
);
this
.
changeView
(
uid
,
'mask'
,
true
)
}
if
(
event
.
type
==
'video'
&&
event
.
state
==
'PAUSED'
)
{
console
.
log
(
`
${
uid
}
----------远端流为暂停`
);
this
.
changeView
(
uid
,
'mask'
,
true
)
}
if
(
event
.
type
==
'video'
&&
event
.
state
==
'PLAYING'
)
{
console
.
log
(
`
${
uid
}
----------远端流为播放`
);
this
.
changeView
(
uid
,
'mask'
,
false
)
}
});
// 避免重复加载
for
(
let
i
=
0
;
i
<
this
.
viewslist
.
length
;
i
++
)
{
if
(
this
.
viewslist
[
i
]
&&
uid
==
this
.
viewslist
[
i
].
userId
)
{
return
}
}
let
isMask
=
false
// this.viewslist.push({ id: id, userId: uid, nick: uid, mask: isMask, vioce: true })
this
.
add
(
id
,
uid
,
isMask
)
this
.
vueInstance
.
addNewMember
(
uid
)
setTimeout
(()
=>
{
// 避免其他乱入视频
let
index
=
this
.
viewslist
.
findIndex
((
item
=>
{
if
(
item
&&
item
.
userId
)
{
return
item
.
userId
==
uid
}
else
{
return
-
1
}
}))
if
(
index
<
0
)
{
return
}
// 播放视频
remoteStream
.
play
(
id
);
if
(
!
remoteStream
.
hasVideo
())
{
this
.
changeView
(
id
,
'mask'
,
true
)
}
if
(
!
remoteStream
.
hasAudio
())
{
this
.
changeView
(
id
,
'vioce'
,
false
)
}
},
1000
)
});
// 当远程流被移除时触发
this
.
client_
.
on
(
'stream-removed'
,
evt
=>
{
const
remoteStream
=
evt
.
stream
;
const
id
=
remoteStream
.
getId
();
const
uid
=
remoteStream
.
userId_
;
console
.
log
(
`
${
uid
}
------------远程流被移除时触发`
);
// 停止播放并删除相应<video>标签
remoteStream
.
stop
();
this
.
inmembers_
.
delete
(
uid
)
this
.
remoteStreams_
=
this
.
remoteStreams_
.
filter
(
stream
=>
{
return
stream
.
getId
()
!==
id
;
});
this
.
remove
(
uid
)
});
// 流更新
this
.
client_
.
on
(
'stream-updated'
,
evt
=>
{
console
.
log
(
'=========流更新========stream-updated==================='
);
console
.
log
(
evt
);
const
remoteStream
=
evt
.
stream
;
let
uid
=
this
.
getUidByStreamId
(
remoteStream
.
getId
());
// remoteStream.hasVideo() // 是否有视频轨道
// remoteStream.hasAudio() //是否有音轨道
// remoteStream.getType() // 主要用于判断一个远端流是主音视频流还是辅路视频流,辅路视频流通常是一个屏幕分享流。
console
.
log
(
'remoteStream ID: '
+
remoteStream
.
getId
()
+
' was updated hasAudio: '
+
remoteStream
.
hasAudio
()
+
' hasVideo: '
+
remoteStream
.
hasVideo
());
});
// 关闭音轨道
this
.
client_
.
on
(
'mute-audio'
,
evt
=>
{
this
.
changeView
(
evt
.
userId
,
'vioce'
,
false
)
console
.
log
(
evt
.
userId
+
'关闭麦克风============================================'
);
});
// 打开音轨道
this
.
client_
.
on
(
'unmute-audio'
,
evt
=>
{
this
.
changeView
(
evt
.
userId
,
'vioce'
,
true
)
if
(
evt
.
type
==
'audio'
&&
evt
.
state
==
'PLAYING'
&&
this
.
vueInstance
.
type
==
1
)
{
this
.
changeView
(
evt
.
userId
,
'mask'
,
true
)
}
});
//关闭视频轨道
this
.
client_
.
on
(
'mute-video'
,
evt
=>
{
console
.
log
(
evt
.
userId
+
'关闭 video=============================================='
);
this
.
changeView
(
evt
.
userId
,
'mask'
,
true
);
});
// 打开视频轨道
this
.
client_
.
on
(
'unmute-video'
,
evt
=>
{
console
.
log
(
evt
.
userId
+
'打开 video============================================='
);
if
(
this
.
members_
.
get
(
evt
.
userId
))
{
this
.
changeView
(
evt
.
userId
,
'mask'
,
false
);
}
});
}
getUidByStreamId
(
streamId
)
{
for
(
let
[
uid
,
stream
]
of
this
.
members_
)
{
if
(
stream
.
getId
()
==
streamId
)
{
return
uid
;
}
}
}
// 移除视频数组
remove
(
userId
)
{
for
(
let
i
=
0
;
i
<
this
.
viewslist
.
length
;
i
++
)
{
if
(
this
.
viewslist
[
i
]
&&
this
.
viewslist
[
i
].
userId
==
userId
)
{
this
.
viewslist
[
i
]
=
null
}
}
this
.
vueInstance
.
removeMember
(
userId
)
};
// 改变视频数组属性
changeView
(
id
,
attr
,
val
)
{
for
(
let
i
=
0
;
i
<
this
.
viewslist
.
length
;
i
++
)
{
if
(
this
.
viewslist
[
i
]
&&
(
this
.
viewslist
[
i
].
userId
==
id
||
this
.
viewslist
[
i
].
id
==
id
))
{
this
.
viewslist
[
i
][
attr
]
=
val
}
}
}
add
(
id
,
uid
,
isMask
)
{
this
.
vueInstance
.
memberList
.
forEach
((
ele
,
index
)
=>
{
if
(
ele
.
liveUserId
==
uid
)
{
this
.
viewslist
[
index
]
=
{
id
:
id
,
userId
:
uid
,
nick
:
uid
,
mask
:
isMask
,
vioce
:
true
}
}
});
}
}
export
default
RtcClient
src/utils/live/
rtc-c
lient.js
→
src/utils/live/
RtcC
lient.js
浏览文件 @
9fd3e21c
文件已移动
src/utils/utils.js
浏览文件 @
9fd3e21c
...
@@ -5,7 +5,7 @@ export const containObject = function(...obj1) {
...
@@ -5,7 +5,7 @@ export const containObject = function(...obj1) {
return
obj
return
obj
}
}
// 获取页面自适应高度
// 获取页面自适应高度
export
function
resizeHeight
(
cMinusHeight
=
152
,
iMinuxHeight
=
210
,
refHeightId
=
'slidebar-container'
,
export
function
resizeHeight
(
cMinusHeight
=
152
,
iMinuxHeight
=
210
,
refHeightId
=
'slidebar-container'
,
containerHeightId
=
'screenSet'
){
containerHeightId
=
'screenSet'
){
// let containerHeight = p_getElm(refHeightId).getBoundingClientRect().height - 15;
// let containerHeight = p_getElm(refHeightId).getBoundingClientRect().height - 15;
let
containerHeight
=
document
.
body
.
clientHeight
-
80
;
let
containerHeight
=
document
.
body
.
clientHeight
-
80
;
...
@@ -800,7 +800,7 @@ export const betaHandle = (limit) => {
...
@@ -800,7 +800,7 @@ export const betaHandle = (limit) => {
}
}
//转换年月日方法
//转换年月日方法
export
const
timeHandle
=
(
str
)
=>
{
export
const
timeHandle
=
(
str
)
=>
{
let
date
=
new
Date
(
str
*
1
);
let
date
=
new
Date
(
str
*
1
);
let
Y
=
date
.
getFullYear
()
+
'-'
;
let
Y
=
date
.
getFullYear
()
+
'-'
;
let
M
=
(
date
.
getMonth
()
+
1
<
10
?
'0'
+
(
date
.
getMonth
()
+
1
)
:
date
.
getMonth
()
+
1
)
+
'-'
;
let
M
=
(
date
.
getMonth
()
+
1
<
10
?
'0'
+
(
date
.
getMonth
()
+
1
)
:
date
.
getMonth
()
+
1
)
+
'-'
;
...
@@ -809,10 +809,48 @@ export const timeHandle = (str) => {
...
@@ -809,10 +809,48 @@ export const timeHandle = (str) => {
let
m
=
change
(
date
.
getMinutes
());
let
m
=
change
(
date
.
getMinutes
());
return
Y
+
M
+
D
+
h
+
m
return
Y
+
M
+
D
+
h
+
m
}
}
//补0操作
//补0操作
const
change
=
(
num
)
=>
{
const
change
=
(
num
)
=>
{
if
(
parseInt
(
num
)
<
10
){
if
(
parseInt
(
num
)
<
10
){
num
=
'0'
+
num
;
num
=
'0'
+
num
;
}
}
return
num
;
return
num
;
}
}
\ No newline at end of file
export
const
bindDragHeader
=
(
classname
,
content
)
=>
{
const
dragDom
=
document
.
querySelector
(
classname
);
const
con
=
document
.
querySelector
(
content
);
let
translate
,
contranslate
;
dragDom
.
onmousedown
=
(
e
)
=>
{
const
disX
=
e
.
clientX
;
const
disY
=
e
.
clientY
;
translate
=
dragDom
.
style
.
transform
.
replace
(
/
[^
0-9
\-
,
]
/g
,
''
).
split
(
','
);
contranslate
=
con
.
style
.
transform
.
replace
(
/
[^
0-9
\-
,
]
/g
,
''
).
split
(
','
);
con
.
style
.
transition
=
"transform 100ms liner"
;
document
.
onmousemove
=
function
(
e
)
{
const
l
=
e
.
clientX
-
disX
;
const
t
=
e
.
clientY
-
disY
;
let
x
,
y
,
tran
;
if
(
contranslate
.
length
>
1
){
x
=
(
l
+
Number
(
contranslate
[
0
]))
+
'px'
;
y
=
(
t
+
Number
(
contranslate
[
1
]))
+
'px'
;
}
else
{
x
=
(
l
)
+
'px'
;
y
=
(
t
)
+
'px'
;
}
tran
=
`translate(
${
x
}
,
${
y
}
)`
;
con
.
style
.
transform
=
tran
;
};
document
.
onmouseup
=
function
(
e
)
{
document
.
onmousemove
=
null
;
document
.
onmouseup
=
null
;
};
}
}
src/views/IM/diagnosis-admin/diagnosis-live.vue
浏览文件 @
9fd3e21c
...
@@ -99,7 +99,6 @@
...
@@ -99,7 +99,6 @@
</div>
</div>
</
template
>
</
template
>
<
script
>
<
script
>
import
RtcClient
from
"@/utils/live/rtc-client.js"
;
import
{
import
{
getLiveTimeText
,
getLiveTimeText
,
countDown
,
countDown
,
...
@@ -107,6 +106,7 @@ import {
...
@@ -107,6 +106,7 @@ import {
laseTime
,
laseTime
,
lastm
,
lastm
,
}
from
"@/utils/live"
;
}
from
"@/utils/live"
;
import
RtcClient
from
"../../../utils/live/RtcClient.js"
;
import
{
openLoading
,
closeLoading
}
from
"@/utils/utils"
;
import
{
openLoading
,
closeLoading
}
from
"@/utils/utils"
;
import
alert
from
"@/components/common/alert.vue"
;
import
alert
from
"@/components/common/alert.vue"
;
import
timeLeft
from
"@/components/timeLeft"
;
import
timeLeft
from
"@/components/timeLeft"
;
...
@@ -390,7 +390,6 @@ export default {
...
@@ -390,7 +390,6 @@ export default {
},
},
// 显示文案
// 显示文案
showText
(
status
,
role
,
item
)
{
showText
(
status
,
role
,
item
)
{
console
.
log
(
"status"
,
status
);
// 1 呼叫中 2 接入 3离线
// 1 呼叫中 2 接入 3离线
if
(
item
.
status
==
2
)
{
if
(
item
.
status
==
2
)
{
item
.
timeleft
=
0
;
item
.
timeleft
=
0
;
...
@@ -555,19 +554,19 @@ export default {
...
@@ -555,19 +554,19 @@ export default {
// 获取信息失败
// 获取信息失败
getErr
()
{
getErr
()
{
closeLoading
(
this
);
closeLoading
(
this
);
this
.
$nextTick
(()
=>
{
//
this.$nextTick(() => {
this
.
$refs
.
alert
//
this.$refs.alert
.
init
({
//
.init({
confirmTxt
:
"我知道了"
,
//
confirmTxt: "我知道了",
title
:
`获取信息失败,请稍后重试`
,
//
title: `获取信息失败,请稍后重试`,
})
//
})
.
then
(()
=>
{
//
.then(() => {
this
.
$router
.
go
(
-
1
);
// //
this.$router.go(-1);
})
//
})
.
catch
((
err
)
=>
{
//
.catch((err) => {
this
.
$router
.
go
(
-
1
);
// //
this.$router.go(-1);
});
//
});
});
//
});
},
},
reloadfn
(
msg
)
{
reloadfn
(
msg
)
{
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
...
...
src/views/IM/diagnosis-admin/workbench.vue
浏览文件 @
9fd3e21c
<
template
>
<
template
>
<div
class=
"diagnosis-list-content"
>
<div
class=
"diagnosis-list-content"
>
<div
class=
"select-content screenSet"
>
<div
class=
"select-content screenSet"
>
<div
class=
"title"
>
我的工作台
</div>
<div
class=
"title"
>
我的工作台
</div>
</div>
</div>
<div
class=
"select-content screenSet"
>
<div
class=
"select-content screenSet"
>
<!-- 时间 -->
<!-- 时间 -->
...
@@ -30,7 +30,7 @@
...
@@ -30,7 +30,7 @@
<el-radio-button
label=
"Cancelled"
>
已取消(0)
</el-radio-button>
<el-radio-button
label=
"Cancelled"
>
已取消(0)
</el-radio-button>
</el-radio-group>
</el-radio-group>
</div>
</div>
<div
class=
"lfet"
v-else
>
<div
class=
"lfet"
v-else
>
<el-row
:gutter=
"20"
>
<el-row
:gutter=
"20"
>
<el-col
:span=
"6"
><div
class=
"grid-content bg-purple"
>
<el-col
:span=
"6"
><div
class=
"grid-content bg-purple"
>
...
@@ -70,19 +70,23 @@
...
@@ -70,19 +70,23 @@
</el-pagination>
</el-pagination>
</el-row>
</el-row>
</div>
</div>
</div>
</div>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
export
default
{
components
:
{
},
data
(){
data
(){
return
{
return
{
tabPosition
:
"all"
,
tabPosition
:
"all"
,
value1
:
Date
.
now
(),
value1
:
Date
.
now
(),
currentPage4
:
0
,
currentPage4
:
0
,
// 判断是否是管理员
// 判断是否是管理员
isdon
:
'1'
,
isdon
:
'1'
,
name
:
''
showChat
:
true
}
}
},
},
methods
:{
methods
:{
...
@@ -134,7 +138,7 @@ export default {
...
@@ -134,7 +138,7 @@ export default {
}
}
}
}
}
}
.el-row
{
.el-row
{
margin-bottom
:
20px
;
margin-bottom
:
20px
;
&
:last-child
{
&
:last-child
{
...
@@ -165,6 +169,6 @@ export default {
...
@@ -165,6 +169,6 @@ export default {
padding
:
10px
0
;
padding
:
10px
0
;
background-color
:
#f9fafc
;
background-color
:
#f9fafc
;
}
}
</
style
>
node
\ No newline at end of file
</
style
>
写
预览
Markdown
格式
0%
请重试
or
附加一个文件
附加文件
取消
您添加了
0
人
到此讨论。请谨慎行事。
先完成此消息的编辑!
取消
想要评论请
注册
或
登录