Commit d9ff54d3 by Sixong.Zhu

调整结构

parent ee2ae62f
<template>
<div class="chat-con" :class="{ userMode, isSingle: isSingleChat }" v-if="chatId != null">
<ChatTitle :close="hide" class="chat-title" @updateActive="$emit('updateActive', $event)" />
<div class="chat-area-con" :class="{isSingle: isSingleChat, needSearch: !modelName}">
<div v-if="chatId != null" class="h-100 chat-area">
<chat-room />
</div>
<div class="chat-panel pos-rel" v-if="!userMode">
<el-tabs class="chat-panel-tabs h-100" v-model="currentTab" v-if="currentChat && !refreshFlag">
<el-tab-pane label="数据" name="one" class="h-100">
<ModelDetail
:model_name="currentChat.business_data.model_name"
:id="currentChat.business_data.obj_id"
:name="currentChat.business_data.detail_name"
/>
</el-tab-pane>
<el-tab-pane
class="h-100"
:label="`成员${chatMembers.length}人`"
name="two"
>
<ChatMembers />
</el-tab-pane>
<el-tab-pane class="h-100" label="工作流" name="three">
<workflow
:model_name="currentChat.business_data.model_name"
:id="currentChat.business_data.obj_id"
:name="currentChat.business_data.detail_name"
/>
</el-tab-pane>
<el-tab-pane class="h-100" label="备注" name="four">
<remarkList
:isInSmallPage="true"
:modelName="currentChat.business_data.model_name"
:associateId="currentChat.business_data.obj_id"
/>
</el-tab-pane>
<el-tab-pane label="回复" name="five" class="h-100">
<MsgShortCut />
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
import remarkList from "../components/common/remarkList.vue";
import ChatMembers from "./components/chat-members.vue";
import ChatRoom from "./components/chat-room.vue";
import ChatTitle from "./components/chat-title.vue";
import MessageList from "./components/message-list.vue";
import ModelDetail from "./components/model-detail.vue";
import MsgShortCut from "./components/msg-shortcut.vue";
import workflow from "./components/workflow.vue";
import buttonThrottle from "./utils/button-throttle";
import { ChatStore, chatStore } from "@/customer-service/store/model";
@Component({
components: {
MsgShortCut,
MessageList,
ChatRoom,
ChatMembers,
remarkList,
ChatTitle,
ModelDetail,
workflow,
},
})
export default class Chat extends Vue {
@chatStore.Getter(ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS)
private readonly chatMembers!: ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS;
@chatStore.Getter(ChatStore.GETTER_CURRENT_CURRENT_CHAT)
private readonly currentChat!: ChatStore.GETTER_CURRENT_CURRENT_CHAT;
@chatStore.State(ChatStore.STATE_CHAT_CURRENT_CHAT_ID)
private readonly chatId!: ChatStore.STATE_CHAT_CURRENT_CHAT_ID;
@chatStore.State(ChatStore.STATE_CHAT_DIALOG_IS_SINGLE)
private readonly isSingleChat: ChatStore.STATE_CHAT_DIALOG_IS_SINGLE;
@chatStore.Mutation(ChatStore.MUTATION_HIDE_CHAT)
private readonly hide!: ChatStore.MUTATION_HIDE_CHAT;
@chatStore.Action(ChatStore.ACTION_TERINATE_CHAT)
private readonly _terminate!: ChatStore.ACTION_TERINATE_CHAT;
@chatStore.Action(ChatStore.ACTION_CHAT_ADD_MEMBERS)
private readonly _addMember!: ChatStore.ACTION_CHAT_ADD_MEMBERS;
private userMode = false;
private refreshFlag = false;
private currentTab = "one";
private get chatMembersId() {
return this.chatMembers.map((k) => +k.eid);
}
@buttonThrottle()
private terminate() {
this._terminate();
}
@Watch("chatId") chatIdUpdate() {
this.refreshFlag = true;
this.$nextTick(() => {
this.refreshFlag = false;
});
}
@Prop(String) modelName: string;
}
</script>
<style lang="less" scoped>
.chat-area-con {
height: 100%;
&.isSingle {
height: 70vh;
}
&.needSearch {
height: calc(100% - 60px);
}
}
.chat-con {
height: 100%;
--chat-panel-width: 350px;
font-size: 13px;
color: black;
&.isSingle {
height: 70vh;
}
&.userMode {
--chat-panel-width: 0px;
.chat-title {
/deep/ .title-buttons {
.button {
display: none;
}
}
}
}
}
.chat-area,
.chat-panel {
display: inline-block;
vertical-align: top;
}
.chat-area {
width: calc(100% - var(--chat-panel-width) - 2px);
}
.chat-panel {
position: relative;
width: var(--chat-panel-width);
border-left: 1px solid #e1e1e1;
height: calc(100% - 2px);
}
.buttons {
position: absolute;
left: 0;
right: 0;
bottom: 20px;
text-align: center;
}
.chat-panel {
/deep/ .el-tabs__item {
padding: 0 18px;
}
}
.chat-panel-tabs {
/deep/ .el-tabs__content {
height: calc(100% - 54px);
}
/deep/ .el-tabs__nav-wrap {
padding-left: 15px;
}
}
</style>
<template>
<div class="chat-container" :class="{ 'is-in-page': isInPage }">
<chat-list
v-if="!modelName"
ref="chatListComp"
@list-count-update="$emit('list-count-update', $event)"
>
<div class="search-wrap" v-if="!modelName">
<el-input
class="keyword-input"
placeholder="会话标题"
prefix-icon="el-icon-search"
v-model="searchKeyword"
v-on:keyup.enter.native="search"
clearable
@clear="search"
></el-input>
<i
v-if="!isInPage"
class="close-btn el-icon-close"
@click="$emit('close')"
></i>
</div>
</chat-list>
<chat-list-model
v-if="modelName"
@list-count-update="$emit('list-count-update', $event)"
ref="chatListModel"
:modelName="modelName"
:listName="listName"
/>
<div class="chat-content-wrap" v-if="chatVisible && onShow">
<chat
:modelName="modelName"
@updateActive="$emit('updateActive', $event)"
/>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Ref, Vue, Watch } from "vue-property-decorator";
import chat from "@/customer-service/chat.vue";
import ChatListModel from "@/customer-service/components/chat-list-model.vue";
import ChatList from "@/customer-service/components/chat-list.vue";
import { ChatStore, chatStore } from "@/customer-service/store/model";
@Component({
name: "ChatContainer",
components: {
chat,
ChatList,
ChatListModel,
},
})
export default class ChatContainer extends Vue {
@Prop(Boolean) isInPage: boolean;
@Prop(String) modelName: string;
@Prop(String) listName: string;
@Prop(String) activeName: string;
@Prop(Boolean) isActive: boolean;
private onShow = false;
@chatStore.State(ChatStore.STATE_CHAT_DIALOG_VISIBLE)
private readonly chatVisible!: ChatStore.STATE_CHAT_DIALOG_VISIBLE;
@chatStore.Mutation(ChatStore.MUTATION_HIDE_CHAT)
private readonly hideChat: ChatStore.MUTATION_HIDE_CHAT;
@Ref("chatListComp") chatListComp: ChatList;
private searchKeyword = "";
@Ref("chatListModel") chatListModel: ChatListModel;
private search() {
this.chatListComp.search(this.searchKeyword);
}
@Watch("isActive", { immediate: true }) isActiveUpdate() {
this.onShow = this.isActive;
if (
(!this.onShow && this.activeName !== "my_receiving") ||
(this.onShow && this.listName)
) {
this.chatListModel && this.chatListModel.clearActiveId();
}
}
private onChatDrawerClose() {
this.hideChat();
}
}
</script>
<style lang="less">
.chat-container {
height: 70vh;
&.is-in-page {
height: 100%;
}
}
.keyword-input {
margin: 15px 0 14px 20px;
/deep/ .el-input__inner {
font-size: 13px;
height: 30px;
line-height: 28px;
border-radius: 15px;
padding-right: 15px;
}
/deep/ .el-icon-time {
background: transparent;
}
/deep/ .el-input__icon {
line-height: 32px;
}
}
.search-wrap {
border-bottom: 1px solid #ddd;
}
.close-btn {
float: right;
font-size: 20px;
color: #aaa;
padding: 5px;
cursor: pointer;
margin: 15px 10px 0;
}
.chat-content-wrap {
display: inline-block;
width: 75%;
height: 100%;
box-sizing: border-box;
vertical-align: top;
}
</style>
......@@ -69,7 +69,6 @@
<div class="d-flex align-items-center justify-content-center">
<el-pagination
class="page-comp"
small
@size-change="handleSizeChange"
:page-size="pageSize"
:total="total"
......@@ -79,15 +78,6 @@
:pager-count="5"
layout="total, prev, pager, next"
></el-pagination>
<i
title="刷新"
class="refresh-icon"
@click="getList"
:class="
refreshing ? 'el-icon-loading' : 'el-icon-refresh'
"
></i>
</div>
<div class="action-row">
......@@ -108,6 +98,15 @@
<el-button v-if="showItemCheckbox" @click="unselectAll"
>取消</el-button
>
<i
title="刷新"
class="refresh-icon"
@click="getList"
:class="
refreshing ? 'el-icon-loading' : 'el-icon-refresh'
"
></i>
</div>
</div>
</div>
......@@ -498,6 +497,8 @@ export default class ModelChatList extends Vue {
padding: 15px 0;
}
.action-row {
position: relative;
.el-button {
padding: 8px 14px;
border-radius: 15px;
......@@ -508,5 +509,8 @@ export default class ModelChatList extends Vue {
margin: 0 5px;
cursor: pointer;
color: #409eff;
position: absolute;
right: 10px;
top: 8px;
}
</style>
......@@ -35,28 +35,28 @@
</div>
</template>
<script lang="ts">
import {
import {
Component,
Prop,
Provide,
Ref,
Vue,
Watch,
} from "vue-property-decorator";
} 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 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({
@Component({
components: {
MessageInput,
messages,
},
})
export default class ChatRoom extends Vue {
})
export default class ChatRoom extends Vue {
@Ref("chatBox") chatBox!: Element;
@Ref("top") refTop!: Element;
@Ref("bottom") refBottom!: Element;
......@@ -161,11 +161,11 @@
((this.refBottom as HTMLElement).style.height =
this.chatBox.clientHeight - this.refTop.clientHeight + "px");
}
}
}
</script>
<style lang="less" scoped>
.chat-status {
.chat-status {
display: inline-block;
width: 46px;
height: 20px;
......@@ -179,9 +179,9 @@
&.chat-done {
background: #c5d4e5;
}
}
}
.chat-panel {
.chat-panel {
height: 100%;
.chat-area,
.chat-info {
......@@ -192,8 +192,8 @@
width: 349px;
border-left: 1px solid #e1e1e1;
}
}
.info-tabs {
}
.info-tabs {
height: 40px;
line-height: 40px;
border-bottom: 1px solid #f0f0f0;
......@@ -211,8 +211,8 @@
font-weight: 600;
}
}
}
.chat-area {
}
.chat-area {
position: relative;
width: 100%;
overflow: hidden;
......@@ -220,7 +220,7 @@
height: calc(100% - 130px + 1px);
border-bottom: 1px solid #e1e1e1;
&.is-not-chat-member {
height: 100%;
height: calc(100% - 50px);
border-bottom: none;
}
}
......@@ -232,15 +232,15 @@
height: 6px;
width: 100%;
}
}
.order-info-con {
}
.order-info-con {
height: calc(100% - 40px);
}
.someone-inputing {
}
.someone-inputing {
position: absolute;
left: 20px;
bottom: 20px;
z-index: 1;
color: #c2c2c2;
}
}
</style>
<template>
<div class="h-100 pos-rel">
<div class="scroll-wrap">
<el-scrollbar class="h-100">
<div
class="data-row"
v-for="item in detailData"
:key="item.label"
>
<span class="data-key"
>{{ item.label }}{{ item.label ? ":" : "" }}
</span>
<span class="data-value" v-html="item.template"></span>
<span
class="operation_field"
v-if="item.actions && item.actions.length > 0"
>
<el-button
v-for="action in item.actions"
:key="action.name"
@click="execute_action(action)"
type="text"
size="small"
>{{ action.label }}</el-button
>
</span>
</div>
<div class="top-actions">
<el-button
v-for="action in actions"
:key="action.name"
@click="execute_action(action)"
type="text"
size="small"
>{{ action.label }}</el-button
>
</div>
</el-scrollbar>
</div>
<div class="detal-btns">
<el-button @click="goTodetail">查看详情</el-button>
</div>
</div>
</template>
<script lang="ts">
import { DetailTypes } from "uniplat-sdk";
import { Component, Prop, Vue } from "vue-property-decorator";
import { ChatStore, chatStore } from "../store/model";
import Chat from "../xim";
import { EVENTS } from "@/EventConsts";
import { goForward } from "@/utils/go-forward";
@Component({ components: {} })
export default class ChatModelDetail extends Vue {
@chatStore.Mutation(ChatStore.MUTATION_HIDE_CHAT)
private readonly hideChat: ChatStore.MUTATION_HIDE_CHAT;
@Prop({ type: String, required: true })
private readonly model_name!: string;
@Prop({ type: String, required: true })
private readonly id!: string;
@Prop({ type: String, default: null })
private readonly name!: string;
@Prop()
private readonly drawer!: boolean;
private sseMessageRefreshData = false;
private detailData:
| DetailTypes.getDetailRequestResult["meta"]["header"]["field_groups"]
| null = null;
private detailRow: DetailTypes.getDetailRequestResult["row"] | null = null;
private keyField = "";
private actions:
| DetailTypes.getDetailRequestResult["meta"]["actions"]
| null = null;
public async created() {
await this.init();
}
private async init() {
const data = await Chat.getSdk()
.model(this.model_name)
.detail(this.id, this.name)
.query();
this.detailData = data.meta.header.field_groups;
this.detailRow = data.row;
this.keyField = data.meta.key_field;
this.actions = data.meta.actions;
}
private goTodetail() {
const path = `/${this.$route.params.project}.${this.$route.params.entrance}/detail/${this.model_name}/key/${this.id}`;
this.openUrl(path);
this.hideChat();
}
private openUrl(path: string) {
if (this.drawer) {
this.$emit("drawer", path);
} else {
this.$router.push(path);
}
}
private async execute_action(actionParams) {
let { action_name, container, forward, confirm_caption, authed } =
actionParams;
const x = this.detailRow;
const r: { v: number; id: number } = { v: 0, id: 0 };
r.id = x[this.keyField].value as number;
if (x.uniplat_version) {
r.v = x.uniplat_version.value as number;
}
if (!authed) {
Chat.$emit(EVENTS.ShowModalDialog, {
dialogName: "authors_list",
params: {
actionName: actionParams.action_name,
modelName: this.model_name,
},
});
return;
}
if (container === "page") {
this.$message.warning("该类型操作暂不支持");
} else if (container === "dialog" || container === "none") {
Chat.$emit(EVENTS.ShowModalDialog, {
dialogName: "general_executor_dialog",
params: {
autoSubmit: container === "none",
modelName: this.model_name,
actionName: action_name,
selected: JSON.stringify([r]),
prefilters: JSON.stringify([]),
options: { confirm_caption },
callWhenSuccess: (data) => {
const forward = data.forward;
this.init();
if (!!forward && forward !== "") {
goForward.call(this, forward, this.init);
}
},
},
});
} else if (container === "batch") {
Chat.$emit(EVENTS.ShowModalDialog, {
dialogName: "general_execute_batch",
params: {
model_name: this.model_name,
action_name,
selected_list: JSON.stringify([r]),
prefilters: [],
onSuccessed: this.init,
},
});
} else if (container === "detail_dialog") {
Chat.$emit(EVENTS.ShowModalDialog, {
dialogName: "general_detail_dialog",
params: {
forward: forward,
},
});
} else if (container === "iframe") {
if (
forward.indexOf("http://") !== 0 &&
forward.indexOf("https://") !== 0
) {
if (this.global.$ssr && this.global.$vapperRootPath) {
forward =
"/" +
this.global.$vapperRootPath +
forward.replace(/^\//, "");
}
}
Chat.$emit(EVENTS.ShowModalDialog, {
dialogName: "general_iframe_dialog",
params: {
title: "",
href: forward,
width: "70%",
height: 500,
},
});
} else if (container === "startProcess") {
Chat.$emit(EVENTS.ShowModalDialog, {
dialogName: "start_process_dialog",
params: {
autoSubmit: container === "none",
modelName: this.model_name,
actionName: action_name,
selected: JSON.stringify([r]),
prefilters: {},
filters: {},
options: { confirm_caption },
callWhenSuccess: () => {
this.$emit("datachange");
},
},
});
} else {
if (
forward.indexOf("http://") === 0 ||
forward.indexOf("https://") === 0
) {
window.open(forward, "_blank");
} else {
this.openUrl(forward);
}
}
}
}
</script>
<style lang="less" scoped>
.data-row {
color: #666;
font-size: 14px;
padding: 5px 20px;
}
.data-key {
width: 5em;
}
.data-value {
display: inline-block;
vertical-align: top;
}
.detal-btns {
position: absolute;
bottom: 20px;
right: 0;
left: 0;
text-align: center;
}
.scroll-wrap {
height: calc(100% - 70px);
}
.operation_field {
margin-left: 2px;
/deep/ .el-button {
padding: 0;
}
}
.top-actions {
margin-top: 10px;
padding: 0 20px;
white-space: normal;
.el-button + .el-button {
margin-left: 0;
}
.el-button {
margin-right: 10px;
}
}
</style>
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