You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dandelionPlatformToB-uni-v3/components/conversationList.vue

361 lines
14 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="g_pb_24">
<div style="width: calc(100vw - 20px);margin: 10px auto;border-radius: 8px;overflow: hidden;">
<div v-for="(item, index) in machineList" :key="index" class="customitem g_bg_f">
<Tooltip ref="tooltipRef" color="white" :isSelf="true" :top="150">
<div class="g_flex_row_start" hover-class="thover" style="padding: 12px 16px" @click="goChatPage(item)" :style="{ backgroundColor: item.stickTop ? '#f5f5f5' : '#fff' }">
<div
class="g_flex_none"
style="border-radius: 50%; width: 48px; height: 48px; background-size: cover; background-repeat: no-repeat; position: relative"
:style="{
'background-image': item.userinfo && item.userinfo[0].avatar ? 'url(' + item.userinfo[0].avatar + ')' : 'none',
}"
>
<div v-if="item.waitcount" style="position: absolute; right: -6px; top: -6px; background-color: #f5222d; width: 20px; height: 20px; border-radius: 50%; text-align: center; line-height: 20px; font-size: 12px; color: #fff">
{{ item.waitcount > 99 ? "99+" : item.waitcount }}
</div>
</div>
<div class="bottom-line g_flex_column_center g_flex_1" style="padding-left: 10px">
<div class="g_fs_17 g_flex_row_between" v-if="item.userinfo && item.userinfo[0].name">
<div class="g_ell_1 g_flex_1" style="color: #333;font-size: 14px;">
{{ item.userinfo[0].name }} <span v-if="item.subtitle" style="color: orange;font-size: 14px;">@{{ item.subtitle }}</span>
</div>
<div class="g_flex_none g_fs_12 g_ml_10" style="color: #999">
{{ item.time }}
</div>
</div>
<!-- 普通文本 -->
<div class="g_fs_14 g_ell_1" style="color: #999; font-size: 12px" v-if="item.lastMessage && item.lastMessage.messageType == 0">
{{ item.lastMessage.text }}
</div>
<!-- tip通知 -->
<div class="g_fs_14 g_ell_1" style="color: #999; font-size: 12px" v-if="item.lastMessage && item.lastMessage.messageType == 10">
{{ item.lastMessage.text }}
</div>
<!-- 图片消息 -->
<div class="g_fs_14 g_ell_1" style="color: #999; font-size: 12px" v-if="item.lastMessage && item.lastMessage.messageType == 1 && item.lastMessage.attachment && item.lastMessage.attachment.raw">[图片]</div>
<!-- 图片消息 -->
<div class="g_fs_14 g_ell_1" style="color: #999; font-size: 12px" v-if="item.lastMessage && item.lastMessage.messageType == 3 && item.lastMessage.attachment && item.lastMessage.attachment.raw">[视频]</div>
<!-- 自定义消息 -->
<div class="g_fs_14 g_ell_1" style="color: #999; font-size: 12px" v-if="item.lastMessage && item.lastMessage.messageType == 100 && item.lastMessage.attachment && item.lastMessage.attachment.raw">
<!-- 100001 商家后台 职位卡片 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).type == 100001">
{{ JSON.parse(item.lastMessage.attachment.raw).obj.jobName }}
</div>
<!-- 1000011 职位列表 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).type == 100011">[职位列表]</div>
<!-- 200001 系统提醒 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).type == 200001">
用户[
<span class="viewdetails">
{{ item.lastMessage && item.lastMessage.attachment && item.lastMessage.attachment.raw && JSON.parse(item.lastMessage.attachment.raw).userName }}
</span>
],询单需要人工介入,请尽快回复
</div>
<!-- 200002 系统通知 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).type == 200002">
<span class="">
{{ item.lastMessage && item.lastMessage.text }}
</span>
</div>
<!-- 未知 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).name">
{{ JSON.parse(item.lastMessage.attachment.raw).name }}
</div>
<!-- 未知 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).type == 100002">
{{ "工单" }}
</div>
<!-- 常用回复 -->
<div v-if="JSON.parse(item.lastMessage.attachment.raw).typs == 'msg'">
{{ item.lastMessage.text }}
</div>
<!-- 100000 移动端职位卡片 -->
<div v-else>
{{ JSON.parse(item.lastMessage.attachment.raw).title }}
</div>
</div>
<!-- 暂无新消息 -->
<div class="g_fs_14 g_ell_1" style="color: #999; font-size: 12px" v-if="!item.lastMessage">暂无新消息</div>
</div>
</div>
<template #content>
<view class="g_fs_16" style="">
<view class="g_flex_column_center g_h_32" @click="topMessage(item, index)">
<view>{{ item.stickTop ? "取消置顶" : "置顶消息" }} </view>
</view>
<view class="g_flex_column_center g_h_32" @click="deleteMessage(item, index)">
<view>不显示</view>
</view>
</view>
</template>
</Tooltip>
</div>
</div>
</view>
<view class="" v-if="machineList && machineList.length == 0">
<view
style="width: calc(100% - 20px); margin: 0 auto"
class=""
:style="{
'padding-top': '260rpx',
}"
>
<rh-empty text="暂无消息"></rh-empty>
</view>
</view>
<view v-show="firstLoad" style="position: fixed; width: 100%; height: 100vh; left: 0px; top: 0px; z-index: 9" class="u-skeleton">
<view v-for="i in 10" :key="i" style="width: 100%; height: 72px; padding: 16px 12px" class="g_flex_row_between">
<view class="u-skeleton-fillet g_w_48 g_h_48 g_mr_8"> </view>
<view class="g_flex_1">
<view class="u-skeleton-fillet g_w_all g_h_20"></view>
<view class="u-skeleton-fillet g_w_all g_h_20 g_mt_4"></view>
</view>
</view>
<!-- </template> -->
</view>
<u-skeleton :loading="firstLoad" :animation="true" el-color="#ededed" bg-color="#fff"></u-skeleton>
</template>
<script setup>
import { ref, onMounted, onUnmounted, getCurrentInstance, watch, defineEmits, defineProps } from "vue";
const props = defineProps({});
const G = getCurrentInstance().appContext.app.config.globalProperties.G;
import Tooltip from "./Tooltip.vue";
const tooltipRef = ref(null);
let firstLoad = ref(false);
const machineList = ref([]);
onMounted(async () => {
uni.$on("updateConversationList", () => {
console.log("监听到事件");
getConversationListAndHideLoading();
});
uni.$on("deleteConversationList", (conversationIds) => {
console.log("监听到事件");
machineList.value.splice(
machineList.value.findIndex((item) => item.conversationId === conversationIds[0]),
1,
);
});
if (uni.getStorageSync("apply-token")) {
await checkLoginStatusAndGetConversations();
}
});
// 抽离:获取会话列表并隐藏加载的核心逻辑
const getConversationListAndHideLoading = async () => {
try {
const result = await G.nim.V2NIMConversationService.getConversationList(0, 100);
console.log("会话列表获取成功", JSON.parse(JSON.stringify(result)));
if (result.conversationList && result.conversationList.length > 0) {
await Promise.all(
result.conversationList.map(async (item, index) => {
item.userinfo = await G.nim.V2NIMUserService.getUserListFromCloud([item.conversationId.split("|")[2]]);
console.log("debg item.conversationId", item.conversationId);
item.waitcount = await G.nim.V2NIMConversationService.getUnreadCountByIds([item.conversationId]);
if (item.userinfo[0].serverExtension) {
item.subtitle = JSON.parse(item.userinfo[0].serverExtension).fullName;
} else {
item.subtitle = "";
}
item.time = formatTime(item.lastMessage?.messageRefer.createTime || item.updateTime);
}),
);
}
// 置顶会话和普通会话分别排序
let stickTopList = result?.conversationList
.filter((item) => {
return item.stickTop == true;
})
.sort((a, b) => {
if (a.lastMessage?.messageRefer.createTime && b.lastMessage?.messageRefer.createTime) {
return b.lastMessage?.messageRefer.createTime - a.lastMessage?.messageRefer.createTime;
} else if (a.lastMessage?.messageRefer.createTime || b.lastMessage?.messageRefer.createTime) {
if (a.lastMessage?.messageRefer.createTime) {
return a.lastMessage?.messageRefer.createTime - b.createTime;
} else {
return a.createTime - b.lastMessage?.messageRefer.createTime;
}
} else {
return a.updateTime - b.updateTime;
}
});
let defaultList = result?.conversationList
.filter((item) => {
return item.stickTop != true;
})
.sort((a, b) => {
if (a.lastMessage?.messageRefer.createTime && b.lastMessage?.messageRefer.createTime) {
return b.lastMessage?.messageRefer.createTime - a.lastMessage?.messageRefer.createTime;
} else if (a.lastMessage?.messageRefer.createTime || b.lastMessage?.messageRefer.createTime) {
if (a.lastMessage?.messageRefer.createTime) {
return a.lastMessage?.messageRefer.createTime - b.createTime;
} else {
return a.createTime - b.lastMessage?.messageRefer.createTime;
}
} else {
return a.updateTime - b.updateTime;
}
});
machineList.value = stickTopList.concat(defaultList) || []; // 空值保护
} catch (error) {
console.error("获取会话列表失败", error);
machineList.value = []; // 报错时置空,避免页面异常
} finally {
// 无论成功/失败,都隐藏加载状态(移除固定 2 秒延迟,按需可保留短延迟)
firstLoad.value = false;
}
};
// 核心:检查登录状态并处理
const checkLoginStatusAndGetConversations = async () => {
try {
// 2. 获取登录状态
const loginStatus = G?.nim && (await G.nim.V2NIMLoginService.getLoginStatus());
console.log("当前登录状态", loginStatus);
if (loginStatus === 1) {
// 3. 已登录(状态码 1直接获取会话列表
await getConversationListAndHideLoading();
} else {
// 4. 未登录:延迟 2 秒重试一次(可根据需求调整重试次数/延迟)
setTimeout(async () => {
const retryLoginStatus = G?.nim && (await G.nim.V2NIMLoginService.getLoginStatus());
console.log("重试登录状态", retryLoginStatus);
if (retryLoginStatus === 1) {
await getConversationListAndHideLoading();
} else {
// 重试仍未登录:兜底隐藏加载状态,避免一直加载
console.log("重试后仍未登录,停止获取会话列表");
firstLoad.value = false;
}
}, 2000);
}
} catch (error) {
// 捕获所有异步错误,兜底处理加载状态
console.error("检查登录状态/获取会话列表失败", error);
firstLoad.value = false;
}
};
// 格式化时间
const formatTime = (time, separator = "-") => {
// 1. 处理入参转为Date对象兼容时间戳/Date
let targetDate;
if (typeof time === "number") {
// 处理毫秒/秒级时间戳网易云信等SDK可能返回秒级
targetDate = new Date(time.toString().length === 10 ? time * 1000 : time);
} else if (time instanceof Date) {
targetDate = new Date(time); // 重新创建,避免修改原对象
} else {
console.error("formatSmartTime入参格式错误需传入时间戳或Date对象");
return "";
}
// 2. 获取当前时间的基准值
const now = new Date();
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); // 今天0点
const todayEnd = new Date(todayStart.getTime() + 24 * 60 * 60 * 1000 - 1); // 今天23:59:59
const currentYear = now.getFullYear(); // 当前年份
// 3. 提取目标时间的年/月/日/时/分(补零)
const year = targetDate.getFullYear();
const month = String(targetDate.getMonth() + 1).padStart(2, "0"); // 月份从0开始补零
const day = String(targetDate.getDate()).padStart(2, "0");
const hour = String(targetDate.getHours()).padStart(2, "0");
const minute = String(targetDate.getMinutes()).padStart(2, "0");
// 4. 按规则格式化
if (targetDate >= todayStart && targetDate <= todayEnd) {
// 当天:仅显示时分
return `${hour}:${minute}`;
} else if (year === currentYear) {
// 当年非当天:月日 时分
return `${month}${separator}${day} ${hour}:${minute}`;
} else {
// 非当年:年月日 时分
return `${year}${separator}${month}${separator}${day} ${hour}:${minute}`;
}
};
const goChatPage = async (_item) => {
console.log(_item);
try {
console.log("debg _item.conversationId", _item.conversationId);
const res = await G.nim.V2NIMConversationService.clearUnreadCountByIds([_item.conversationId]);
console.log("V2NIMConversationService.clearUnreadCountByIds", res);
} catch (error) {
console.log("error", error);
}
uni.navigateTo({
url: "/root/NEUIKit/index?item=" + _item.conversationId,
});
};
/**
* 置顶或取消置顶会话
*/
const topMessage = async (_item, _index) => {
console.log(_item);
try {
if (_item && _item.stickTop) {
G.nim.V2NIMConversationService.stickTopConversation(_item.conversationId, false);
} else {
G.nim.V2NIMConversationService.stickTopConversation(_item.conversationId, true);
}
console.log("tooltipRef.value", tooltipRef.value);
} catch (error) {
uni.showToast({
title: error,
icon: "none",
});
console.log("errorerror", error);
}
tooltipRef.value[_index].close();
// let res1 = await G.nim.V2NIMConversationService.getStickTopConversationList();
// console.log("res1res1", res1);
};
/**
* 删除会话
*/
const deleteMessage = async (_item, _index) => {
console.log(_item);
G.handleConfirm({
content: "不显示该聊天?",
confirmText: "不显示",
cancelText: "取消",
success(res) {
if (res.confirm) {
try {
G.nim.V2NIMConversationService.deleteConversation(_item.conversationId, false);
} catch (error) {
console.log("errorerror", error);
uni.showToast({
title: error,
icon: "none",
});
}
}
},
});
tooltipRef.value[_index].close();
};
onUnmounted(() => {
uni.$off("updateConversationList");
});
</script>
<style lang="less" scoped>
.bottom-line {
position: relative;
&::after {
content: "";
width: 100%;
height: 1px;
background-color: #ededed;
position: absolute;
left: 10px;
top: -12px;
}
}
</style>