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
8d36ee61
authored
Oct 26, 2021
by
Sixong.Zhu
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
会话控制
parent
3347be3d
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
396 additions
and
322 deletions
components/chat-room.vue
components/chat-title.vue
components/message-input.vue
components/message-item/file-controller.ts
components/message-list.vue
hybrid-input/index.vue
store/index.ts
store/model.ts
components/chat-room.vue
View file @
8d36ee61
...
...
@@ -5,7 +5,7 @@
<div
ref=
"top"
class=
"chat-messages pos-rel flex-fill d-flex"
:class=
"
{ 'is-not-chat-member': !
isChatMember
}"
:class=
"
{ 'is-not-chat-member': !
hasInput
}"
>
<div
v-if=
"getCurrentInputingPeople.length"
...
...
@@ -21,12 +21,12 @@
title=
"收缩侧边栏"
ref=
"resize"
@
mousedown=
"dragControllerDiv"
v-if=
"
isChatMember
"
v-if=
"
hasInput
"
></div>
<div
ref=
"bottom"
class=
"chat-input flex-none h-100"
v-if=
"
isChatMember
"
v-if=
"
hasInput
"
>
<message-input
@
error=
"onError"
/>
</div>
...
...
@@ -36,206 +36,213 @@
</
template
>
<
script
lang=
"ts"
>
import
{
Component
,
Prop
,
Provide
,
Ref
,
Vue
,
Watch
,
}
from
"vue-property-decorator"
;
import
MessageInput
from
"@/customer-service/components/message-input.vue"
;
import
messages
from
"@/customer-service/components/message-list.vue"
;
import
{
ChatStore
,
chatStore
}
from
"@/customer-service/store/model"
;
import
{
Component
,
Prop
,
Provide
,
Ref
,
Vue
,
Watch
,
}
from
"vue-property-decorator"
;
import
MessageInput
from
"@/customer-service/components/message-input.vue"
;
import
messages
from
"@/customer-service/components/message-list.vue"
;
import
{
ChatStore
,
chatStore
}
from
"@/customer-service/store/model"
;
type
RoomInfoTab
=
"customer"
|
"order"
;
type
RoomInfoTab
=
"customer"
|
"order"
;
@
Component
({
components
:
{
MessageInput
,
messages
}
})
export
default
class
ChatRoom
extends
Vue
{
@
Ref
(
"chatBox"
)
chatBox
!
:
Element
;
@
Ref
(
"top"
)
refTop
!
:
Element
;
@
Ref
(
"bottom"
)
refBottom
!
:
Element
;
@
Ref
(
"resize"
)
refResize
!
:
Element
;
@
Component
({
components
:
{
MessageInput
,
messages
}
})
export
default
class
ChatRoom
extends
Vue
{
@
Ref
(
"chatBox"
)
chatBox
!
:
Element
;
@
Ref
(
"top"
)
refTop
!
:
Element
;
@
Ref
(
"bottom"
)
refBottom
!
:
Element
;
@
Ref
(
"resize"
)
refResize
!
:
Element
;
@
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
;
@
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
.
Mutation
(
ChatStore
.
MUTATION_CLEAR_CURRENT_CHAT_MEMBERS
)
private
readonly
clearChatMembers
!
:
ChatStore
.
MUTATION_CLEAR_CURRENT_CHAT_MEMBERS
;
@
chatStore
.
Mutation
(
ChatStore
.
MUTATION_CLEAR_CURRENT_CHAT_MEMBERS
)
private
readonly
clearChatMembers
!
:
ChatStore
.
MUTATION_CLEAR_CURRENT_CHAT_MEMBERS
;
@
chatStore
.
State
(
ChatStore
.
STATE_CURRENT_CHAT_TITLE
)
private
readonly
chatTitle
!
:
ChatStore
.
STATE_CURRENT_CHAT_TITLE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CURRENT_CHAT_TITLE
)
private
readonly
chatTitle
!
:
ChatStore
.
STATE_CURRENT_CHAT_TITLE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CURRENT_CHAT_INPUTING
)
private
readonly
currentInputPeople
!
:
ChatStore
.
STATE_CURRENT_CHAT_INPUTING
;
@
chatStore
.
State
(
ChatStore
.
STATE_CURRENT_CHAT_INPUTING
)
private
readonly
currentInputPeople
!
:
ChatStore
.
STATE_CURRENT_CHAT_INPUTING
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
)
private
readonly
currentChatUniplatId
!
:
ChatStore
.
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
)
private
readonly
currentChatUniplatId
!
:
ChatStore
.
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
;
@
chatStore
.
State
(
ChatStore
.
STATE_MY_CHAT_ROOM_LIST
)
private
readonly
myChatList
!
:
ChatStore
.
STATE_MY_CHAT_ROOM_LIST
;
@
chatStore
.
State
(
ChatStore
.
STATE_MY_CHAT_ROOM_LIST
)
private
readonly
myChatList
!
:
ChatStore
.
STATE_MY_CHAT_ROOM_LIST
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
)
private
readonly
isChatMember
!
:
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
)
private
readonly
isChatMember
!
:
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
;
private
allChatList
=
{
list
:
[]
};
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
)
private
readonly
chatError
:
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
;
@
Prop
({
type
:
Function
})
private
close
?:
()
=>
void
;
private
get
hasInput
()
{
return
this
.
isChatMember
&&
this
.
chatError
!==
this
.
chatId
;
}
@
Provide
()
showReadSummary
=
true
;
private
allChatList
=
{
list
:
[]
}
;
@
Watch
(
"currentChatUniplatId"
)
private
whenCurrentChatIdChanged
(
newValue
:
string
,
oldValue
:
string
)
{
if
(
Number
(
oldValue
)
===
Number
(
newValue
))
return
;
this
.
clearChatMembers
();
}
@
Prop
({
type
:
Function
})
private
close
?:
()
=>
void
;
private
activeTab
:
RoomInfoTab
=
"customer"
;
@
Provide
()
showReadSummary
=
true
;
private
get
getCurrentInputingPeople
()
{
return
this
.
currentInputPeople
.
map
(()
=>
""
/* this.userInfo[k].name */
)
.
join
(
"、"
);
}
@
Watch
(
"currentChatUniplatId"
)
private
whenCurrentChatIdChanged
(
newValue
:
string
,
oldValue
:
string
)
{
if
(
Number
(
oldValue
)
===
Number
(
newValue
))
return
;
this
.
clearChatMembers
(
);
}
private
get
currentChat
()
{
const
chatId
=
this
.
chatId
;
if
(
this
.
myChatList
==
null
)
return
;
const
result
=
this
.
myChatList
.
list
.
find
((
k
)
=>
k
.
chat_id
===
chatId
);
return
result
??
{};
}
private
activeTab
:
RoomInfoTab
=
"customer"
;
private
get
customerInfoTabShow
()
{
return
this
.
activeTab
===
"customer"
;
}
private
get
getCurrentInputingPeople
()
{
return
this
.
currentInputPeople
.
map
(()
=>
""
/* this.userInfo[k].name */
)
.
join
(
"、"
);
}
private
get
orderInfoTabShow
()
{
return
this
.
activeTab
===
"order"
;
}
private
get
currentChat
()
{
const
chatId
=
this
.
chatId
;
if
(
this
.
myChatList
==
null
)
return
;
const
result
=
this
.
myChatList
.
list
.
find
((
k
)
=>
k
.
chat_id
===
chatId
);
return
result
??
{};
}
private
onError
(
msg
:
string
)
{
this
.
$message
.
error
(
msg
)
;
}
private
get
customerInfoTabShow
(
)
{
return
this
.
activeTab
===
"customer"
;
}
private
dragControllerDiv
(
e
:
MouseEvent
)
{
const
resize
=
this
.
refResize
as
any
;
const
top
=
this
.
refTop
as
HTMLElement
;
const
bottom
=
this
.
refBottom
as
HTMLElement
;
const
box
=
this
.
chatBox
as
HTMLElement
;
const
startY
=
e
.
clientY
;
const
originTop
=
resize
.
offsetTop
;
document
.
onmousemove
=
function
(
e
)
{
const
endY
=
e
.
clientY
;
let
moveLen
=
originTop
+
(
endY
-
startY
);
// (endY-startY) = 移动的距离。originTop + 移动的距离 = 顶部区域最后的高度
if
(
moveLen
<
300
)
moveLen
=
300
;
// 上部区域的最小高度为300px
if
(
box
.
clientHeight
-
moveLen
<
150
)
{
moveLen
=
box
.
clientHeight
-
150
;
}
const
bottomHeight
=
box
.
clientHeight
-
moveLen
;
resize
.
style
.
top
=
moveLen
+
"px"
;
// 设置左侧区域的宽度
top
.
style
.
height
=
moveLen
+
"px"
;
bottom
.
style
.
height
=
bottomHeight
+
"px"
;
};
document
.
onmouseup
=
function
()
{
document
.
onmousemove
=
null
;
document
.
onmouseup
=
null
;
resize
.
releaseCapture
&&
resize
.
releaseCapture
();
};
resize
.
setCapture
&&
resize
.
setCapture
();
return
false
;
}
private
get
orderInfoTabShow
()
{
return
this
.
activeTab
===
"order"
;
}
mounted
()
{
this
.
refBottom
&&
((
this
.
refBottom
as
HTMLElement
).
style
.
height
=
this
.
chatBox
.
clientHeight
-
this
.
refTop
.
clientHeight
+
"px"
);
}
}
</
script
>
private
onError
(
msg
:
string
)
{
this
.
$message
.
error
(
msg
);
}
<
style
lang=
"less"
scoped
>
.chat-status
{
display
:
inline-block
;
width
:
46px
;
height
:
20px
;
line-height
:
20px
;
background
:
#22bd7a
;
font-size
:
13px
;
border-radius
:
2px
;
color
:
#ffffff
;
text-align
:
center
;
margin-left
:
10px
;
&.chat-done
{
background
:
#c5d4e5
;
}
}
private
dragControllerDiv
(
e
:
MouseEvent
)
{
const
resize
=
this
.
refResize
as
any
;
const
top
=
this
.
refTop
as
HTMLElement
;
const
bottom
=
this
.
refBottom
as
HTMLElement
;
const
box
=
this
.
chatBox
as
HTMLElement
;
const
startY
=
e
.
clientY
;
const
originTop
=
resize
.
offsetTop
;
document
.
onmousemove
=
function
(
e
)
{
const
endY
=
e
.
clientY
;
let
moveLen
=
originTop
+
(
endY
-
startY
);
// (endY-startY) = 移动的距离。originTop + 移动的距离 = 顶部区域最后的高度
if
(
moveLen
<
300
)
moveLen
=
300
;
// 上部区域的最小高度为300px
if
(
box
.
clientHeight
-
moveLen
<
150
)
{
moveLen
=
box
.
clientHeight
-
150
;
}
const
bottomHeight
=
box
.
clientHeight
-
moveLen
;
resize
.
style
.
top
=
moveLen
+
"px"
;
// 设置左侧区域的宽度
top
.
style
.
height
=
moveLen
+
"px"
;
bottom
.
style
.
height
=
bottomHeight
+
"px"
;
};
document
.
onmouseup
=
function
()
{
document
.
onmousemove
=
null
;
document
.
onmouseup
=
null
;
resize
.
releaseCapture
&&
resize
.
releaseCapture
();
};
resize
.
setCapture
&&
resize
.
setCapture
();
return
false
;
}
.chat-panel
{
height
:
100%
;
.chat-area,
.chat-info
{
display
:
inline-block
;
vertical-align
:
top
;
}
.chat-info
{
width
:
349px
;
border-left
:
1px
solid
#e1e1e1
;
mounted
()
{
this
.
refBottom
&&
((
this
.
refBottom
as
HTMLElement
).
style
.
height
=
this
.
chatBox
.
clientHeight
-
this
.
refTop
.
clientHeight
+
"px"
);
}
}
}
.info-tabs
{
height
:
40px
;
line-height
:
40px
;
border-bottom
:
1px
solid
#f0f0f0
;
</
script
>
.info-tab
{
<
style
lang=
"less"
scoped
>
.chat-status
{
display
:
inline-block
;
vertical-align
:
top
;
width
:
50%
;
width
:
46px
;
height
:
20px
;
line-height
:
20px
;
background
:
#22bd7a
;
font-size
:
13px
;
border-radius
:
2px
;
color
:
#ffffff
;
text-align
:
center
;
font-size
:
15px
;
color
:
#333
;
cursor
:
pointer
;
margin-left
:
10px
;
&.chat-done
{
background
:
#c5d4e5
;
}
}
&.active
{
font-weight
:
600
;
.chat-panel
{
height
:
100%
;
.chat-area,
.chat-info
{
display
:
inline-block
;
vertical-align
:
top
;
}
.chat-info
{
width
:
349px
;
border-left
:
1px
solid
#e1e1e1
;
}
}
}
.chat-area
{
position
:
relative
;
width
:
100%
;
overflow
:
hidden
;
.chat-messages
{
height
:
calc
(
100%
-
130px
+
1px
);
border-bottom
:
1px
solid
#e1e1e1
;
&.is-not-chat-member
{
height
:
calc
(
100%
-
50px
);
border-bottom
:
none
;
.info-tabs
{
height
:
40px
;
line-height
:
40px
;
border-bottom
:
1px
solid
#f0f0f0
;
.info-tab
{
display
:
inline-block
;
vertical-align
:
top
;
width
:
50%
;
text-align
:
center
;
font-size
:
15px
;
color
:
#333
;
cursor
:
pointer
;
&.active
{
font-weight
:
600
;
}
}
}
.resize
{
cursor
:
row-resize
;
position
:
absolute
;
left
:
0
;
top
:
calc
(
100%
-
130px
+
1px
);
height
:
6px
;
.chat-area
{
position
:
relative
;
width
:
100%
;
overflow
:
hidden
;
.chat-messages
{
height
:
calc
(
100%
-
130px
+
1px
);
border-bottom
:
1px
solid
#e1e1e1
;
&.is-not-chat-member
{
height
:
calc
(
100%
-
50px
);
border-bottom
:
none
;
}
}
.resize
{
cursor
:
row-resize
;
position
:
absolute
;
left
:
0
;
top
:
calc
(
100%
-
130px
+
1px
);
height
:
6px
;
width
:
100%
;
}
}
.order-info-con
{
height
:
calc
(
100%
-
40px
);
}
.someone-inputing
{
position
:
absolute
;
left
:
20px
;
bottom
:
20px
;
z-index
:
1
;
color
:
#c2c2c2
;
}
}
.order-info-con
{
height
:
calc
(
100%
-
40px
);
}
.someone-inputing
{
position
:
absolute
;
left
:
20px
;
bottom
:
20px
;
z-index
:
1
;
color
:
#c2c2c2
;
}
</
style
>
components/chat-title.vue
View file @
8d36ee61
...
...
@@ -21,9 +21,14 @@
size=
"small"
v-if=
"!isChatMember"
type=
"primary"
:disabled=
"isChatError"
>
我要接待
</el-button
>
<el-button
class=
"button"
@
click=
"showAddMember"
size=
"small"
<el-button
class=
"button"
@
click=
"showAddMember"
size=
"small"
:disabled=
"isChatError"
>
添加客服
</el-button
>
<el-button
...
...
@@ -32,6 +37,7 @@
size=
"small"
v-if=
"isChatMember && operatorType > 25"
type=
"warning"
:disabled=
"isChatError"
>
结束接待
</el-button
>
<el-button
...
...
@@ -40,6 +46,7 @@
size=
"small"
v-if=
"isChatMember"
type=
"danger"
:disabled=
"isChatError"
>
退出会话
</el-button
>
<i
...
...
@@ -58,164 +65,171 @@
</template>
<
script
lang=
"ts"
>
import
{
Component
,
Prop
,
Vue
}
from
"vue-property-decorator"
;
import
ChatCreator
from
"@/customer-service/components/create-chat.vue"
;
import
{
ChatStore
,
chatStore
}
from
"@/customer-service/store/model"
;
import
{
ChatRole
}
from
"../model"
;
import
{
ChatChangedEvent
,
ChatEventHandler
,
}
from
"./controller/chat-event-handler"
;
import
{
Component
,
Prop
,
Vue
}
from
"vue-property-decorator"
;
import
ChatCreator
from
"@/customer-service/components/create-chat.vue"
;
import
{
ChatStore
,
chatStore
}
from
"@/customer-service/store/model"
;
import
{
ChatRole
}
from
"../model"
;
import
{
ChatChangedEvent
,
ChatEventHandler
,
}
from
"./controller/chat-event-handler"
;
@
Component
({
components
:
{
ChatCreator
}
})
export
default
class
ChatTitle
extends
Vue
{
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
)
private
readonly
chatId
!
:
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
;
@
Component
({
components
:
{
ChatCreator
}
})
export
default
class
ChatTitle
extends
Vue
{
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
)
private
readonly
chatId
!
:
ChatStore
.
STATE_CHAT_CURRENT_CHAT_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_CURRENT_CHAT_TITLE
)
private
readonly
chatTitle
!
:
ChatStore
.
STATE_CURRENT_CHAT_TITLE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CURRENT_CHAT_TITLE
)
private
readonly
chatTitle
!
:
ChatStore
.
STATE_CURRENT_CHAT_TITLE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_DIALOG_IS_SINGLE
)
private
readonly
isSingleChat
:
ChatStore
.
STATE_CHAT_DIALOG_IS_SINGLE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_DIALOG_IS_SINGLE
)
private
readonly
isSingleChat
:
ChatStore
.
STATE_CHAT_DIALOG_IS_SINGLE
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_ADD_CS
)
private
readonly
_addCS
!
:
ChatStore
.
ACTION_CHAT_ADD_CS
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
)
private
readonly
chatError
:
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_START_RECEPTION
)
private
readonly
_startReception
!
:
ChatStore
.
ACTION_CHAT_START_RECEPTION
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_ADD_CS
)
private
readonly
_addCS
!
:
ChatStore
.
ACTION_CHAT_ADD_CS
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_FINISH
_RECEPTION
)
private
readonly
_finishReception
!
:
ChatStore
.
ACTION_CHAT_FINISH
_RECEPTION
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_START
_RECEPTION
)
private
readonly
_startReception
!
:
ChatStore
.
ACTION_CHAT_START
_RECEPTION
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_USER_EXIT
)
private
readonly
_userExitChat
!
:
ChatStore
.
ACTION_CHAT_USER_EXIT
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_FINISH_RECEPTION
)
private
readonly
_finishReception
!
:
ChatStore
.
ACTION_CHAT_FINISH_RECEPTION
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_CS
_EXIT
)
private
readonly
_csExitChat
!
:
ChatStore
.
ACTION_CHAT_CS
_EXIT
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_USER
_EXIT
)
private
readonly
_userExitChat
!
:
ChatStore
.
ACTION_CHAT_USER
_EXIT
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
)
private
readonly
isChatMember
!
:
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CHAT_CS_EXIT
)
private
readonly
_csExitChat
!
:
ChatStore
.
ACTION_CHAT_CS_EXIT
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_USER_UID
)
private
readonly
operatorUid
!
:
ChatStore
.
STATE_CHAT_CURRENT_USER_UID
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
)
private
readonly
isChatMember
!
:
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_USER_TYPE
)
private
readonly
operatorType
!
:
ChatStore
.
STATE_CHAT_CURRENT_USER_TYPE
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_USER_UID
)
private
readonly
operatorUid
!
:
ChatStore
.
STATE_CHAT_CURRENT_USER_UID
;
@
chatStore
.
Mutation
(
ChatStore
.
MUTATION_HIDE_CHAT
)
private
readonly
hideChat
:
ChatStore
.
MUTATION_HIDE_CHAT
;
@
chatStore
.
State
(
ChatStore
.
STATE_CHAT_CURRENT_USER_TYPE
)
private
readonly
operatorType
!
:
ChatStore
.
STATE_CHAT_CURRENT_USER_TYPE
;
private
get
chatMembersId
()
{
return
this
.
chatMembers
.
map
((
k
)
=>
+
k
.
eid
);
}
@
chatStore
.
Mutation
(
ChatStore
.
MUTATION_HIDE_CHAT
)
private
readonly
hideChat
:
ChatStore
.
MUTATION_HIDE_CHAT
;
private
visible
=
false
;
private
get
isChatError
()
{
return
this
.
chatError
===
this
.
chatId
;
}
private
get
notOnlyCheck
():
boolean
{
return
true
;
}
private
get
chatMembersId
()
{
return
this
.
chatMembers
.
map
((
k
)
=>
+
k
.
eid
)
;
}
@
Prop
({
type
:
Function
})
private
close
?:
()
=>
void
;
private
visible
=
false
;
private
showAddMember
()
{
this
.
visible
=
true
;
}
private
get
notOnlyCheck
():
boolean
{
return
true
;
}
private
hideAddMember
()
{
this
.
visible
=
false
;
}
@
Prop
({
type
:
Function
})
private
close
?:
()
=>
void
;
private
async
addMember
(
users
:
string
[],
done
:
()
=>
void
)
{
try
{
await
this
.
_addCS
(
users
);
this
.
hideAddMember
();
}
catch
(
error
)
{
console
.
error
(
error
);
}
finally
{
done
();
private
showAddMember
()
{
this
.
visible
=
true
;
}
}
private
noop
()
{
return
1
;
}
private
hideAddMember
()
{
this
.
visible
=
false
;
}
private
async
exitChat
()
{
this
.
$confirm
(
"确认要退出此会话?"
)
.
then
(
async
()
=>
{
try
{
if
(
+
this
.
operatorType
===
ChatRole
.
Default
)
{
await
this
.
_userExitChat
();
}
else
if
(
+
this
.
operatorType
>
ChatRole
.
Default
)
{
await
this
.
_csExitChat
();
}
this
.
hideChat
();
}
catch
(
error
)
{
console
.
error
(
error
);
}
})
.
catch
(
this
.
noop
);
}
private
async
addMember
(
users
:
string
[],
done
:
()
=>
void
)
{
try
{
await
this
.
_addCS
(
users
);
this
.
hideAddMember
();
}
catch
(
error
)
{
console
.
error
(
error
);
}
finally
{
done
();
}
}
private
async
startReception
()
{
try
{
await
this
.
_startReception
().
then
(()
=>
ChatEventHandler
.
raiseChatChanged
(
ChatChangedEvent
.
Start
,
this
.
chatId
)
);
this
.
$emit
(
"updateActive"
,
"my_receiving"
);
}
catch
(
error
)
{
console
.
error
(
error
);
private
noop
()
{
return
1
;
}
private
async
exitChat
()
{
this
.
$confirm
(
"确认要退出此会话?"
)
.
then
(
async
()
=>
{
try
{
if
(
+
this
.
operatorType
===
ChatRole
.
Default
)
{
await
this
.
_userExitChat
();
}
else
if
(
+
this
.
operatorType
>
ChatRole
.
Default
)
{
await
this
.
_csExitChat
();
}
this
.
hideChat
();
}
catch
(
error
)
{
console
.
error
(
error
);
}
})
.
catch
(
this
.
noop
);
}
}
private
async
finishReception
()
{
await
this
.
$confirm
(
"确定要结束接待吗?结束接待将会终止客服会话"
,
"提示"
,
{
confirmButtonText
:
"确定"
,
cancelButtonText
:
"取消"
,
type
:
"warning"
,
private
async
startReception
()
{
try
{
await
this
.
_startReception
().
then
(()
=>
ChatEventHandler
.
raiseChatChanged
(
ChatChangedEvent
.
Start
,
this
.
chatId
)
);
this
.
$emit
(
"updateActive"
,
"my_receiving"
);
}
catch
(
error
)
{
console
.
error
(
error
);
}
);
await
this
.
_finishReception
().
then
(()
=>
ChatEventHandler
.
raiseChatChanged
(
ChatChangedEvent
.
End
,
this
.
chatId
)
);
this
.
hideChat
();
}
private
async
finishReception
()
{
await
this
.
$confirm
(
"确定要结束接待吗?结束接待将会终止客服会话"
,
"提示"
,
{
confirmButtonText
:
"确定"
,
cancelButtonText
:
"取消"
,
type
:
"warning"
,
}
);
await
this
.
_finishReception
().
then
(()
=>
ChatEventHandler
.
raiseChatChanged
(
ChatChangedEvent
.
End
,
this
.
chatId
)
);
this
.
hideChat
();
}
}
}
</
script
>
<
style
lang=
"less"
scoped
>
.room-title
{
font-size
:
16px
;
padding
:
0
20px
;
height
:
60px
;
min-height
:
60px
;
border-bottom
:
1px
solid
#e1e1e1
;
.title
{
cursor
:
pointer
;
}
.members-count
{
color
:
#666666
;
}
.title-right-arrow
{
font-size
:
10px
;
margin-left
:
10px
;
vertical-align
:
middle
;
color
:
#666
;
}
.title-close
{
color
:
#8d959d
;
cursor
:
pointer
;
margin-left
:
30px
;
.room-title
{
font-size
:
16px
;
padding
:
0
20px
;
height
:
60px
;
min-height
:
60px
;
border-bottom
:
1px
solid
#e1e1e1
;
.title
{
cursor
:
pointer
;
}
.members-count
{
color
:
#666666
;
}
.title-right-arrow
{
font-size
:
10px
;
margin-left
:
10px
;
vertical-align
:
middle
;
color
:
#666
;
}
.title-close
{
color
:
#8d959d
;
cursor
:
pointer
;
margin-left
:
30px
;
}
}
}
</
style
>
components/message-input.vue
View file @
8d36ee61
...
...
@@ -128,7 +128,7 @@
};
img
.
remove
();
}
uploadFile
(
file
)
return
uploadFile
(
file
)
.
then
((
r
)
=>
{
if
(
r
)
{
const
msg
=
{
...
...
@@ -150,6 +150,7 @@
});
this
.
removeSendingMessages
(
index
);
URL
.
revokeObjectURL
(
src
.
url
);
return
index
;
}
else
{
this
.
setMsg2Failed
(
index
);
}
...
...
components/message-item/file-controller.ts
View file @
8d36ee61
...
...
@@ -196,6 +196,7 @@ export const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
export
const
MAX_IMAGE_SIZE_STRING
=
"5MB"
;
export
const
MESSAGE_IMAGE_TOO_LARGE
=
`您发送的图片大小超过
${
MAX_IMAGE_SIZE_STRING
}
。`
;
export
const
MESSAGE_FILE_EMPTY
=
"不能发送空文件。"
;
export
const
ERROR_IMAGE
=
"发送的不是图片"
;
/**
* 最大文件大小
...
...
components/message-list.vue
View file @
8d36ee61
...
...
@@ -69,9 +69,6 @@
@
chatStore
.
Action
(
ChatStore
.
ACTION_GET_CHAT_MESSAGES_AFTER_SPECIFIC_ID
)
private
readonly
getNextPageMsg
!
:
ChatStore
.
ACTION_GET_CHAT_MESSAGES_AFTER_SPECIFIC_ID
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_CLEAR_CURRENT_CHAT_DATA
)
private
readonly
clearChatId
!
:
ChatStore
.
ACTION_CLEAR_CURRENT_CHAT_DATA
;
@
chatStore
.
Mutation
(
ChatStore
.
MUTATION_SAVE_FUNC_SCROLL_TO_BOTTOM
)
private
readonly
saveScrollToBottomFunc
!
:
ChatStore
.
MUTATION_SAVE_FUNC_SCROLL_TO_BOTTOM
;
...
...
@@ -87,6 +84,9 @@
@
chatStore
.
Mutation
(
ChatStore
.
MUTATION_WITHDRAW
)
private
readonly
executeWithDraw
!
:
ChatStore
.
MUTATION_WITHDRAW
;
@
chatStore
.
Action
(
ChatStore
.
ACTION_SET_CHAT_ERROR
)
private
readonly
setError
!
:
ChatStore
.
ACTION_SET_CHAT_ERROR
;
@
Prop
({
default
:
"circle"
})
private
shape
!
:
string
;
...
...
@@ -148,6 +148,14 @@
}
}
@
Watch
(
"chatId"
)
private
onChatChanged
(
o
:
number
,
n
:
number
)
{
o
&&
n
&&
this
.
messages
.
length
?
this
.
fetchNewMsg
()
:
setTimeout
(()
=>
this
.
fetchNewMsg
(),
300
);
this
.
scroll2End
(
this
.
messages
.
length
?
0
:
100
);
}
private
raiseFileOpen
(
value
:
boolean
)
{
this
.
$emit
(
"file-open"
,
value
);
}
...
...
@@ -204,7 +212,6 @@
this
.
scollWrapper
.
removeEventListener
(
"scroll"
,
this
.
handleScroll
);
this
.
clearScrollToBottomFunc
();
this
.
clearNewMessage
();
// this.clearChatId();
}
public
scroll2End
(
delay
?:
number
)
{
...
...
@@ -325,13 +332,25 @@
if
(
msg
.
length
===
0
)
return
;
this
.
startLoadingNew
();
const
msgId
=
getLastMessageId
(
msg
);
const
data
=
await
this
.
getNextPageMsg
(
msgId
);
if
(
data
.
length
===
0
)
{
// eslint-disable-next-line no-console
console
.
log
(
"没有更多新消息了"
);
}
this
.
$emit
(
"next-page"
,
msgId
);
this
.
endLoadingNew
();
return
this
.
getNextPageMsg
(
msgId
)
.
then
((
data
)
=>
{
if
(
data
.
length
===
0
)
{
// eslint-disable-next-line no-console
console
.
log
(
"没有更多新消息了"
);
}
this
.
$emit
(
"next-page"
,
msgId
);
this
.
endLoadingNew
();
})
.
catch
((
e
)
=>
{
if
(
e
&&
e
.
message
&&
(
e
.
message
as
string
).
includes
(
"sql: no rows in result set"
)
)
{
this
.
setError
(
this
.
chatId
);
}
})
.
finally
(()
=>
this
.
endLoadingNew
());
}
private
format2Time
(
time
:
number
)
{
...
...
hybrid-input/index.vue
View file @
8d36ee61
...
...
@@ -18,9 +18,17 @@
src=
"@/customer-service/imgs/pic.png"
/>
</label>
<!--
<label
for=
"chat-upload-file"
:title=
"tip4File"
@
click=
"allowLoadFile"
>
<img
class=
"tool-bar-icon"
src=
"@/customer-service/imgs/file.png"
/>
</label>
-->
<label
for=
"chat-upload-file"
:title=
"tip4File"
@
click=
"allowLoadFile"
v-if=
"enableFileSelection"
>
<img
class=
"tool-bar-icon"
src=
"@/customer-service/imgs/file.png"
/>
</label>
<input
@
change=
"onChange"
...
...
@@ -63,6 +71,7 @@
import
{
Component
,
Ref
,
Vue
,
Watch
}
from
"vue-property-decorator"
;
import
{
namespace
}
from
"vuex-class"
;
import
{
ERROR_IMAGE
,
getFileType
,
getSvg
,
MAX_FILE_SIZE
,
...
...
@@ -130,6 +139,8 @@
private
tip4Image
=
`发送图片(最大
${
MAX_IMAGE_SIZE_STRING
}
)`
;
private
tip4File
=
`发送文件(最大
${
MAX_FILE_SIZE_STRING
}
)`
;
private
enableFileSelection
=
false
;
private
emoji
:
EmojiItem
[]
=
[];
@
Watch
(
"chatId"
)
...
...
@@ -489,6 +500,15 @@
this
.
$emit
(
"error"
,
MESSAGE_FILE_EMPTY
);
return
;
}
if
(
this
.
enableFileSelection
&&
file
.
size
>=
MAX_FILE_SIZE
)
{
this
.
$emit
(
"error"
,
MESSAGE_FILE_TOO_LARGE
);
return
;
}
if
(
this
.
isImage
(
file
))
{
if
(
file
.
size
>=
MAX_IMAGE_SIZE
)
{
this
.
$emit
(
"error"
,
MESSAGE_IMAGE_TOO_LARGE
);
...
...
@@ -496,11 +516,11 @@
}
html
+=
this
.
buildImageHtml
(
file
);
}
else
{
if
(
file
.
size
>=
MAX_FILE_SIZE
)
{
this
.
$emit
(
"error"
,
MESSAGE_FILE_TOO_LARGE
);
return
;
if
(
this
.
enableFileSelection
)
{
html
+=
this
.
buildFileHtml
(
file
);
}
else
{
return
this
.
$emit
(
"error"
,
ERROR_IMAGE
);
}
html
+=
this
.
buildFileHtml
(
file
);
}
}
...
...
store/index.ts
View file @
8d36ee61
...
...
@@ -31,7 +31,7 @@ function uniqueMessages(
messages
:
NonNullable
<
ChatStore
.
STATE_CHAT_MSG_HISTORY
>
)
{
const
arr
=
[...
messages
];
return
unique
(
arr
,
function
(
item
,
all
)
{
return
unique
(
arr
,
function
(
item
,
all
)
{
return
all
.
findIndex
((
k
)
=>
k
.
id
===
item
.
id
);
});
}
...
...
@@ -119,6 +119,7 @@ export default {
[
ChatStore
.
STATE_CHAT_DIALOG_VISIBLE
]:
false
,
[
ChatStore
.
STATE_CHAT_DIALOG_IS_SINGLE
]:
false
,
[
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_MEMBER
]:
false
,
[
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
]:
null
,
[
ChatStore
.
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
]:
null
,
[
ChatStore
.
STATE_CHAT_CURRENT_USER_UID
]:
null
,
[
ChatStore
.
STATE_CHAT_MSG_HISTORY
]:
null
,
...
...
@@ -326,7 +327,7 @@ export default {
state
[
ChatStore
.
STATE_CHAT_SENDING_MESSAGES
]
=
[...
current
];
}
},
[
ChatStore
.
MUTATION_SAVE_CURRENT_CHAT_INPUTING
]:
(
function
()
{
[
ChatStore
.
MUTATION_SAVE_CURRENT_CHAT_INPUTING
]:
(
function
()
{
const
setTimeoutId
:
{
[
key
:
string
]:
number
}
=
{};
return
(
state
:
ChatStoreState
,
...
...
@@ -683,13 +684,15 @@ export default {
);
commit
(
ChatStore
.
MUTATION_INITING_CHAT_DONE
);
commit
(
ChatStore
.
MUTATION_SCROLL_TO_BOTTOM
);
state
[
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
]
=
null
;
},
async
[
ChatStore
.
ACTION_CLEAR_CURRENT_CHAT_DATA
]({
commit
})
{
async
[
ChatStore
.
ACTION_CLEAR_CURRENT_CHAT_DATA
]({
commit
,
state
})
{
commit
(
ChatStore
.
MUTATION_CLEAR_CURRENT_CHAT_ID
);
commit
(
ChatStore
.
MUTATION_CLEAR_MYSELF_ID
);
commit
(
ChatStore
.
MUTATION_CLEAR_CHAT_MSG_HISTORY
);
commit
(
ChatStore
.
MUTATION_CLEAR_CHAT_TITLE
);
commit
(
ChatStore
.
MUTATION_CLEAR_CURRENT_CHAT_MEMBERS
);
state
[
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
]
=
null
;
},
async
[
ChatStore
.
ACTION_GET_CHAT_MEMBERS
]({
commit
,
state
})
{
const
chatId
=
state
[
ChatStore
.
STATE_CHAT_CURRENT_CHAT_ID
];
...
...
@@ -729,7 +732,7 @@ export default {
}
commit
(
ChatStore
.
MUTATION_SAVE_CURRENT_CHAT_MEMBERS
,
unique
(
newChatMembers
,
function
(
item
,
all
)
{
unique
(
newChatMembers
,
function
(
item
,
all
)
{
return
all
.
findIndex
((
k
)
=>
k
.
eid
===
item
.
eid
);
})
);
...
...
@@ -916,6 +919,9 @@ export default {
.
updateChat
(
p
)
.
finally
(()
=>
dispatch
(
ChatStore
.
ACTION_GET_MY_CHAT_LIST
));
},
[
ChatStore
.
ACTION_SET_CHAT_ERROR
]:
({
state
},
chat
:
number
)
=>
{
state
[
ChatStore
.
STATE_CHAT_CURRENT_IS_CHAT_ERROR
]
=
chat
;
},
},
getters
:
{
[
ChatStore
.
STATE_CHAT_MSG_HISTORY
](
state
)
{
...
...
store/model.ts
View file @
8d36ee61
...
...
@@ -44,6 +44,9 @@ export namespace ChatStore {
"当前用户类型状态,25-普通用户,92-客服"
;
export
type
STATE_CHAT_CURRENT_USER_TYPE
=
string
|
null
;
export
const
STATE_CHAT_CURRENT_IS_CHAT_ERROR
=
"当前会话是否错误"
;
export
type
STATE_CHAT_CURRENT_IS_CHAT_ERROR
=
number
|
null
;
export
const
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
=
"当前chat的Uniplat id"
;
export
type
STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
=
string
|
null
;
...
...
@@ -344,6 +347,9 @@ export namespace ChatStore {
value
:
dto
.
MessageHandled
;
})
=>
void
;
export
const
ACTION_SET_CHAT_ERROR
=
"标记会话关键性错误"
;
export
type
ACTION_SET_CHAT_ERROR
=
(
chat
:
number
)
=>
void
;
export
interface
ChatUpdateParameter
{
chat
:
number
;
type
?:
dto
.
MessageType
;
...
...
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