Commit 2a016425 by Sixong.Zhu

u

parents a66594be 0452b288
...@@ -13,7 +13,10 @@ ...@@ -13,7 +13,10 @@
> >
{{ getCurrentInputingPeople }}正在输入 {{ getCurrentInputingPeople }}正在输入
</div> </div>
<messages class="flex-fill" /> <messages
class="flex-fill"
@open-pay-message="openPayMessage"
/>
<slot name="chat-right-panel"></slot> <slot name="chat-right-panel"></slot>
</div> </div>
<div <div
...@@ -36,49 +39,31 @@ ...@@ -36,49 +39,31 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { import { Component, Provide, Ref, Vue, Watch } from "vue-property-decorator";
Component,
Prop,
Provide,
Ref,
Vue,
Watch,
} from "vue-property-decorator";
import MessageInput from "@/customer-service/components/message-input.vue"; import MessageInput from "@/customer-service/components/message-input.vue";
import messages from "@/customer-service/components/message-list.vue"; import messages from "@/customer-service/components/message-list.vue";
import { ChatStore, chatStore } from "@/customer-service/store/model"; import { ChatStore, chatStore } from "@/customer-service/store/model";
import Chat from "@/customer-service/xim"; import Chat from "@/customer-service/xim";
type RoomInfoTab = "customer" | "order";
@Component({ components: { MessageInput, messages } }) @Component({ components: { MessageInput, messages } })
export default class ChatRoom extends Vue { export default class ChatRoom extends Vue {
@Ref("chatBox") chatBox!: Element; @Ref("chatBox") private readonly chatBox!: Element;
@Ref("top") refTop!: Element; @Ref("top") private readonly refTop!: Element;
@Ref("bottom") refBottom!: Element; @Ref("bottom") private readonly refBottom!: Element;
@Ref("resize") refResize!: Element; @Ref("resize") private readonly refResize!: Element;
@chatStore.State(ChatStore.STATE_CHAT_CURRENT_CHAT_ID) @chatStore.State(ChatStore.STATE_CHAT_CURRENT_CHAT_ID)
private readonly chatId!: 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.Mutation(ChatStore.MUTATION_CLEAR_CURRENT_CHAT_MEMBERS) @chatStore.Mutation(ChatStore.MUTATION_CLEAR_CURRENT_CHAT_MEMBERS)
private readonly clearChatMembers!: 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_INPUTING) @chatStore.State(ChatStore.STATE_CURRENT_CHAT_INPUTING)
private readonly currentInputPeople!: ChatStore.STATE_CURRENT_CHAT_INPUTING; private readonly currentInputPeople!: ChatStore.STATE_CURRENT_CHAT_INPUTING;
@chatStore.State(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; 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_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 readonly isChatMember!: ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER;
...@@ -89,11 +74,6 @@ ...@@ -89,11 +74,6 @@
return this.isChatMember && this.chatError !== this.chatId; return this.isChatMember && this.chatError !== this.chatId;
} }
private allChatList = { list: [] };
@Prop({ type: Function })
private close?: () => void;
@Provide() showReadSummary = true; @Provide() showReadSummary = true;
@Watch("currentChatUniplatId") @Watch("currentChatUniplatId")
...@@ -102,28 +82,12 @@ ...@@ -102,28 +82,12 @@
this.clearChatMembers(); this.clearChatMembers();
} }
private activeTab: RoomInfoTab = "customer";
private get getCurrentInputingPeople() { private get getCurrentInputingPeople() {
return this.currentInputPeople return this.currentInputPeople
.map(() => "" /* this.userInfo[k].name */) .map(() => "" /* this.userInfo[k].name */)
.join("、"); .join("、");
} }
private get currentChat() {
const chatId = this.chatId;
const result = this.myChatList.find((k) => k.chat_id === chatId);
return result ?? {};
}
private get customerInfoTabShow() {
return this.activeTab === "customer";
}
private get orderInfoTabShow() {
return this.activeTab === "order";
}
private onError(msg: string) { private onError(msg: string) {
Chat.error(msg); Chat.error(msg);
} }
...@@ -163,6 +127,10 @@ ...@@ -163,6 +127,10 @@
((this.refBottom as HTMLElement).style.height = ((this.refBottom as HTMLElement).style.height =
this.chatBox.clientHeight - this.refTop.clientHeight + "px"); this.chatBox.clientHeight - this.refTop.clientHeight + "px");
} }
private openPayMessage(id: number) {
this.$emit("open-pay-message", id);
}
} }
</script> </script>
...@@ -183,6 +151,10 @@ ...@@ -183,6 +151,10 @@
} }
} }
.chat-room-con {
min-width: 400px;
}
.chat-panel { .chat-panel {
height: 100%; height: 100%;
.chat-area, .chat-area,
......
import { MessageType } from "@/customer-service/model"; import { MessageType } from "@/customer-service/model";
import { CardMessage } from '@/customer-service/model/card';
const mapping = new Map<MessageType, string>([ const mapping = new Map<MessageType, string>([
[MessageType.Image, '图片'], [MessageType.Image, '图片'],
...@@ -9,9 +10,14 @@ const mapping = new Map<MessageType, string>([ ...@@ -9,9 +10,14 @@ const mapping = new Map<MessageType, string>([
[MessageType.MyPurchasePlan, '我的采购计划'], [MessageType.MyPurchasePlan, '我的采购计划'],
[MessageType.MyWelfare, '我的福利'], [MessageType.MyWelfare, '我的福利'],
[MessageType.QuestionAnswer, '问答'], [MessageType.QuestionAnswer, '问答'],
[MessageType.Pay, '付款通知'],
[MessageType.PayV1, '付款通知'],
[MessageType.Refund, '退款通知'],
[MessageType.RefundV1, '退款通知'],
]) ])
export function parserMessage(type: MessageType, rawMsg: string) { export function parserMessage(type: MessageType, rawMsg: string) {
try {
if (!type) return ""; if (!type) return "";
if (!rawMsg) return ""; if (!rawMsg) return "";
if (type === MessageType.Text) { if (type === MessageType.Text) {
...@@ -25,9 +31,18 @@ export function parserMessage(type: MessageType, rawMsg: string) { ...@@ -25,9 +31,18 @@ export function parserMessage(type: MessageType, rawMsg: string) {
if (type === MessageType.Notify) { if (type === MessageType.Notify) {
return rawMsg; return rawMsg;
} }
if (type === MessageType.Card) {
const p = JSON.parse(rawMsg) as CardMessage;
if (p && p.title) {
return p.title || '通知';
}
}
const t = mapping.get(type) const t = mapping.get(type)
if (t) { if (t) {
return `[${t}]`; return `[${t}]`;
} }
return `[系统自动回复]`; return `[系统自动回复]`;
} catch {
return ""
}
} }
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
v-else-if="fileFailed2Load" v-else-if="fileFailed2Load"
title="[语音加载失败]" title="[语音加载失败]"
></i> ></i>
<text-message v-model="value" v-if="backend" />
</div> </div>
</template> </template>
...@@ -27,12 +29,16 @@ ...@@ -27,12 +29,16 @@
import { Component, Ref } from "vue-property-decorator"; import { Component, Ref } from "vue-property-decorator";
import BaseMessage from "./index"; import BaseMessage from "./index";
import VoiceIcon from "./voice.vue"; import VoiceIcon from "./voice.vue";
import TextMessage from "./text-message.vue";
import Chat from "@/customer-service/xim";
@Component({ components: { VoiceIcon } }) @Component({ components: { VoiceIcon, TextMessage } })
export default class Index extends BaseMessage { export default class Index extends BaseMessage {
@Ref("audio") @Ref("audio")
private readonly audioRef!: HTMLAudioElement; private readonly audioRef!: HTMLAudioElement;
private readonly backend = Chat.isBackend();
private playing = false; private playing = false;
private get duration() { private get duration() {
...@@ -95,6 +101,13 @@ ...@@ -95,6 +101,13 @@
font-size: 16px; font-size: 16px;
} }
} }
.inline-text {
position: absolute;
bottom: 0;
left: 40px;
}
.my-message { .my-message {
.voice-message { .voice-message {
> div { > div {
......
<template> <template>
<div class="msg-detail file-message d-flex" @dblclick="openFile"> <div
class="msg-detail file-message d-flex"
@dblclick="openFile"
@click="download"
>
<div <div
class="file-message-info" class="file-message-info"
:class="{ 'd-flex align-items-center': !messageBody.msg.size }" :class="{ 'd-flex align-items-center': !messageBody.msg.size }"
...@@ -15,21 +19,8 @@ ...@@ -15,21 +19,8 @@
{{ format(messageBody.msg.size) }} {{ format(messageBody.msg.size) }}
</div> </div>
</div> </div>
<file-icon :value="fileIcon"></file-icon>
<a <file-icon :value="fileIcon"></file-icon>
class="
d-flex
align-items-center
justify-content-center
download-icon
"
:href="messageRealUrl"
:download="getAttachment"
title="下载文件"
>
<img src="~@/customer-service/imgs/download.png" alt="Download" />
</a>
</div> </div>
</template> </template>
...@@ -65,13 +56,6 @@ ...@@ -65,13 +56,6 @@
@Component({ components: { FileIcon } }) @Component({ components: { FileIcon } })
export default class Index extends BaseMessage { export default class Index extends BaseMessage {
protected get getAttachment() {
if (this.messageBody) {
return this.messageBody.msg.name;
}
return "文件下载";
}
protected get fileIcon() { protected get fileIcon() {
if (this.value) { if (this.value) {
return getFileType(this.messageBody.msg.name); return getFileType(this.messageBody.msg.name);
...@@ -80,6 +64,10 @@ ...@@ -80,6 +64,10 @@
return FileType.Others; return FileType.Others;
} }
private download() {
window.open(this.messageRealUrl);
}
protected format(v: number) { protected format(v: number) {
return formatSize(v); return formatSize(v);
} }
...@@ -92,9 +80,14 @@ ...@@ -92,9 +80,14 @@
<style lang="less" scoped> <style lang="less" scoped>
.file-message { .file-message {
background-color: transparent !important; background-color: #f5f6fa;
border-radius: 4px !important; border-radius: 6px !important;
border: 1px solid #c5d4e5; cursor: pointer;
&.my-message {
background-color: #dbf2ff;
}
.file-message-name { .file-message-name {
max-width: 130px; max-width: 130px;
word-break: break-all; word-break: break-all;
......
<template> <template>
<div <div
class="msg-detail image-message" class="image-message"
:class="{ 'image-404': fileFailed2Load }" :class="{ 'image-404': fileFailed2Load }"
@dblclick="dbClick" @dblclick="dbClick"
@click="open" @click="open"
...@@ -52,12 +52,11 @@ ...@@ -52,12 +52,11 @@
<style lang="less" scoped> <style lang="less" scoped>
.image-message { .image-message {
background-color: transparent !important;
border-radius: 4px !important;
border: 1px solid #c5d4e5;
line-height: 1; line-height: 1;
max-width: 300px; max-width: 300px;
box-sizing: content-box; box-sizing: content-box;
background-color: unset;
img { img {
width: 100%; width: 100%;
} }
...@@ -76,11 +75,4 @@ ...@@ -76,11 +75,4 @@
margin-left: 0; margin-left: 0;
} }
} }
.my-message {
&.image-message:not(.image-404) {
background-color: transparent !important;
border-radius: 4px !important;
border: 1px solid #c5d4e5;
}
}
</style> </style>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-3">
<stop stop-color="#F95B3C" offset="0%"></stop>
<stop stop-color="#F94623" offset="100%"></stop>
</linearGradient>
</defs>
<g id="专项订单" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="专项订单-结束订单的咨询-通知" transform="translate(-88.000000, -204.000000)">
<g id="交易卡片类型" transform="translate(12.000000, 20.000000)">
<g id="扣费类型-copy-3" transform="translate(0.000000, 166.000491)">
<g id="客服-待扣费" transform="translate(56.000000, 0.000000)">
<g id="对话内容">
<g id="2" transform="translate(124.000000, 53.000491) scale(-1, 1) translate(-124.000000, -53.000491) ">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
</g>
<g id="1" transform="translate(124.000000, 38.000246) scale(-1, 1) translate(-124.000000, -38.000246) ">
<use fill="url(#linearGradient-3)" xlink:href="#path-4"></use>
<use fill-opacity="0.3" fill="#FFFFFF" xlink:href="#path-4"></use>
</g>
</g>
<g id="Group-2" transform="translate(20.000000, 18.000000)" stroke="#FFFFFF" stroke-linejoin="round" stroke-width="2">
<path d="M20,39 C30.4934102,39 39,30.4934102 39,20 C39,9.50658975 30.4934102,1 20,1 C9.50658975,1 1,9.50658975 1,20 C1,30.4934102 9.50658975,39 20,39 Z M10,20 L16.22774,28.0070942 C16.56681,28.4430414 17.1950856,28.5215759 17.6310328,28.1825058 C17.6909399,28.1359114 17.745348,28.0826486 17.7932062,28.0237462 L30,13 L30,13" id="Oval-3"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<div
class="pay-message d-flex flex-column"
:class="[
messageClass,
{ 'user-side': !backend || !isChatMember, click: isChatMember },
]"
@click="view"
>
<div class="d-flex align-items-center flex-fill pay-msg-body">
<span :class="icon" class="icon"></span>
<div class="texts flex-fill">
<div class="text-left">{{ title }}</div>
<div class="d-flex justify-content-between">
<span>{{ amount | currency }}</span>
<span>{{ status }}</span>
</div>
</div>
</div>
<div class="pay-method text-left">{{ method }}</div>
</div>
</template>
<script lang="ts">
import {
PayMethod,
payMethodMapping,
PayStatus,
payStatusMapping,
} from "@/customer-service/model";
import { PayMessageBody } from "@/customer-service/xim/models/chat";
import { Component } from "vue-property-decorator";
import BaseMessage from "./index";
import Chat from "@/customer-service/xim";
import { ChatStore, chatStore } from "@/customer-service/store/model";
@Component({ components: {} })
export default class Index extends BaseMessage {
@chatStore.State(ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER)
private readonly isChatMember!: ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER;
protected backend = Chat.isBackend();
private get payData() {
return this.messageBody.msg as PayMessageBody;
}
private get title() {
return this.payData.itemName;
}
private get amount() {
return +this.payData.amount;
}
private get status() {
return payStatusMapping.get(+this.payData.status);
}
private get method() {
return payMethodMapping.get(+this.payData.paymentFunction);
}
private get icon() {
const m = +this.payData.paymentFunction;
const s = +this.payData.status;
if (m === PayMethod.Balance && s === PayStatus.UnPay) {
return "icon-1";
}
if (m === PayMethod.CardTransfer && s === PayStatus.UnPay) {
return "icon-2";
}
if (s === PayStatus.WaitRefund) {
return "icon-3";
}
return "completed";
}
private get messageClass() {
const m = +this.payData.paymentFunction;
const s = +this.payData.status;
if (m === PayMethod.Balance) {
if (s === PayStatus.UnPay) {
return "balance-unpay";
}
if (s === PayStatus.Paied) {
return "balance-paied";
}
}
if (m === PayMethod.CardTransfer) {
if (s === PayStatus.UnPay) {
return "card-unpay";
}
if (s === PayStatus.Paied) {
return "card-paied";
}
}
if (s === PayStatus.WaitRefund) {
return "refund-unpay";
}
if (s === PayStatus.Refund) {
return "refund-paied";
}
return "default";
}
private view() {
this.isChatMember &&
this.$emit("open-pay-message", this.payData.paymentId);
}
}
</script>
<style lang="less" scoped>
.pay-message {
border-radius: 10px 0 10px 10px;
width: 248px;
height: 106px;
color: #fff;
&.click {
cursor: pointer;
}
&.user-side {
border-radius: 0 10px 10px 10px;
}
&.default,
&.balance-unpay {
background: linear-gradient(180deg, #f95b3c 0%, #f94623 100%);
}
&.balance-paied {
background: linear-gradient(180deg, #f95b3c 0%, #f94623 100%)
rgba(255, 255, 255, 0.3);
}
&.card-unpay {
background: linear-gradient(180deg, #ff884d 0%, #ff7a38 100%);
}
&.card-paied {
background: linear-gradient(180deg, #ff884d 0%, #ff7a38 100%)
rgba(255, 255, 255, 0.3);
}
&.refund-unpay {
background: linear-gradient(180deg, #faad14 0%, #faa40f 100%);
}
&.refund-paied {
background: linear-gradient(180deg, #faad14 0%, #faa40f 100%)
rgba(255, 255, 255, 0.3);
}
.pay-msg-body {
padding: 20px;
}
.pay-method {
background-color: #fff;
color: #8e8d99;
padding: 5px 10px;
border-radius: 0 0 10px 10px;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.06);
}
.icon {
width: 40px;
height: 40px;
display: inline-block;
margin-right: 20px;
background-repeat: no-repeat;
&.icon-1 {
background-image: url("./pay-status-1.svg");
}
&.icon-2 {
background-image: url("./pay-status-2.svg");
}
&.icon-3 {
background-image: url("./pay-status-3.svg");
}
&.completed {
background-image: url("./pay-completed.svg");
}
}
.texts {
div:last-child {
margin-left: -2px;
}
}
}
</style>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-88.000000, -320.000000)">
<g transform="translate(12.000000, 20.000000)">
<g transform="translate(0.000000, 282.000982)">
<g transform="translate(56.000000, 0.000000)">
<g>
<path d="M10,0 L248,0 L248,0 L248,76.0004911 L0,76.0004911 L0,10 C-2.45271059e-15,4.4771525 4.4771525,1.01453063e-15 10,0 Z" id="1" fill="url(#linearGradient-3)" transform="translate(124.000000, 38.000246) scale(-1, 1) translate(-124.000000, -38.000246) "></path>
</g>
<g transform="translate(20.000000, 18.000000)">
<path d="M38.0401212,26.4966147 C34.4139232,36.354512 23.3977849,41.4372833 13.4348876,37.8493021 C8.06509634,35.9154559 4.09724597,31.8574701 2.14778094,26.9833815 L2.16352869,27.0357731 C1.61303033,25.6706127 1.21797399,24.227429 1,22.7276344 L4.6900288,24.0565404 M1.96127879,13.5033853 C5.58747682,3.64548801 16.6036151,-1.43728327 26.5665124,2.15069793 C31.3994686,3.89121133 35.1565883,7.49572098 37.8378713,12.9642269 C38.3871931,14.3264697 38.7817367,15.7664026 39,17.2627503 L35.3172901,15.9364801" id="Path" stroke="#FFFFFF" stroke-width="2" stroke-linecap="square"></path>
<text id="转" font-family="PingFang-SC-Medium, PingFang SC" font-size="18" font-weight="400" line-spacing="18" fill="#FFFFFF">
<tspan x="11" y="26"></tspan>
</text>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-88.000000, -320.000000)">
<g transform="translate(12.000000, 20.000000)">
<g transform="translate(0.000000, 282.000982)">
<g transform="translate(56.000000, 0.000000)">
<g>
<path d="M10,0 L248,0 L248,0 L248,76.0004911 L0,76.0004911 L0,10 C-2.45271059e-15,4.4771525 4.4771525,1.01453063e-15 10,0 Z" id="1" fill="url(#linearGradient-3)" transform="translate(124.000000, 38.000246) scale(-1, 1) translate(-124.000000, -38.000246) "></path>
</g>
<g transform="translate(20.000000, 18.000000)">
<path d="M38.0401212,26.4966147 C34.4139232,36.354512 23.3977849,41.4372833 13.4348876,37.8493021 C8.06509634,35.9154559 4.09724597,31.8574701 2.14778094,26.9833815 L2.16352869,27.0357731 C1.61303033,25.6706127 1.21797399,24.227429 1,22.7276344 L4.6900288,24.0565404 M1.96127879,13.5033853 C5.58747682,3.64548801 16.6036151,-1.43728327 26.5665124,2.15069793 C31.3994686,3.89121133 35.1565883,7.49572098 37.8378713,12.9642269 C38.3871931,14.3264697 38.7817367,15.7664026 39,17.2627503 L35.3172901,15.9364801" id="Path" stroke="#FFFFFF" stroke-width="2" stroke-linecap="square"></path>
<text id="转" font-family="PingFang-SC-Medium, PingFang SC" font-size="18" font-weight="400" line-spacing="18" fill="#FFFFFF">
<tspan x="11" y="26"></tspan>
</text>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-88.000000, -320.000000)">
<g transform="translate(12.000000, 20.000000)">
<g transform="translate(0.000000, 282.000982)">
<g transform="translate(56.000000, 0.000000)">
<g>
<path d="M10,0 L248,0 L248,0 L248,76.0004911 L0,76.0004911 L0,10 C-2.45271059e-15,4.4771525 4.4771525,1.01453063e-15 10,0 Z" id="1" fill="url(#linearGradient-3)" transform="translate(124.000000, 38.000246) scale(-1, 1) translate(-124.000000, -38.000246) "></path>
</g>
<g transform="translate(20.000000, 18.000000)">
<path d="M38.0401212,26.4966147 C34.4139232,36.354512 23.3977849,41.4372833 13.4348876,37.8493021 C8.06509634,35.9154559 4.09724597,31.8574701 2.14778094,26.9833815 L2.16352869,27.0357731 C1.61303033,25.6706127 1.21797399,24.227429 1,22.7276344 L4.6900288,24.0565404 M1.96127879,13.5033853 C5.58747682,3.64548801 16.6036151,-1.43728327 26.5665124,2.15069793 C31.3994686,3.89121133 35.1565883,7.49572098 37.8378713,12.9642269 C38.3871931,14.3264697 38.7817367,15.7664026 39,17.2627503 L35.3172901,15.9364801" id="Path" stroke="#FFFFFF" stroke-width="2" stroke-linecap="square"></path>
<text id="转" font-family="PingFang-SC-Medium, PingFang SC" font-size="18" font-weight="400" line-spacing="18" fill="#FFFFFF">
<tspan x="11" y="26">退</tspan>
</text>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>bg_head</title>
<defs>
<circle id="path-1" cx="20" cy="20" r="20"></circle>
<filter x="-17.5%" y="-12.5%" width="135.0%" height="135.0%" filterUnits="objectBoundingBox" id="filter-3">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.846 0 0 0 0 0.8712 0 0 0 0 0.9 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-4">
<stop stop-color="#FF884D" offset="0%"></stop>
<stop stop-color="#FF7A38" offset="100%"></stop>
</linearGradient>
</defs>
<g id="专项订单" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="专项订单-结束订单的咨询-通知" transform="translate(-12.000000, -302.000000)">
<g id="交易卡片类型" transform="translate(12.000000, 20.000000)">
<g id="扣费类型-copy" transform="translate(0.000000, 282.000982)">
<g id="deal_payment" transform="translate(0.000000, 0.000491)">
<g id="bg_head" transform="translate(4.000000, 2.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Oval-Copy-3">
<use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-1"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
</g>
<g id="Group-2" mask="url(#mask-2)" stroke="url(#linearGradient-4)" stroke-linecap="square" stroke-width="1.5">
<g transform="translate(10.000000, 10.000000)" id="Path">
<path d="M18.7826906,13.1628256 C17.0173047,17.962065 11.6541847,20.4365721 6.80382687,18.6897918 C4.18958638,17.7483141 2.25786975,15.7727157 1.30878809,13.3998042 L1.31645476,13.4253106 C1.04844898,12.760693 0.856118915,12.0580905 0.75,11.3279273 L2.54646139,11.9748947 M1.21799099,6.83717444 C2.98337687,2.03793495 8.34649684,-0.436572119 13.1968547,1.3102082 C15.5497413,2.15756341 17.3788653,3.91239048 18.6842268,6.57468939 C18.9516598,7.23788655 19.1437402,7.93890651 19.25,8.66739159 L17.4571018,8.0217074 M6.91666667,8.76666667 L13.0833333,8.76666667 M6.91666667,11.2333333 L13.0833333,11.2333333 M10,13.7 L10,8.76666667 M10,8.76666667 L12.4666667,6.3 M10,8.76666667 L7.53333333,6.3"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
...@@ -13,20 +13,20 @@ ...@@ -13,20 +13,20 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component } from "vue-property-decorator"; import { Component } from "vue-property-decorator";
import BaseMessage from "./index"; import BaseMessage from "./index";
import VideoPlayerIcon from "./video-player-icon.vue"; import VideoPlayerIcon from "./video-player-icon.vue";
@Component({ components: { VideoPlayerIcon } }) @Component({ components: { VideoPlayerIcon } })
export default class Index extends BaseMessage { export default class Index extends BaseMessage {
mounted() { mounted() {
this.buildMessageUrl(); this.buildMessageUrl();
} }
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.video-message { .video-message {
height: 160px; height: 160px;
width: 200px; width: 200px;
background-color: #000 !important; background-color: #000 !important;
......
...@@ -2,49 +2,51 @@ ...@@ -2,49 +2,51 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink"
t="1595840909244"
class="icon" class="icon"
viewBox="0 0 1024 1024" viewBox="0 0 1024 1024"
version="1.1" version="1.1"
p-id="11656"
:width="size" :width="size"
:height="size" :height="size"
> >
<path <path
d="M56.888889 512a105.016889 102.4 90 1 0 204.8 0 105.016889 102.4 90 1 0-204.8 0Z" d="M56.888889 512a105.016889 102.4 90 1 0 204.8 0 105.016889 102.4 90 1 0-204.8 0Z"
fill="#8D959D" :fill="fill"
p-id="11657"
v-if="!status || status >= 1" v-if="!status || status >= 1"
/> />
<path <path
d="M425.415111 782.449778a68.266667 68.266667 0 0 1-97.792-95.288889A249.912889 249.912889 0 0 0 398.222222 512c0-66.787556-25.713778-129.137778-70.542222-175.160889a68.266667 68.266667 0 0 1 97.735111-95.288889A386.389333 386.389333 0 0 1 534.755556 512c0 102.627556-39.822222 199.111111-109.340445 270.449778z" d="M425.415111 782.449778a68.266667 68.266667 0 0 1-97.792-95.288889A249.912889 249.912889 0 0 0 398.222222 512c0-66.787556-25.713778-129.137778-70.542222-175.160889a68.266667 68.266667 0 0 1 97.735111-95.288889A386.389333 386.389333 0 0 1 534.755556 512c0 102.627556-39.822222 199.111111-109.340445 270.449778z"
fill="#8D959D" :fill="fill"
p-id="11658"
v-if="!status || status >= 2" v-if="!status || status >= 2"
/> />
<path <path
d="M618.496 980.48a68.266667 68.266667 0 0 1-97.792-95.288889A532.707556 532.707556 0 0 0 671.288889 512a532.707556 532.707556 0 0 0-150.584889-373.191111A68.266667 68.266667 0 0 1 618.496 43.52 669.184 669.184 0 0 1 807.822222 512c0 177.891556-68.835556 344.917333-189.326222 468.48z" d="M618.496 980.48a68.266667 68.266667 0 0 1-97.792-95.288889A532.707556 532.707556 0 0 0 671.288889 512a532.707556 532.707556 0 0 0-150.584889-373.191111A68.266667 68.266667 0 0 1 618.496 43.52 669.184 669.184 0 0 1 807.822222 512c0 177.891556-68.835556 344.917333-189.326222 468.48z"
fill="#8D959D" :fill="fill"
p-id="11659"
v-if="!status || status >= 3" v-if="!status || status >= 3"
/> />
</svg> </svg>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { Component, Prop, Vue, Watch } from "vue-property-decorator";
@Component({ components: {} }) @Component({ components: {} })
export default class VoiceIcon extends Vue { export default class VoiceIcon extends Vue {
@Prop({ default: 25 }) @Prop({ default: 25 })
private size!: number; private size!: number;
@Prop() @Prop()
private loading!: boolean; private loading!: boolean;
@Prop()
private readonly white!: boolean;
private status = 0; private status = 0;
private interval = 0; private interval = 0;
private get fill() {
return this.white ? "#fff" : "#8D959D";
}
@Watch("loading") @Watch("loading")
private onLoadingChanged() { private onLoadingChanged() {
if (this.loading) { if (this.loading) {
...@@ -65,5 +67,5 @@ export default class VoiceIcon extends Vue { ...@@ -65,5 +67,5 @@ export default class VoiceIcon extends Vue {
beforeDestroy() { beforeDestroy() {
clearInterval(this.interval); clearInterval(this.interval);
} }
} }
</script> </script>
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
:shape="shape" :shape="shape"
@open="open" @open="open"
@withdraw="refresh" @withdraw="refresh"
@open-pay-message="openPayMessage"
/> />
</div> </div>
</template> </template>
...@@ -395,6 +396,10 @@ ...@@ -395,6 +396,10 @@
private refresh() { private refresh() {
this.fetchNewMsg(); this.fetchNewMsg();
} }
private openPayMessage(id: number) {
this.$emit("open-pay-message", id);
}
} }
</script> </script>
......
...@@ -12,13 +12,22 @@ ...@@ -12,13 +12,22 @@
<div <div
class="msg-name no-selection" class="msg-name no-selection"
:class="{ 'algin-left': !isMyMessage }" :class="{ 'algin-left': !isMyMessage }"
v-if="!isWithdrawMessage" v-if="
!isWithdrawMessage &&
!isQuestionAnswerMessage &&
needReadTip
"
> >
{{ isQuestionAnswerMessage ? "" : userName }} {{ userName }}
</div> </div>
<div class="d-flex"> <div class="d-flex" :class="{ 'justify-content-end': isMyMessage }">
<template <template
v-if="backend && showReadSummary && !isWithdrawMessage" v-if="
backend &&
showReadSummary &&
!isWithdrawMessage &&
needReadTip
"
> >
<div v-if="isMyMessage" class="msg-read pos-rel"> <div v-if="isMyMessage" class="msg-read pos-rel">
<span <span
...@@ -59,6 +68,7 @@ ...@@ -59,6 +68,7 @@
v-if="messageComponent" v-if="messageComponent"
v-model="data" v-model="data"
@open="openFile" @open="openFile"
@open-pay-message="openPayMessage"
/> />
<avatar <avatar
v-if="!isQuestionAnswerMessage && !isWithdrawMessage" v-if="!isQuestionAnswerMessage && !isWithdrawMessage"
...@@ -154,6 +164,7 @@ ...@@ -154,6 +164,7 @@
import PurchasePlanMessage from "./message-item/purchase-plan-message.vue"; import PurchasePlanMessage from "./message-item/purchase-plan-message.vue";
import MyWelfareMessage from "./message-item/my-welfare-message.vue"; import MyWelfareMessage from "./message-item/my-welfare-message.vue";
import QuestionAnswerMessage from "./message-item/question-answer-message.vue"; import QuestionAnswerMessage from "./message-item/question-answer-message.vue";
import PayMessage from "./message-item/pay-message.vue";
import { ChatRole } from "@/customer-service/model"; import { ChatRole } from "@/customer-service/model";
import { getUserMapping } from "../utils/user-info"; import { getUserMapping } from "../utils/user-info";
import Xim from "@/customer-service/xim"; import Xim from "@/customer-service/xim";
...@@ -172,6 +183,10 @@ ...@@ -172,6 +183,10 @@
[dto.MessageType.MyWelfare, "my-welfare-message"], [dto.MessageType.MyWelfare, "my-welfare-message"],
[dto.MessageType.QuestionAnswer, "question-answer-message"], [dto.MessageType.QuestionAnswer, "question-answer-message"],
[dto.MessageType.Action, "action-message"], [dto.MessageType.Action, "action-message"],
[dto.MessageType.Pay, "pay-message"],
[dto.MessageType.PayV1, "pay-message"],
[dto.MessageType.Refund, "pay-message"],
[dto.MessageType.RefundV1, "pay-message"],
]); ]);
@Component({ @Component({
...@@ -188,6 +203,7 @@ ...@@ -188,6 +203,7 @@
MyWelfareMessage, MyWelfareMessage,
QuestionAnswerMessage, QuestionAnswerMessage,
ActionMessage, ActionMessage,
PayMessage,
}, },
}) })
export default class Message extends Vue { export default class Message extends Vue {
...@@ -251,6 +267,15 @@ ...@@ -251,6 +267,15 @@
return false; return false;
} }
private get needReadTip() {
return (
this.data.type !== dto.MessageType.Pay &&
this.data.type !== dto.MessageType.Refund &&
this.data.type !== dto.MessageType.PayV1 &&
this.data.type !== dto.MessageType.RefundV1
);
}
private get isWithdrawMessage() { private get isWithdrawMessage() {
return this.data.type === dto.MessageType.Withdraw; return this.data.type === dto.MessageType.Withdraw;
} }
...@@ -405,14 +430,19 @@ ...@@ -405,14 +430,19 @@
this.data && this.data &&
!this.isAllRead && !this.isAllRead &&
ximInstance ximInstance
.queryNextPageMsg("group", this.chatId, this.data.id - 1, 1) .queryNextPageMsg(
"group",
this.chatId as number,
this.data.id - 1,
1
)
.then((m) => { .then((m) => {
if (m && m.length) { if (m && m.length) {
const t = m[0]; const t = m[0];
t.read_count && t.read_count &&
t.read_count !== this.data.read_count && t.read_count !== this.data.read_count &&
this.updateMessage({ this.updateMessage({
chat: this.chatId, chat: this.chatId as number,
start: this.data.id, start: this.data.id,
all: (this.manualAllRead = all: (this.manualAllRead =
t.read_count === t.total_read_count), t.read_count === t.total_read_count),
...@@ -485,6 +515,10 @@ ...@@ -485,6 +515,10 @@
private closeKeywordPopover() { private closeKeywordPopover() {
document.body.click(); document.body.click();
} }
private openPayMessage(id: number) {
this.$emit("open-pay-message", id);
}
} }
</script> </script>
...@@ -573,7 +607,7 @@ ...@@ -573,7 +607,7 @@
margin-top: 10px; margin-top: 10px;
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
background: #f5f6fa; background-color: #f5f6fa;
border-radius: 0px 8px 8px; border-radius: 0px 8px 8px;
padding: 10px; padding: 10px;
word-break: break-word; word-break: break-word;
......
...@@ -173,6 +173,29 @@ class ChatCacheDatabaseController { ...@@ -173,6 +173,29 @@ class ChatCacheDatabaseController {
}); });
} }
public updateChat4UnreadCount(chat: number, unread: number) {
return new Promise<void>((resolve) => {
if (this.db) {
const store = this.buildStore(this.chatListKey);
const t = store.get(chat);
t.onsuccess = (r) => {
const chat = (r.target as any).result as Chat;
if (chat) {
chat.unread_msg_count = unread;
const u = store.put(chat, chat.id);
u.onsuccess = () => resolve();
u.onerror = () => resolve();
} else {
resolve();
}
};
t.onerror = () => resolve();
} else {
resolve();
}
});
}
public setRead(chat: number) { public setRead(chat: number) {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
if (this.db) { if (this.db) {
...@@ -351,6 +374,7 @@ class ChatCacheDatabaseController { ...@@ -351,6 +374,7 @@ class ChatCacheDatabaseController {
for (const item of source2) { for (const item of source2) {
const t = source1.find((i) => i.id === item.id); const t = source1.find((i) => i.id === item.id);
if (t) { if (t) {
item.unread_msg_count = Math.max(item.unread_msg_count, t.unread_msg_count);
const index = source1.indexOf(t); const index = source1.indexOf(t);
source1[index] = item; source1[index] = item;
} else { } else {
......
export interface CardMessage {
title: string;
date: string;
desc?: string;
desc1?: string;
desc2?: string;
values?: { key: string; value: string }[];
params?: any;
path?: string;
}
import type { UniplatSdk } from "uniplat-sdk"; import type { UniplatSdk } from "uniplat-sdk";
export * from "./order";
export * from "./order-product"
export const enum ChatRole { export const enum ChatRole {
Default = 25, Default = 25,
...@@ -47,13 +49,13 @@ export const enum ServiceType { ...@@ -47,13 +49,13 @@ export const enum ServiceType {
export const enum ImEnvironment { export const enum ImEnvironment {
Dev = 1, Dev = 1,
Stage, Stage,
Pro Pro,
} }
export const socketMapping = new Map<ImEnvironment, string>([ export const socketMapping = new Map<ImEnvironment, string>([
[ImEnvironment.Dev, 'ws://hro.channel.jinsehuaqin.com:8080/ws'], [ImEnvironment.Dev, "ws://hro.channel.jinsehuaqin.com:8080/ws"],
[ImEnvironment.Stage, 'wss://pre-channel.qinqinxiaobao.com/ws'], [ImEnvironment.Stage, "wss://pre-channel.qinqinxiaobao.com/ws"],
[ImEnvironment.Pro, 'wss://channel.qinqinxiaobao.com/ws'], [ImEnvironment.Pro, "wss://channel.qinqinxiaobao.com/ws"],
]); ]);
export type TokenStringGetter = () => Promise<string>; export type TokenStringGetter = () => Promise<string>;
...@@ -111,8 +113,13 @@ export const enum MessageType { ...@@ -111,8 +113,13 @@ export const enum MessageType {
MyWelfare = "my_welfare", MyWelfare = "my_welfare",
QuestionAnswer = "question_answer", QuestionAnswer = "question_answer",
Action = "action", Action = "action",
Notify = 'notify', Notify = "notify",
MpNavigate = "mp-navigate", MpNavigate = "mp-navigate",
PayV1 = 'gpay',
Pay = "gpay2",
RefundV1 = 'grefund',
Refund = "grefund2",
Card = 'card'
} }
export const enum MessageHandled { export const enum MessageHandled {
...@@ -285,3 +292,11 @@ export interface GetAllChatListParams { ...@@ -285,3 +292,11 @@ export interface GetAllChatListParams {
page_size?: number; page_size?: number;
last_cs_eid?: string | number; last_cs_eid?: string | number;
} }
export const enum IMCatalog {
BiJie = "大爱毕节",
Qqxb = "亲亲小保",
Hrs100 = "HRS100",
Flb = "福利宝",
Order = "专项业务订单",
}
import { Chat } from "../xim/models/chat";
export interface ChatGroup extends Chat {
children?: ChatGroup[];
}
export const enum OrderStatus {
None,
Waiting,
Progressing,
Finished,
Failed,
UserCancelled,
AdminCancelled,
Deleted,
}
export const statusMapping = new Map<OrderStatus, string>([
[OrderStatus.None, ""],
[OrderStatus.Waiting, "等待处理"],
[OrderStatus.Progressing, "处理中"],
[OrderStatus.Finished, "已完成"],
[OrderStatus.Failed, "处理失败"],
[OrderStatus.UserCancelled, "已取消"],
[OrderStatus.AdminCancelled, "已取消"],
[OrderStatus.Deleted, "已删除"],
]);
export interface OrderTableListItem {
id: string;
v: number;
no: string;
title: string;
time: string;
chat: number;
status: OrderStatus;
status_label: string;
remark: string;
/**
* 待支付金额
*/
PayAmount: number;
/**
* 已支付金额
*/
PaidAmount: number;
/**
* 待退款金额
*/
RefundAmount: number;
/**
* 已退款金额
*/
RefundedAmount: number;
remarkTitle: string;
remarkContent: string;
chatTypeCode: string;
lastMsgContent: string;
lastMsgTime: string;
unreadCount: number;
}
export const orderPredict = {
id: "ID",
v: "uniplat_version",
no: "OrderDocNo",
title: "ProductId#product.OuterName",
status: "Status_label",
remark: "Remark",
time: "CreatedDate",
PayAmount: "PayAmount",
PaidAmount: "PaidAmount",
RefundAmount: "RefundAmount",
RefundedAmount: "RefundedAmount",
chat: "UniplatImChatId",
remarkTitle: "Title",
remarkContent: "Remark",
chatTypeCode: "UniplatChatTypeCode",
lastMsgContent: "UniplatLastMsgContent",
lastMsgTime: "UniplatLastMsgTime",
unreadCount: 0
};
export const enum PayStatus {
UnPay = 1,
Paied,
Cancel,
Deleted,
WaitRefund,
Refund,
}
export const payStatusMapping = new Map<PayStatus, string>([
[PayStatus.UnPay, "待支付"],
[PayStatus.Paied, "已支付"],
[PayStatus.Cancel, "已取消"],
[PayStatus.Deleted, "已删除"],
[PayStatus.WaitRefund, "待退费"],
[PayStatus.Refund, "已退费"],
]);
export const enum PayMethod {
CardTransfer = 1,
Balance,
Refund2Card,
Refund2Balance,
}
export const payMethodMapping = new Map<PayMethod, string>([
[PayMethod.CardTransfer, "银行卡转账"],
[PayMethod.Balance, "余额扣费"],
[PayMethod.Refund2Card, "退款至银行卡"],
[PayMethod.Refund2Balance, "退款至余额"],
]);
export interface OrderPayItem {
id: number;
v: number;
title: string;
time: string;
dueTime: string;
value: number;
status: PayStatus;
method: PayMethod;
method_label: string;
type: string;
desc: string;
agent: string;
createdTime: string;
actions?: any[];
bankAccountName?: string;
OpenningBankName?: string;
bankAccountNo?: string;
providerHandleId?: number;
}
export const orderPayItemPredict = {
status: "Status",
time: "PaymentDate",
dueTime: "PaymentDueDate",
method: "PaymentFunction_label",
type: "ItemName",
desc: "PaymentDesc",
agent: "ProviderCollectId#provider_agent.AgentTrueName",
id: "ID",
v: "uniplat_version",
title: "ItemName",
value: "Amount",
createdTime: "CreatedDate",
bankAccountName: "ProviderHandleId#ServiceProviderBank.AccountName",
OpenningBankName: "ProviderHandleId#ServiceProviderBank.OpenningBankName",
bankAccountNo: "ProviderHandleId#ServiceProviderBank.AccountNo",
providerHandleId: "ProviderHandleId#ServiceProviderBank.ID",
}
export const enum ChatOpenDirection {
/**
* 通知外壳,打开一个新的Webview容器打开会话
*/
NewWebview,
/**
* 直接会用当前Webview,跳转到聊天页面
*/
Current,
}
export const enum SrcPlatform {
Backend = 1,
Website,
H5,
OtherApp,
QqxbApp,
}
export const enum OperationType {
User = 1,
Backend,
}
export interface UploadImageItem {
url: string;
fileName: string;
time: string;
fileSize: number;
}
export * from "./upload"
\ No newline at end of file
import Chat from "@/customer-service/xim";
import { metaRow, UniplatSdkExtender, UniplatSdk } from "uniplat-sdk";
import {
OperationType,
OrderStatus,
OrderTableListItem,
orderPredict,
SrcPlatform,
OrderPayItem,
UploadImageItem,
orderPayItemPredict,
} from "../model";
import { GeneralOrderDirection } from '../model/order-product';
class OrderService {
public readonly generalOrder = "general_order_info";
public readonly generalOrderPaymentModel = "general_order_payment_info";
private readonly handler = new UniplatSdkExtender();
private innerSdk: UniplatSdk | null = null;
public injectSdk(sdk: UniplatSdk) {
this.innerSdk = sdk;
return this;
}
private getSdk() {
return this.innerSdk || Chat.getSdk();
}
public openOrder(params: {
productCode: string;
srcPlatform: SrcPlatform;
createdType: OperationType;
/**
* 是否强制开启新订单,默认如果有已存在处理中的订单时会直接返回
*/
forceNewOrder?: boolean;
}) {
return this.getSdk()
.model(this.generalOrder)
.action(params.forceNewOrder ? "addOrder" : "getOrAddOrder")
.addInputs_parameter({
ProductCode: params.productCode,
SrcPlatform: params.srcPlatform,
CreatedType: params.createdType,
})
.execute();
}
public updateOrderStatus(
id: number | string,
v: number,
status: OrderStatus
) {
return this.getSdk()
.model(this.generalOrder)
.action("editStatus")
.addInputs_parameter({
Status: status,
userType: OperationType.User,
})
.updateInitialParams({ selected_list: [{ v, id: +id }] })
.execute();
}
/** 专项工单 */
public getOrders(params: {
productCode?: GeneralOrderDirection;
index: number;
size?: number;
}, isHrs = false) {
const list = this.getSdk()
.model(this.generalOrder)
.list(isHrs ? "hroOrgOrderList" : "userOrderList");
if (params.productCode) {
list.addPrefilter({ 'ProductId#product.Code': params.productCode });
}
return list
.query({ item_size: params.size || 100, pageIndex: params.index })
.then((r) => {
return {
total: r.pageData.record_count,
list: this.handler.buildRows<OrderTableListItem>(
r.pageData.rows,
orderPredict
),
};
});
}
public getProcessOrders(params: {
productCode?: GeneralOrderDirection;
index: number;
size?: number;
}) {
const list = this.getSdk()
.model(this.generalOrder)
.list("hroOrgDoingOrderList");
if (params.productCode) {
list.addPrefilter({ 'ProductId#product.Code': params.productCode });
}
return list
.query({ item_size: params.size || 100, pageIndex: params.index })
.then((r) => {
return {
total: r.pageData.record_count,
list: this.handler.buildRows<OrderTableListItem>(
r.pageData.rows,
orderPredict
),
};
});
}
/**
* 获取订单支付记录
* @param id 订单id
* @param withActions 是否获取行数据中action条目
*/
public getPayments(id: number, withActions = false) {
return this.getSdk()
.model(orderService.generalOrderPaymentModel)
.list(withActions ? "" : "userOrderPaymentList")
.addPrefilter({ OrderId: id })
.query({ pageIndex: 1, item_size: 100 })
.then((r) => {
if (r && r.pageData && r.pageData.rows) {
const items = this.handler.buildRows<OrderPayItem>(
r.pageData.rows,
orderPayItemPredict
);
if (withActions) {
for (let i = 0; i < r.pageData.rows.length; i++) {
items[i].actions = r.pageData.rows[i].actions;
}
}
return items;
}
return [];
});
}
public getPayment(id: string | number) {
return this.getSdk()
.model(orderService.generalOrderPaymentModel)
.detail(id as string, "userOrderPaymentDetail")
.query()
.then((r) =>
this.handler.buildRow<OrderPayItem>(r.row, orderPayItemPredict)
);
}
public addImage4Payment(payment: number | string, v: number, path: string) {
return this.getSdk()
.model(orderService.generalOrderPaymentModel)
.action("addImages")
.updateInitialParams({
selected_list: [{ v, id: payment as number }],
})
.addInputs_parameter({ images: path })
.execute();
}
public getImages4Payment(payment: number) {
return this.getSdk()
.model("general_order_payment_file_info")
.list()
.addPrefilter({ PaymentId: payment })
.query({ item_size: 10, pageIndex: 1 })
.then((r) =>
this.handler.buildRows<UploadImageItem>(r.pageData.rows, {
time: "CreatedDate",
fileSize: "FileSize",
fileName: "FileName",
url: "Url",
})
);
}
public buildOrder(o: metaRow) {
return this.handler.buildRow<OrderTableListItem>(o, orderPredict);
}
/** 设置备注 */
public setRemark(
id: string,
v: number,
data: { Title: string; Remark: string }
) {
return this.getSdk()
.model(this.generalOrder)
.action("editTitle")
.updateInitialParams({ selected_list: [{ v, id: +id }] })
.addInputs_parameter(data)
.execute();
}
public getOrderDetail(id: number | string) {
return this.getSdk()
.model(this.generalOrder)
.detail(id as string)
.query();
}
public sendPayAccountInfo(params: { send: string; accountId: number }) {
return this.getSdk().domainService("hro_spview", "OrderSetting", "sendPayAccountInfo")
.request("get", { params })
}
public sendPayAccountInfoForAgent(params: { send: string; userOrderPaymentId: number }) {
return this.getSdk().domainService("hro_spview", "OrderSetting", "sendPayAccountInfoForAgent")
.request("get", { params })
}
}
export const orderService = new OrderService();
...@@ -7,3 +7,63 @@ export async function uploadFile(file: File, uploading?: (p: number) => void) { ...@@ -7,3 +7,63 @@ export async function uploadFile(file: File, uploading?: (p: number) => void) {
}); });
return `${sdk.global.baseUrl}${url}`; return `${sdk.global.baseUrl}${url}`;
} }
export const enum UploadType {
Default,
Image,
Camera
}
/**
* x
* @param type UploadType 默认值 UploadType.Default
* @param size 可不传 单位M
* @returns
*/
export function chooseFileAndUpload(type = UploadType.Default, size?: number) {
return chooseFile(type).then(r => {
const sdk = Chat.getSdk();
return sdk.uploadFileV2(r)
})
}
/**
*
* @param type UploadType
* @param size 可不传 单位M
* @returns
*/
export function chooseFile(type = UploadType.Default, size?: number) {
return new Promise<File>((resolve, reject) => {
const target = document.createElement('input');
target.setAttribute('type', 'file');
// 添加这个属性,就可以唤起相机的功能
(type === UploadType.Camera) && target.setAttribute('capture', 'camera');
// 这里如果不加属性 accept 是 "image/*" 或者 "video/*",就默认打开摄像头,既可以拍照也可以录像
(type === UploadType.Image) && target.setAttribute('accept', 'image/*');
target.setAttribute('style', 'display:none');
// 监听改变事件
target.addEventListener('change', (e: Event) => {
// 拿到文件对象
if (e && e.target) {
const t = e.target as HTMLInputElement;
const { files } = t;
if (files) {
// 返回的是一个文件对象
if (size && files[0].size >= size * 1024 * 1024) {
reject(`上传的${type === UploadType.Default ? "文件" : "图片"}太大了~`);
return;
}
resolve(files[0]);
setTimeout(() => target.remove(), 200);
return;
}
}
reject(new Error('系统不支持'));
setTimeout(() => target.remove(), 200);
});
document.body.appendChild(target);
// 这里是模拟点击了input控件
target.click();
});
}
\ No newline at end of file
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
MessageHandled, MessageHandled,
RawChatItem, RawChatItem,
BaseChatItemBusinessData, BaseChatItemBusinessData,
MessageType,
} from "../model"; } from "../model";
import { isAccessibleUrl } from "../service/tools"; import { isAccessibleUrl } from "../service/tools";
import { unique } from "../utils"; import { unique } from "../utils";
...@@ -83,11 +84,16 @@ async function preCacheImgs(msgs?: any[]) { ...@@ -83,11 +84,16 @@ async function preCacheImgs(msgs?: any[]) {
} }
function buildChatItem(chat: RawChatItem) { function buildChatItem(chat: RawChatItem) {
if (!chat.model_name && chat.business_data) { if ((!chat.model_name || !chat.obj_id) && chat.business_data) {
const b = JSON.parse(chat.business_data) as BaseChatItemBusinessData; const b = JSON.parse(chat.business_data) as BaseChatItemBusinessData;
chat.model_name = b.model_name; chat.model_name = b.model_name;
b.obj_id && (chat.obj_id = b.obj_id); b.obj_id && (chat.obj_id = b.obj_id);
} }
if (!chat.detail_name && chat.business_data) {
const b = JSON.parse(chat.business_data) as BaseChatItemBusinessData;
b.detail_name && (chat.detail_name = b.detail_name);
}
return { ...chat, chat_id: chat.id } as ChatType; return { ...chat, chat_id: chat.id } as ChatType;
} }
...@@ -199,10 +205,19 @@ export default { ...@@ -199,10 +205,19 @@ export default {
) { ) {
const old = state[ChatStore.STATE_CHAT_MSG_HISTORY] || []; const old = state[ChatStore.STATE_CHAT_MSG_HISTORY] || [];
const chatid = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID]; const chatid = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
if (chatid == null) return; if (chatid) {
// 移除撤回的消息
const newItems = data || [];
const withdraw = newItems
.filter((i) => i.type === MessageType.Withdraw)
.map((i) => +i.msg);
const filterout = newItems.filter(
(i) => !withdraw.includes(i.id)
);
state[ChatStore.STATE_CHAT_MSG_HISTORY] = Object.freeze( state[ChatStore.STATE_CHAT_MSG_HISTORY] = Object.freeze(
filterMessages([...old, ...(data || [])], chatid) filterMessages([...old, ...filterout], chatid)
); );
}
}, },
[ChatStore.MUTATION_SAVE_MYSELF_ID](state) { [ChatStore.MUTATION_SAVE_MYSELF_ID](state) {
if (!state[ChatStore.STATE_CHAT_MY_ID]) { if (!state[ChatStore.STATE_CHAT_MY_ID]) {
...@@ -477,12 +492,42 @@ export default { ...@@ -477,12 +492,42 @@ export default {
return await execute().then((d) => d); return await execute().then((d) => d);
}, },
async [ChatStore.ACTION_FORCE_RELOAD_CHAT_LIST]({ commit }) {
return new Promise<ChatType[]>((resolve) => {
Chat.onReady(() => {
xim.fetchChatList().then((data) => {
if (!data) {
return resolve([]);
}
const chatList = filterActiveChats(
data.args[0] as RawChatItem[]
);
const items = chatList.map((chat) =>
buildChatItem(chat)
);
dbController.saveChatList(items);
commit(ChatStore.MUTATION_SAVE_CHAT_LIST, items);
resolve(items);
});
});
});
},
async [ChatStore.ACTION_REBUILD_UNREAD_MESSAGE_COUNT]({ state }) { async [ChatStore.ACTION_REBUILD_UNREAD_MESSAGE_COUNT]({ state }) {
let items = await dbController.getChatList(); let items = await dbController.getChatList();
let sum = 0; let sum = 0;
items.forEach((i) => (sum += i.unread_msg_count)); items.forEach((i) => (sum += i.unread_msg_count));
state[ChatStore.STATE_CURRENT_UNREAD_MESSAGE_COUNT] = sum; state[ChatStore.STATE_CURRENT_UNREAD_MESSAGE_COUNT] = sum;
}, },
async [ChatStore.ACTION_UPDATE_CHAT_UNREAD_MESSAGE_COUNT](
{ dispatch },
p: { chat: number; unread: number }
) {
dbController
.updateChat4UnreadCount(p.chat, p.unread)
.then(() =>
dispatch(ChatStore.ACTION_REBUILD_UNREAD_MESSAGE_COUNT)
);
},
async [ChatStore.ACTION_GET_CHAT_MESSAGES]({ state, commit, getters }) { async [ChatStore.ACTION_GET_CHAT_MESSAGES]({ state, commit, getters }) {
const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID]; const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
const chat = getters[ const chat = getters[
...@@ -662,10 +707,27 @@ export default { ...@@ -662,10 +707,27 @@ export default {
}) { }) {
const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID]; const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
const onNewMsg = (e: Message) => { const onNewMsg = (e: Message) => {
if (e.chat_id === chatId) { const thenAction = () => {
dispatch(ChatStore.ACTION_GET_FRESH_MESSAGE); if (e.type === MessageType.Withdraw) {
commit(ChatStore.MUTATION_WITHDRAW, +e.msg);
} }
const scroll = () =>
state[ChatStore.STATE_FUNC_ON_NEW_MSG](e); state[ChatStore.STATE_FUNC_ON_NEW_MSG](e);
if (e.chat_id === chatId) {
dispatch(ChatStore.ACTION_GET_FRESH_MESSAGE).finally(
() => scroll()
);
} else {
scroll();
}
};
if (e.type === MessageType.Withdraw) {
dbController
.removeMessage(e.chat_id, +e.msg)
.finally(() => thenAction());
} else {
thenAction();
}
}; };
if (!chatId) { if (!chatId) {
xim.off("msg", onNewMsg); xim.off("msg", onNewMsg);
...@@ -884,11 +946,18 @@ export default { ...@@ -884,11 +946,18 @@ export default {
) { ) {
return Promise.reject(); return Promise.reject();
} }
return await Chat.getSdk() return new Promise<void>((resolve, reject) => {
Chat.getSdk()
.model(currentChat.model_name) .model(currentChat.model_name)
.chat(currentChat.obj_id, orgId()) .chat(currentChat.obj_id, orgId())
.startChat() .startChat()
.finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS)); .catch(reject)
.finally(() =>
dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS)
.then(resolve)
.catch(reject)
);
});
}, },
async [ChatStore.ACTION_CHAT_FINISH_RECEPTION]({ getters, dispatch }) { async [ChatStore.ACTION_CHAT_FINISH_RECEPTION]({ getters, dispatch }) {
const currentChat = getters[ const currentChat = getters[
...@@ -918,6 +987,7 @@ export default { ...@@ -918,6 +987,7 @@ export default {
) { ) {
return Promise.reject(); return Promise.reject();
} }
await dbController.removeChatFromList(currentChat.id);
return await Chat.getSdk() return await Chat.getSdk()
.model(currentChat.model_name) .model(currentChat.model_name)
.chat(currentChat.obj_id, orgId()) .chat(currentChat.obj_id, orgId())
...@@ -935,6 +1005,7 @@ export default { ...@@ -935,6 +1005,7 @@ export default {
) { ) {
return Promise.reject(); return Promise.reject();
} }
await dbController.removeChatFromList(currentChat.id);
return await Chat.getSdk() return await Chat.getSdk()
.model(currentChat.model_name) .model(currentChat.model_name)
.chat(currentChat.obj_id, orgId()) .chat(currentChat.obj_id, orgId())
......
...@@ -247,6 +247,9 @@ export namespace ChatStore { ...@@ -247,6 +247,9 @@ export namespace ChatStore {
keyword?: string keyword?: string
) => Promise<ChatType[]>; ) => Promise<ChatType[]>;
export const ACTION_FORCE_RELOAD_CHAT_LIST = "重新获取我的会话列表";
export type ACTION_FORCE_RELOAD_CHAT_LIST = () => Promise<ChatType[]>;
export const ACTION_REBUILD_UNREAD_MESSAGE_COUNT = "重新计算未读消息数"; export const ACTION_REBUILD_UNREAD_MESSAGE_COUNT = "重新计算未读消息数";
export type ACTION_REBUILD_UNREAD_MESSAGE_COUNT = () => void; export type ACTION_REBUILD_UNREAD_MESSAGE_COUNT = () => void;
...@@ -378,6 +381,13 @@ export namespace ChatStore { ...@@ -378,6 +381,13 @@ export namespace ChatStore {
export const ACTION_UPDATE_CHAT = "更新会话信息"; export const ACTION_UPDATE_CHAT = "更新会话信息";
export type ACTION_UPDATE_CHAT = (p: ChatUpdateParameter) => void; export type ACTION_UPDATE_CHAT = (p: ChatUpdateParameter) => void;
export const ACTION_UPDATE_CHAT_UNREAD_MESSAGE_COUNT =
"更新Chat会话未读消息并重新计数总数";
export type ACTION_UPDATE_CHAT_UNREAD_MESSAGE_COUNT = (p: {
chat: number;
unread: number;
}) => void;
} }
export interface ChatStoreState { export interface ChatStoreState {
......
...@@ -11,14 +11,16 @@ export const getChatModel = () => chatInfo; ...@@ -11,14 +11,16 @@ export const getChatModel = () => chatInfo;
export async function getChatModelInfo( export async function getChatModelInfo(
modelName: string, modelName: string,
id: string | number, id: string | number,
detailname?: string detailname?: string,
forceReload = false
) { ) {
if (chatInfo[id]) { const key = `${modelName}-${id}`
if (chatInfo[key] && !forceReload) {
return Promise.resolve({ return Promise.resolve({
uniplatId: chatInfo[id].row.UniplatChatId.value, uniplatId: chatInfo[key].row.UniplatChatId.value,
chat_id: +chatInfo[id].row.UniplatImChatId.value, chat_id: +chatInfo[key].row.UniplatImChatId.value,
uniplat_version: 0, uniplat_version: 0,
data: chatInfo[id], data: chatInfo[key],
}); });
} }
const info = await Chat.getSdk() const info = await Chat.getSdk()
...@@ -26,7 +28,7 @@ export async function getChatModelInfo( ...@@ -26,7 +28,7 @@ export async function getChatModelInfo(
.detail(id + "", detailname) .detail(id + "", detailname)
.query(); .query();
const data = info; const data = info;
chatInfo[id] = data; chatInfo[key] = data;
return { return {
uniplatId: info.row.UniplatChatId.value, uniplatId: info.row.UniplatChatId.value,
chat_id: Number(info.row.UniplatImChatId.value), chat_id: Number(info.row.UniplatImChatId.value),
......
...@@ -141,7 +141,7 @@ class Chat { ...@@ -141,7 +141,7 @@ class Chat {
if (xim.isConnected()) { if (xim.isConnected()) {
return Promise.resolve(uri); return Promise.resolve(uri);
} }
return new Promise((resolve) => { return new Promise<void>((resolve) => {
xim.open(uri, this.token).finally(() => { xim.open(uri, this.token).finally(() => {
this.registerXimEvent(); this.registerXimEvent();
if (xim.isConnected()) { if (xim.isConnected()) {
......
import { MessageHandled, MessageType } from "@/customer-service/model"; import {
MessageHandled,
MessageType,
PayMethod,
PayStatus
} from "@/customer-service/model";
export interface Chat { export interface Chat {
id: number; id: number;
...@@ -197,6 +202,14 @@ export type CommentForwardMessageBody = { ...@@ -197,6 +202,14 @@ export type CommentForwardMessageBody = {
comment_ids: number[]; // 评论id集合 comment_ids: number[]; // 评论id集合
}; };
export interface PayMessageBody {
status: PayStatus;
paymentFunction: PayMethod;
itemName: string;
amount: string;
paymentId: string;
}
export interface CsUser { export interface CsUser {
id: number; id: number;
oid: string; oid: string;
......
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