|
|
<template>
|
|
|
<div
|
|
|
:class="[
|
|
|
'conversation-item-container',
|
|
|
{
|
|
|
'show-action-list': showMoreActions,
|
|
|
'stick-on-top': conversation.stickTop,
|
|
|
},
|
|
|
props.heihei,
|
|
|
]"
|
|
|
class="u-skeleton"
|
|
|
@touchstart="handleTouchStart"
|
|
|
@touchmove="handleTouchMove"
|
|
|
@click="handleConversationItemClick()"
|
|
|
>
|
|
|
<div class="conversation-item-content">
|
|
|
<div class="conversation-item-left">
|
|
|
<div class="unread" v-if="unread">
|
|
|
<div class="dot" v-if="isMute"></div>
|
|
|
<div class="badge" v-else>{{ unread }}</div>
|
|
|
</div>
|
|
|
<Avatar class="u-skeleton-fillet" :account="to" size="48" :avatar="teamAvatar" />
|
|
|
</div>
|
|
|
<div class="conversation-item-right">
|
|
|
<div class="conversation-item-top">
|
|
|
<Appellation class="conversation-item-title u-skeleton-fillet" fontSize="17" v-if="conversation.type === V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P" :account="to" />
|
|
|
<span v-else class="conversation-item-title u-skeleton-fillet">
|
|
|
{{ sessionName }}
|
|
|
</span>
|
|
|
<span class="conversation-item-time" :data-time="date">{{ date }}</span>
|
|
|
</div>
|
|
|
<div class="conversation-item-desc">
|
|
|
<span v-if="beMentioned" class="beMentioned u-skeleton-fillet">
|
|
|
{{ "[" + t("someoneText") + "@" + t("meText") + "]" }}
|
|
|
</span>
|
|
|
<!-- <ConversationItemIsRead v-if="showSessionUnread" :conversation="props.conversation"></ConversationItemIsRead> -->
|
|
|
<span class="conversation-item-desc-content">{{ lastMsgContent }}</span>
|
|
|
<Icon v-if="isMute" iconClassName="conversation-item-desc-state" type="icon-xiaoximiandarao" color="#ccc" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="right-action-list">
|
|
|
<div v-for="action in moreActions" :key="action.type" :class="['right-action-item', action.class]" @click="() => handleClick(action.type)">
|
|
|
{{ action.name }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<u-skeleton :loading="loading" :animation="true" bgColor="#ededed"></u-skeleton>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
import Avatar from "../../../components/Avatar.vue";
|
|
|
import Appellation from "../../../components/Appellation.vue";
|
|
|
import Icon from "../../../components/Icon.vue";
|
|
|
import { getMsgContentTipByType } from "../../../utils/msg";
|
|
|
import { computed, onUpdated, withDefaults, onMounted, ref } from "../../../utils/transformVue";
|
|
|
import dayjs from "dayjs";
|
|
|
import { t } from "../../../utils/i18n";
|
|
|
import { V2NIMConst } from "nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK";
|
|
|
import { V2NIMConversationForUI } from "@xkit-yx/im-store-v2/dist/types/types";
|
|
|
import ConversationItemIsRead from "./conversation-item-isRead.vue";
|
|
|
const props = withDefaults(
|
|
|
defineProps<{
|
|
|
conversation: V2NIMConversationForUI;
|
|
|
showMoreActions?: boolean;
|
|
|
loading: boolean;
|
|
|
heihei: "";
|
|
|
}>(),
|
|
|
{ showMoreActions: false }
|
|
|
);
|
|
|
let pageScroll = ref(false);
|
|
|
|
|
|
onMounted(() => {
|
|
|
uni.$on("pageScroll", (e: any) => {
|
|
|
console.log("pageScroll", e);
|
|
|
if (Math.abs(e.detail.deltaY) > 0) {
|
|
|
pageScroll.value = true;
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
const emit = defineEmits(["click", "delete", "stickyToTop", "leftSlide"]);
|
|
|
|
|
|
const moreActions = computed(() => {
|
|
|
return [
|
|
|
{
|
|
|
name: props.conversation.stickTop ? t("deleteStickTopText") : t("addStickTopText"),
|
|
|
class: "action-top",
|
|
|
type: "action-top",
|
|
|
},
|
|
|
{
|
|
|
name: t("deleteSessionText"),
|
|
|
class: "action-delete",
|
|
|
type: "action-delete",
|
|
|
},
|
|
|
];
|
|
|
});
|
|
|
|
|
|
const handleClick = (type: string) => {
|
|
|
if (type === "action-top") {
|
|
|
emit("stickyToTop", props.conversation);
|
|
|
} else {
|
|
|
emit("delete", props.conversation);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 群头像
|
|
|
const teamAvatar = computed(() => {
|
|
|
if (props.conversation.type === V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM) {
|
|
|
const { avatar } = props.conversation;
|
|
|
return avatar;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 对话方
|
|
|
const sessionName = computed(() => {
|
|
|
if (props.conversation.name) {
|
|
|
return props.conversation.name;
|
|
|
}
|
|
|
return props.conversation.conversationId;
|
|
|
});
|
|
|
|
|
|
// 会话最后一条消息内容
|
|
|
const lastMsgContent = computed(() => {
|
|
|
const lastMsg = props.conversation.lastMessage;
|
|
|
|
|
|
if (lastMsg) {
|
|
|
const { sendingState, messageType, lastMessageState } = lastMsg;
|
|
|
|
|
|
if (lastMessageState === V2NIMConst.V2NIMLastMessageState.V2NIM_MESSAGE_STATUS_REVOKE) {
|
|
|
return t("recall");
|
|
|
}
|
|
|
|
|
|
if (messageType === void 0) {
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
if (messageType === V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION) {
|
|
|
return t("conversationNotificationText");
|
|
|
}
|
|
|
|
|
|
if (sendingState === V2NIMConst.V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SENDING) {
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
if (sendingState === V2NIMConst.V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_FAILED) {
|
|
|
return t("conversationSendFailText");
|
|
|
}
|
|
|
console.log("lastMsg", lastMsg);
|
|
|
let _str = "[多媒体]";
|
|
|
|
|
|
if (!lastMsg.text && lastMsg.attachment && lastMsg.attachment.raw) {
|
|
|
_str = "[职位] " + JSON.parse(lastMsg.attachment.raw).title;
|
|
|
}
|
|
|
if (!lastMsg.text && lastMsg.attachment && lastMsg.attachment.ext == ".mp3") {
|
|
|
_str = "[语音]";
|
|
|
}
|
|
|
if (!lastMsg.text && lastMsg.attachment && (lastMsg.attachment.ext == ".jpg" || lastMsg.attachment.ext == ".png" || lastMsg.attachment.ext == ".jpeg" || lastMsg.attachment.ext == ".gif")) {
|
|
|
_str = "[图片]";
|
|
|
}
|
|
|
|
|
|
console.log("lastMsg.text", lastMsg.text, " -- ", _str);
|
|
|
return getMsgContentTipByType({
|
|
|
messageType: lastMsg.messageType,
|
|
|
text: lastMsg.text ? lastMsg.text : _str,
|
|
|
});
|
|
|
}
|
|
|
return "";
|
|
|
});
|
|
|
|
|
|
const to = computed(() => {
|
|
|
const res = uni.$UIKitNIM.V2NIMConversationIdUtil.parseConversationTargetId(props.conversation.conversationId);
|
|
|
return res;
|
|
|
});
|
|
|
|
|
|
const date = computed(() => {
|
|
|
const now = dayjs();
|
|
|
// console.log(dayjs(props.conversation.lastMessage?.messageRefer.createTime).format('YYYY-MM-DD HH:mm') +"最后一条消息时间 ");
|
|
|
// console.log(dayjs(props.conversation.updateTime).format('YYYY-MM-DD HH:mm') +"更新时间 ");
|
|
|
|
|
|
const time = props.conversation.lastMessage?.messageRefer.createTime || props.conversation.updateTime;
|
|
|
// const time = props.conversation.updateTime;
|
|
|
if (!time) {
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
const messageTime = dayjs(time);
|
|
|
|
|
|
// 1. 刚刚(1分钟内)
|
|
|
if (now.diff(messageTime, "minute") < 1) {
|
|
|
return "刚刚";
|
|
|
}
|
|
|
|
|
|
// 2. 今天的时间,显示格式如 17:45
|
|
|
if (messageTime.isSame(now, "day")) {
|
|
|
return messageTime.format("HH:mm");
|
|
|
}
|
|
|
|
|
|
// 3. 昨天的时间,显示格式如 昨天 17:34
|
|
|
if (messageTime.isSame(now.subtract(1, "day"), "day")) {
|
|
|
return `昨天 ${messageTime.format("HH:mm")}`;
|
|
|
}
|
|
|
|
|
|
// 4. 同一年的日期,显示格式如 5月2日
|
|
|
if (messageTime.isSame(now, "year")) {
|
|
|
const month = messageTime.month() + 1;
|
|
|
const day = messageTime.date();
|
|
|
return `${month}月${day}日`;
|
|
|
}
|
|
|
|
|
|
// 5. 去年及更早的日期,显示格式如 2024年7月8日
|
|
|
const year = messageTime.year();
|
|
|
const month = messageTime.month() + 1;
|
|
|
const day = messageTime.date();
|
|
|
return `${year}年${month}月${day}日`;
|
|
|
});
|
|
|
|
|
|
const max = 99;
|
|
|
|
|
|
const unread = computed(() => {
|
|
|
return props.conversation.unreadCount > 0 ? (props.conversation.unreadCount > max ? `${max}+` : props.conversation.unreadCount + "") : "";
|
|
|
});
|
|
|
|
|
|
const isMute = computed(() => {
|
|
|
return !!props.conversation.mute;
|
|
|
});
|
|
|
|
|
|
const beMentioned = computed(() => {
|
|
|
return !!props.conversation.beMentioned;
|
|
|
});
|
|
|
|
|
|
const showSessionUnread = computed(() => {
|
|
|
const myUserAccountId = uni.$UIKitNIM.V2NIMLoginService.getLoginUser();
|
|
|
if (props.conversation.type === V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P) {
|
|
|
return props?.conversation?.lastMessage?.messageRefer.senderId === myUserAccountId && props?.conversation?.lastMessage?.messageType !== V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL && props?.conversation?.lastMessage?.messageType !== V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION && props?.conversation?.lastMessage?.sendingState === V2NIMConst.V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED && props?.conversation?.lastMessage?.lastMessageState !== V2NIMConst.V2NIMLastMessageState.V2NIM_MESSAGE_STATUS_REVOKE;
|
|
|
} else {
|
|
|
return false;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 左滑显示 action 动画
|
|
|
let startX = 0,
|
|
|
startY = 0;
|
|
|
// 开始左滑
|
|
|
function handleTouchStart(event: TouchEvent) {
|
|
|
startX = event.changedTouches[0].pageX;
|
|
|
startY = event.changedTouches[0].pageY;
|
|
|
pageScroll.value = false;
|
|
|
}
|
|
|
|
|
|
function handleTouchMove(event: TouchEvent) {
|
|
|
const moveEndX = event.changedTouches[0].pageX;
|
|
|
const moveEndY = event.changedTouches[0].pageY;
|
|
|
const X = moveEndX - startX;
|
|
|
const Y = moveEndY - startY;
|
|
|
// console.log("X", X, "Y", Y);
|
|
|
console.log("pageScroll.value", pageScroll.value);
|
|
|
if (Math.abs(Y) > Math.abs(X) || pageScroll.value == true) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const horizontalThreshold = 50;
|
|
|
const verticalThreshold = 50;
|
|
|
|
|
|
if (Math.abs(X) > Math.abs(Y) && Math.abs(X) > verticalThreshold) {
|
|
|
if (X < -horizontalThreshold) {
|
|
|
emit("leftSlide", props.conversation);
|
|
|
// emit("leftSlide", null);
|
|
|
} else if (X > horizontalThreshold) {
|
|
|
// emit("leftSlide", props.conversation);
|
|
|
emit("leftSlide", null);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleConversationItemClick() {
|
|
|
if (props.showMoreActions) {
|
|
|
emit("leftSlide", null);
|
|
|
return;
|
|
|
}
|
|
|
emit("click", props.conversation);
|
|
|
}
|
|
|
|
|
|
onUpdated(() => {
|
|
|
console.log("onUpdated", props.conversation.unreadCount);
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
$cellHeight: 72px;
|
|
|
|
|
|
.conversation-item-container {
|
|
|
position: relative;
|
|
|
transition: transform 0.3s;
|
|
|
background-color: #fff;
|
|
|
padding: 0 10px;
|
|
|
&.show-action-list {
|
|
|
transform: translateX(-200px);
|
|
|
}
|
|
|
|
|
|
&.stick-on-top {
|
|
|
background: #efefef;
|
|
|
}
|
|
|
|
|
|
.beMentioned {
|
|
|
color: #ff4d4f;
|
|
|
}
|
|
|
|
|
|
.content {
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.right-action-list {
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
right: -200px;
|
|
|
bottom: 0;
|
|
|
width: 200px;
|
|
|
|
|
|
.right-action-item {
|
|
|
width: 100px;
|
|
|
display: inline-block;
|
|
|
color: #fff;
|
|
|
text-align: center;
|
|
|
height: $cellHeight;
|
|
|
line-height: $cellHeight;
|
|
|
}
|
|
|
|
|
|
.action-top {
|
|
|
background: #337eff;
|
|
|
}
|
|
|
|
|
|
.action-delete {
|
|
|
background: #fa9d3b;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.conversation-item-content {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
padding: 10px 16px;
|
|
|
height: $cellHeight;
|
|
|
box-sizing: border-box;
|
|
|
// background-color: #fff;
|
|
|
}
|
|
|
|
|
|
.conversation-item-left {
|
|
|
position: relative;
|
|
|
|
|
|
.conversation-item-badge {
|
|
|
position: absolute;
|
|
|
top: 0px;
|
|
|
right: 0px;
|
|
|
z-index: 10;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.conversation-item-right {
|
|
|
flex: 1;
|
|
|
width: 0;
|
|
|
margin-left: 10px;
|
|
|
&:after {
|
|
|
content: "";
|
|
|
display: block;
|
|
|
height: 1rpx;
|
|
|
width: calc(100% - 80px);
|
|
|
background: #eee;
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
right: 0;
|
|
|
}
|
|
|
}
|
|
|
.mmp0 .conversation-item-right::after {
|
|
|
height: 0;
|
|
|
}
|
|
|
.conversation-item-top {
|
|
|
margin-bottom: 5px;
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
|
|
|
.conversation-item-title {
|
|
|
overflow: hidden; //超出的文本隐藏
|
|
|
text-overflow: ellipsis; //溢出用省略号显示
|
|
|
white-space: nowrap; //溢出不换行
|
|
|
}
|
|
|
|
|
|
.conversation-item-time {
|
|
|
font-size: 12px;
|
|
|
color: #cccccc;
|
|
|
text-align: right;
|
|
|
width: 90px;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.conversation-item-desc {
|
|
|
font-size: 14px;
|
|
|
color: #999;
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
|
|
|
.conversation-item-desc-content {
|
|
|
overflow: hidden; //超出的文本隐藏
|
|
|
text-overflow: ellipsis; //溢出用省略号显示
|
|
|
white-space: nowrap; //溢出不换行
|
|
|
flex: 1;
|
|
|
}
|
|
|
|
|
|
.conversation-item-desc-state {
|
|
|
margin-left: 10px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.dot {
|
|
|
background-color: #ff4d4f;
|
|
|
color: #fff;
|
|
|
width: 10px;
|
|
|
height: 10px;
|
|
|
border-radius: 5px;
|
|
|
box-sizing: border-box;
|
|
|
z-index: 99;
|
|
|
}
|
|
|
|
|
|
.badge {
|
|
|
background-color: #ff4d4f;
|
|
|
color: #fff;
|
|
|
font-size: 12px;
|
|
|
min-width: 20px;
|
|
|
height: 20px;
|
|
|
line-height: 19px;
|
|
|
border-radius: 10px;
|
|
|
padding: 0 5px;
|
|
|
box-sizing: border-box;
|
|
|
text-align: center;
|
|
|
z-index: 99;
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
.unread {
|
|
|
position: absolute;
|
|
|
right: -4px;
|
|
|
top: -2px;
|
|
|
z-index: 99;
|
|
|
}
|
|
|
</style>
|