Commit 561c7886 by 吴云建

会话页面添加

parent 67148e4c
<template> <template>
<div class="chat-con" :class="{ userMode }"> <div class="chat-con" :class="{ userMode }">
<ChatTitle :close="hide" class="chat-title" /> <ChatTitle :close="hide" class="chat-title" />
<div class="chat-area-con" :class="{isSingle: isSingleChat}"> <div class="chat-area-con" :class="{isSingle: isSingleChat, needSearch: !modelName}">
<div v-if="chatId != null" class="h-100 chat-area"> <div v-if="chatId != null" class="h-100 chat-area">
<chat-room /> <chat-room />
</div> </div>
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator"; import { Component, Vue, Watch, Prop } from "vue-property-decorator";
import remarkList from "../components/common/remarkList.vue"; import remarkList from "../components/common/remarkList.vue";
import ChatMembers from "./components/chat-members.vue"; import ChatMembers from "./components/chat-members.vue";
import ChatRoom from "./components/chat-room.vue"; import ChatRoom from "./components/chat-room.vue";
...@@ -110,14 +110,19 @@ export default class Chat extends Vue { ...@@ -110,14 +110,19 @@ export default class Chat extends Vue {
this.refreshFlag = false; this.refreshFlag = false;
}) })
} }
@Prop(String) modelName: string;
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.chat-area-con { .chat-area-con {
height: calc(100% - 60px); height: 100%;
&.isSingle { &.isSingle {
height: 70vh; height: 70vh;
} }
&.needSearch {
height: calc(100% - 60px);
}
} }
.chat-con { .chat-con {
height: 100%; height: 100%;
...@@ -148,7 +153,7 @@ export default class Chat extends Vue { ...@@ -148,7 +153,7 @@ export default class Chat extends Vue {
position: relative; position: relative;
width: var(--chat-panel-width); width: var(--chat-panel-width);
border-left: 1px solid #e1e1e1; border-left: 1px solid #e1e1e1;
height: calc(100% - 1px); height: calc(100% - 2px);
} }
.buttons { .buttons {
position: absolute; position: absolute;
......
<template> <template>
<div class="chat-container" :class="{'is-in-page': isInPage}"> <div class="chat-container" :class="{'is-in-page': isInPage}">
<div class="search-wrap"> <div class="search-wrap" v-if="!modelName">
<el-input <el-input
class="keyword-input" class="keyword-input"
placeholder="昵称、手机、Email、备注" placeholder="昵称、手机、Email、备注"
...@@ -12,16 +12,18 @@ ...@@ -12,16 +12,18 @@
></el-input> ></el-input>
<i v-if="!isInPage" class="close-btn el-icon-close" @click="$emit('close')"></i> <i v-if="!isInPage" class="close-btn el-icon-close" @click="$emit('close')"></i>
</div> </div>
<chat-list :searchKeyword="searchKeyword" ref="chatListComp" /> <chat-list v-if="!modelName" :searchKeyword="searchKeyword" ref="chatListComp" />
<chat-list-model v-if="modelName" @update-page-info="$emit('update-page-info', $event)"/>
<div class="chat-content-wrap"> <div class="chat-content-wrap">
<chat v-if="chatVisible" /> <chat v-if="chatVisible && onShow" :modelName="modelName"/>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Ref, Vue } from "vue-property-decorator"; import { Component, Prop, Ref, Vue, Watch } from "vue-property-decorator";
import ChatList from "@/customer-service/components/chat-list.vue"; import ChatList from "@/customer-service/components/chat-list.vue";
import ChatListModel from "@/customer-service/components/chat-list-model.vue";
import chat from "@/customer-service/chat.vue"; import chat from "@/customer-service/chat.vue";
import { ChatStore, chatStore } from "@/customer-service/store/model"; import { ChatStore, chatStore } from "@/customer-service/store/model";
import buttonThrottle from "../utils/button-throttle"; import buttonThrottle from "../utils/button-throttle";
...@@ -30,11 +32,16 @@ import buttonThrottle from "../utils/button-throttle"; ...@@ -30,11 +32,16 @@ import buttonThrottle from "../utils/button-throttle";
name: "ChatContainer", name: "ChatContainer",
components: { components: {
chat, chat,
ChatList ChatList,
ChatListModel
} }
}) })
export default class ChatContainer extends Vue { export default class ChatContainer extends Vue {
@Prop(Boolean) isInPage: boolean; @Prop(Boolean) isInPage: boolean;
@Prop(String) modelName: string;
private onShow = false;
private orginPath = "";
@chatStore.State(ChatStore.STATE_CHAT_DIALOG_VISIBLE) @chatStore.State(ChatStore.STATE_CHAT_DIALOG_VISIBLE)
private readonly chatVisible!: ChatStore.STATE_CHAT_DIALOG_VISIBLE; private readonly chatVisible!: ChatStore.STATE_CHAT_DIALOG_VISIBLE;
...@@ -50,9 +57,22 @@ export default class ChatContainer extends Vue { ...@@ -50,9 +57,22 @@ export default class ChatContainer extends Vue {
this.chatListComp.search() this.chatListComp.search()
} }
@Watch("$route") routeUpdate() {
if (this.$route.fullPath !== this.orginPath) {
this.onShow = false;
} else {
this.onShow = true
}
}
private onChatDrawerClose(){ private onChatDrawerClose(){
this.hideChat() this.hideChat()
} }
created() {
this.orginPath = this.$route.fullPath;
this.onShow = true
}
} }
</script> </script>
...@@ -60,7 +80,7 @@ export default class ChatContainer extends Vue { ...@@ -60,7 +80,7 @@ export default class ChatContainer extends Vue {
.chat-container { .chat-container {
height: 70vh; height: 70vh;
&.is-in-page { &.is-in-page {
height: 100%; height: calc(100% - 45px);
} }
} }
.keyword-input { .keyword-input {
......
<template>
<div class="chat-list-con">
<div class="chat-list h-100">
<div class="chat-list-scroll">
<el-scrollbar ref="scrollbar" class="list-scroll no-bottom-scrollbar">
<div
v-for="item in chatRooms"
:key="'room_' + item.id"
class="chat-item"
:class="{ selected: isSelected(item) }"
@click="goToChatRoom(item)"
>
<div class="chat-info">
<div
class="
chat-info-left
d-flex
justify-content-between
align-items-center
"
>
<div
class="chat-name flex-fill text-dot-dot-dot"
>
<span>{{ item.title }}</span>
</div>
<div v-if="item.last_msg_ts" class="chat-time">
{{ formatTimestamp(item.last_msg_ts) }}
</div>
</div>
<div class="chat-msg text-dot-dot-dot">
{{ parseMesage(item) }}
</div>
</div>
</div>
<div
class="empty"
v-if="chatRooms && chatRooms.length <= 0"
>
无接待
</div>
</el-scrollbar>
<el-pagination
background
@size-change="handleSizeChange"
:page-size="pageSize"
:total="total"
:current-page.sync="currentPage"
@current-change="getList"
:page-sizes="[10, 20, 50]"
layout="total, prev, pager, next"
></el-pagination>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Ref, Vue } from "vue-property-decorator";
import { chatStore, ChatStore } from "@/customer-service/store/model";
import { formatTime, TimeFormatRule } from "@/customer-service/utils/time";
import { Chat as ChatType } from "@/customer-service/xim/models/chat";
export function parserMessage(type: string, rawMsg: string) {
if (!type) return "";
if (!rawMsg) return "";
const msg = JSON.parse(rawMsg);
if (type === "text") {
return msg.text;
} else if (type === "image") {
return `[图片]`;
} else if (type === "file") {
return `[文件]`;
} else {
return `[不支持的消息格式]`;
}
}
@Component({ components: {} })
export default class ModelChatList extends Vue {
@chatStore.State(ChatStore.STATE_CHAT_CURRENT_CHAT_ID)
private readonly chatId!: ChatStore.STATE_CHAT_CURRENT_CHAT_ID;
@chatStore.Action(ChatStore.ACTION_CREATE_NEW_CHAT_BY_SERVICE_MAN)
private readonly _createChat!: ChatStore.ACTION_CREATE_NEW_CHAT_BY_SERVICE_MAN;
private chatList: ChatType[] = [];
@chatStore.Action(ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION)
private readonly saveChatId!: ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION;
@chatStore.Mutation(ChatStore.MUTATION_SAVE_MYSELF_ID)
private readonly saveMyId!: ChatStore.MUTATION_SAVE_MYSELF_ID;
@chatStore.Mutation(ChatStore.MUTATION_SET_CHAT_SOURCE)
private readonly setSource!: ChatStore.MUTATION_SET_CHAT_SOURCE;
@chatStore.Mutation(ChatStore.MUTATION_SHOW_CHAT)
private readonly showChat: ChatStore.MUTATION_SHOW_CHAT;
@Prop({ type: Number, default: -1 })
private selected!: number;
@Ref("scrollbar")
private scrollbar: Vue & { update: () => void };
private modelName = "";
private listName = "";
private activeId = "";
private pageSize = 10;
private total = 0;
private currentPage = 1;
private get chatRooms() {
return this.chatList || [];
}
private isSelected(item: ChatType) {
if (this.chatId) {
return item.chat_id === this.chatId;
}
return this.selected === item.chat_id;
}
private async getList() {
const result = await this.sdk.model(this.modelName).list(this.listName || undefined).query({
pageIndex: 1,
item_size: 50
})
this.chatList = result.pageData.rows.map(it => {
return {
id: it.id.value,
business_data: {
model_name: it.ModelName.value,
obj_id: it.ObjId.value,
detail_name: "",
},
last_msg_content: it.LastMsgContent.value,
last_msg_ts: it.LastMsgTime.value,
last_msg_type: it.LastMsgType.value,
title: it.title.value
} as ChatType
})
this.total = result.pageData.record_count;
this.$emit("update-page-info", {title: result.pageData.title})
}
async created() {
this.modelName = this.$route.params.modelName;
this.listName = this.$route.params.listName;
await this.getList();
this.setSource(ChatStore.StateChatSourceDirection.Server);
this.scrollbar.update();
}
mounted() {
this.saveMyId();
}
private handleSizeChange(newPageSize) {
this.pageSize = newPageSize
this.getList()
}
private async goToChatRoom(data: ChatType) {
await this._createChat({
modelName: data.business_data.model_name,
selectedListId: data.business_data.obj_id,
uids:[],
showByPage: true
});
this.activeId = data.id.toString();
}
private raiseChatIdChanged() {
this.$emit("change");
}
private parseMesage(data: ChatType) {
if (data.last_msg_content === "") return "[暂无消息]";
return parserMessage(data.last_msg_type, data.last_msg_content);
}
private formatTimestamp(v: number) {
return formatTime(v, { short: true, rule: TimeFormatRule.Hour12 });
}
private goToDetail(model_name: string, keyvalue: string) {
this.$router.push(
`/${this.$route.params.project}/${this.$route.params.entrance}/detail/${model_name}/key/${keyvalue}`
);
}
}
</script>
<style lang="less" scoped>
.chat-list-con {
display: inline-block;
width: 25%;
box-sizing: border-box;
height: 100%;
border-right: 1px solid #ccc;
.title {
padding-left: 20px;
line-height: 59px;
font-size: 18px;
border-bottom: 1px solid #e1e1e1;
}
}
.chat-list {
text-align: center;
}
.chat-list-scroll {
height: 100%;
.empty {
padding-top: 100%;
}
.list-scroll {
height: calc(100% - 88px);
}
}
.keyword-input {
width: 200px;
margin: 15px 0;
/deep/ .el-input__inner {
font-size: 13px;
height: 30px;
line-height: 30px;
border-radius: 15px;
padding-right: 15px;
}
/deep/ .el-icon-time {
background: transparent;
}
}
.chat-list {
.chat-item {
cursor: pointer;
padding: 4px 15px 10px;
border-bottom: 1px solid #eee;
&:hover {
background: #e4f0ff;
}
&.selected {
background: #f0f0f0;
}
.chat-avatar {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-right: 5px;
}
.chat-info {
display: inline-block;
vertical-align: middle;
width: calc(100% - 10px);
.chat-name {
line-height: 35px;
font-size: 15px;
}
}
.chat-info-left {
text-align: start;
font-size: 14px;
line-height: 20px;
color: #333333;
margin-bottom: 10px;
.chat-time {
text-align: end;
flex: none;
color: #999999;
margin-left: 10px;
font-size: 12px;
line-height: 1;
}
}
.chat-msg {
color: #888;
text-align: start;
font-size: 12px;
margin-top: -13px;
}
}
}
.chat-check-detail {
margin-left: 10px;
}
</style>
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
:title="item.customer_name" :title="item.customer_name"
class="chat-name flex-fill text-dot-dot-dot" class="chat-name flex-fill text-dot-dot-dot"
> >
<span>{{ item.chat_id }}</span> <span>{{ item.title || item.chat_id }}</span>
</div> </div>
<div v-if="item.last_msg_ts" class="chat-time"> <div v-if="item.last_msg_ts" class="chat-time">
{{ formatTimestamp(item.last_msg_ts) }} {{ formatTimestamp(item.last_msg_ts) }}
...@@ -195,7 +195,7 @@ export default class ChatList extends Vue { ...@@ -195,7 +195,7 @@ export default class ChatList extends Vue {
display: inline-block; display: inline-block;
width: 25%; width: 25%;
box-sizing: border-box; box-sizing: border-box;
height: calc(100% - 60px); height: calc(100% - 59px);
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
.title { .title {
padding-left: 20px; padding-left: 20px;
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
class="message-con d-flex align-items-center" class="message-con d-flex align-items-center"
:class="isMyMessage ? 'my-message flex-row-reverse' : ''" :class="isMyMessage ? 'my-message flex-row-reverse' : ''"
> >
<div class="msg-content"> <div class="msg-content" :class="{'algin-left': !isMyMessage}">
<div class="msg-name no-selection">{{ senderName }}</div> <div class="msg-name no-selection" :class="{'algin-left': !isMyMessage}">{{ userName }}</div>
<!-- Image --> <!-- Image -->
<div <div
class="msg-detail image-message" class="msg-detail image-message"
...@@ -158,8 +158,8 @@ import { chatStore, ChatStore } from "@/customer-service/store/model"; ...@@ -158,8 +158,8 @@ import { chatStore, ChatStore } from "@/customer-service/store/model";
components: { FileIcon, VoiceIcon, WhoReadList, VideoPlayerIcon, avatar }, components: { FileIcon, VoiceIcon, WhoReadList, VideoPlayerIcon, avatar },
}) })
export default class Message extends Mixins(Filters) { export default class Message extends Mixins(Filters) {
@chatStore.State(ChatStore.STATE_CHAT_MY_ID) @chatStore.State(ChatStore.STATE_CHAT_CURRENT_USER_UID)
private readonly chatMyId!: ChatStore.STATE_CHAT_MY_ID; private readonly chatMyId!: ChatStore.STATE_CHAT_CURRENT_USER_UID;
@chatStore.Getter(ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS) @chatStore.Getter(ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS)
private readonly chatMembers!: ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS; private readonly chatMembers!: ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS;
...@@ -177,7 +177,6 @@ export default class Message extends Mixins(Filters) { ...@@ -177,7 +177,6 @@ export default class Message extends Mixins(Filters) {
private fileFailed2Load = false; private fileFailed2Load = false;
private image404 = FileType.Image_404; private image404 = FileType.Image_404;
private loadingRealUrl = false; private loadingRealUrl = false;
private senderName = "";
@Prop({ type: Object, default: () => Object.create(null) }) @Prop({ type: Object, default: () => Object.create(null) })
private data!: dto.Message; private data!: dto.Message;
...@@ -236,44 +235,36 @@ export default class Message extends Mixins(Filters) { ...@@ -236,44 +235,36 @@ export default class Message extends Mixins(Filters) {
} }
const senderEid = this.messageBody.eid; const senderEid = this.messageBody.eid;
return senderEid.toString() === this.chatMyId.toString();
if (this.messageBody) { // if (this.messageBody) {
const msg = this.messageBody; // const msg = this.messageBody;
if (this.chatSource) { // if (this.chatSource) {
const source = msg.msg.source; // const source = msg.msg.source;
if (source) { // if (source) {
return ( // return (
source === this.chatSource && // source === this.chatSource &&
senderEid === this.chatMyId // senderEid === this.chatMyId
); // );
} // }
// if (this.org && this.messageBody.oid) {
if (this.org && this.messageBody.oid) { // return (
return ( // this.messageBody.oid === this.org &&
this.messageBody.oid === this.org && // senderEid === this.chatMyId
senderEid === this.chatMyId // );
); // }
} // return false;
// }
return false; // return senderEid === this.chatMyId;
} // }
// return false;
return senderEid === this.chatMyId;
}
return false;
}
private created() {
this.getUserName(this.data.eid);
} }
private mounted() { private mounted() {
this.buildMessageUrl() this.buildMessageUrl()
} }
private getUserName(eid: string) { private get userName() {
this.senderName = this.chatMembers.find(member => member.eid === eid)?.name ?? ""; return this.chatMembers.find(member => member.eid === this.data.eid)?.name ?? "";
} }
private get avatar() { private get avatar() {
...@@ -511,6 +502,16 @@ i.msg-avatar { ...@@ -511,6 +502,16 @@ i.msg-avatar {
text-align: right; text-align: right;
margin-bottom: 3px; margin-bottom: 3px;
min-height: 16px; min-height: 16px;
&.algin-left {
text-align: left;
}
}
.msg-content {
text-align: right;
&.algin-left {
text-align: left;
}
} }
.msg-detail { .msg-detail {
......
...@@ -425,10 +425,10 @@ export default { ...@@ -425,10 +425,10 @@ export default {
const { imChatId } = await sdk() const { imChatId } = await sdk()
.model(params.modelName) .model(params.modelName)
.chat(+params.selectedListId, orgId()) .chat(+params.selectedListId, orgId())
.createChat("这是名字,要改"); .createChat();
const chatId = Number(imChatId); const chatId = Number(imChatId);
await dispatch(ChatStore.ACTION_GET_MY_CHAT_LIST); await dispatch(ChatStore.ACTION_GET_MY_CHAT_LIST);
commit(ChatStore.MUTATION_SHOW_CHAT, true); commit(ChatStore.MUTATION_SHOW_CHAT, !params.showByPage);
await dispatch(ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION, chatId); await dispatch(ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION, chatId);
}, },
async [ChatStore.ACTION_CREATE_NEW_CHAT_BY_CLIENT]( async [ChatStore.ACTION_CREATE_NEW_CHAT_BY_CLIENT](
...@@ -438,7 +438,7 @@ export default { ...@@ -438,7 +438,7 @@ export default {
const { imChatId } = await sdk() const { imChatId } = await sdk()
.model(params.modelName) .model(params.modelName)
.chat(+params.selectedListId, orgId()) .chat(+params.selectedListId, orgId())
.createChat("这是名字,要改", true); .createChat(true);
const chatId = Number(imChatId); const chatId = Number(imChatId);
await commit(ChatStore.MUTATION_SHOW_CHAT, true); await commit(ChatStore.MUTATION_SHOW_CHAT, true);
await dispatch(ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION, chatId); await dispatch(ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION, chatId);
......
...@@ -242,6 +242,7 @@ export namespace ChatStore { ...@@ -242,6 +242,7 @@ export namespace ChatStore {
modelName: string; modelName: string;
selectedListId: string; selectedListId: string;
uids: string[]; uids: string[];
showByPage?: boolean;
}) => Promise<void> }) => Promise<void>
export const ACTION_CREATE_NEW_CHAT_BY_CLIENT = "顾客向客服发起新会话"; export const ACTION_CREATE_NEW_CHAT_BY_CLIENT = "顾客向客服发起新会话";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment