cyl/master-0804
jscyl13849007907 7 months ago
parent 0f33935b6d
commit 79a50c010d

@ -61,12 +61,12 @@ export default {
uni.$on("isGlogin", function (data) {
console.log("app.vue 接收", data);
if (uni.getStorageSync("apply-uid")) {
// that.initWyyx()
that.initWyyx()
}
});
if (uni.getStorageSync("apply-token")) {
console.log("app.vue 是否调用");
// that.initWyyx()
that.initWyyx()
}
},
onHide: function () {

@ -610,6 +610,12 @@
"selectedIconPath": "static/image/apply-active.png",
"text": "工单"
},
{
"iconPath": "static/image/message.png",
"selectedIconPath": "static/image/message_active.png",
"pagePath": "pages/message/index",
"text": "消息"
},
{
"pagePath": "pages/person/index",
"iconPath": "static/image/person.png",

@ -0,0 +1,208 @@
<template>
<!-- :style="{ height: `calc(100vh - ${43 + tabbarHeight}px)` }" -->
<view class="g_bg_f_5">
<div style="height: 10px"></div>
<div class="g_bg_f_5" style="width: calc(100% - 40rpx); margin: 0px auto 10px">
<div class="">
<view style="background-color: #ededed; padding: 0 12px">
<u-search placeholder="请输入手机号" :show-action="false" bg-color="#fff" placeholder-class="g_c_c" :input-style="{ fontSize: '16px' }" height="80" search-icon-color="#999999" v-model="wxyx_tel" @search="handleSearch" @custom="handleCustom"></u-search>
</view>
</div>
<div class="g_flex_row_between g_pl_10 g_pr_10" v-if="receiverUserData && receiverUserData.userId" style="background-color: #fff; border-radius: 8px; padding: 12px 10px; margin-top: 10px">
<div class="g_flex_1">
<div style="font-size: 16px; font-weight: 600">
{{ receiverUserData.tel || "-" }}
</div>
<div style="color: #a1a1a1; font-size: 14px; margin-top: 8px">
{{ receiverUserData.userName || "-" }}
</div>
</div>
<div class="g_flex_none g_ml_12 g_flex_column_center">
<div>
<u-button :loading="isStart" type="primary" size="mini" @click="startChat"></u-button>
</div>
</div>
</div>
<div class="nei-box g_mt_10" style="background-color: #fff; border-radius: 8px" v-if="allUser && allUser.length > 0">
<!-- <div class="label-title" style="font-size: 16px; font-weight: 600; padding: 12px 16px; border-bottom: 1px solid #eee">内部通讯录</div> -->
<div v-for="(item, index) in allUser" :key="index" @click="startCopyChat(item)" class="g_flex_row_between g_pt_12 g_pb_12 g_pl_16 g_pr_16 border_bottom">
<div class="g_flex_1 g_flex_row_start g_fs_16">
<div class="g_flex_none g_mr_12">
<image :src="item.imgSrc || 'https://matripe-cms.oss-cn-beijing.aliyuncs.com/1shoudan/defaultAva.svg'" style="width: 48px; height: 48px; border-radius: 50px"></image>
</div>
<div class="g_flex_1 g_flex_column_between">
<div class="g_c_3 g_fs_17" style="line-height: 24px">
{{ item.userName }}
</div>
<div
class="g_c_3 g_fs_14 g_ell_1"
style="line-height: 20px"
:style="{
color: '#999',
}"
>
{{ item.tel }}
</div>
</div>
</div>
<!-- <div class="g_flex_1">
<div style="font-size: 16px; font-weight: 600">
{{ item.userName || "-" }}
</div>
<div style="color: #a1a1a1; font-size: 14px; margin-top: 8px">
{{ item.tel || "-" }}
</div>
</div> -->
<!-- <div class="g_flex_none g_ml_12 g_flex_column_center">
<div>
<u-button :loading="item.isStart" type="primary" size="mini" @click="startCopyChat(item)"></u-button>
</div>
</div> -->
</div>
</div>
</div>
<div style="height: 100rpx"></div>
</view>
</template>
<script>
import { customRedirectTo, customReLaunch, customSwitchTab, customNavigateTo } from "../../utils/customNavigate";
export default {
data() {
return {
allUser: [],
isStart: false,
isSend: false,
wxyx_tel: "",
receiverUserData: {},
tabbarHeight: uni.getStorageSync("TABBAR_HEIGHT"),
};
},
created() {
let that = this;
that.getCopyUserList();
},
methods: {
getCopyUserList() {
let that = this;
that.F.wyyxGet(that.api.wyyx_getCorpUserList, {}, (res) => {
console.log("获取通讯录:", res);
if (res && res.length > 0) {
res.forEach((item) => {
item.isStart = false;
});
}
that.allUser = res;
});
},
handleSearch(e) {
this.getUserList(e);
},
handleCustom(e) {
this.getUserList(e);
},
getUserList(e) {
let that = this;
that.F.wyyxGet(
this.api.wyyx_getCorpUserList,
{
key: e,
},
(res) => {
console.log("搜索手机号", res);
that.receiverUserData = res[0];
}
);
},
sendPointMessage() {
let that = this;
that.isSend = true;
that.F.wyyxPost(
that.api.wyyx_sendMessage,
{
conversationType: 1,
senderUserId: uni.getStorageSync("apply-uid"), // id id
receiverUserId: that.receiverUserData.userId, // id
text: "我是招呼语" + that.G.getPointTime(),
},
(res) => {
that.isSend = false;
that.receiverUserData = {};
that.goYunxin();
},
() => {
that.isSend = false;
}
);
},
async startCopyChat($item) {
let that = this;
$item.isStart = true;
that.F.wyyxPost(
that.api.wyyx_create,
{
senderUserId: uni.getStorageSync("apply-uid"),
receiverUserId: $item.userId,
},
(res) => {
$item.isStart = false;
uni.$UIKitStore.uiStore.selectConversation(res.conversationId);
customNavigateTo({
url: "/pages/Chat/index",
});
},
() => {
$item.isStart = false;
}
);
},
async startChat() {
let that = this;
that.isStart = true;
that.F.wyyxPost(
that.api.wyyx_create,
{
senderUserId: uni.getStorageSync("apply-uid"),
receiverUserId: that.receiverUserData.userId,
},
(res) => {
that.isStart = false;
uni.$UIKitStore.uiStore.selectConversation(res.conversationId);
that.receiverUserData = {};
customNavigateTo({
url: "/pages/Chat/index",
});
},
() => {
that.isStart = false;
}
);
},
goYunxin() {
customNavigateTo({
url: "/pages/Conversation/index",
});
},
},
};
</script>
<style lang="less">
page {
background-color: #ededed;
}
.border_bottom {
position: relative;
&:not(:last-child):after {
content: "";
display: block;
height: 1rpx;
width: calc(100% - 80px);
background: #eee;
position: absolute;
bottom: 0;
right: 0;
}
}
</style>

@ -0,0 +1,111 @@
<template>
<div class="friend-list-container g_bg_f_5">
<div class="friend-group-list">
<Empty v-if="friendGroupList.length === 0" :text="t('noFriendText')" />
<div
class="friend-group-item"
v-for="friendGroup in friendGroupList"
:key="friendGroup.key"
>
<div class="friend-group-title">
{{ friendGroup.key }}
</div>
<div
class="friend-item"
v-for="friend in friendGroup.data"
:key="friend.accountId"
@click="handleFriendItemClick(friend)"
>
<Avatar :account="friend.accountId" />
<div class="friend-name">{{ friend.appellation }}</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import Avatar from '../../../components/Avatar.vue'
import { autorun } from 'mobx'
import { onUnmounted, ref } from '../../../utils/transformVue'
import { friendGroupByPy } from '../../../utils/friend'
import { customNavigateTo } from '../../../utils/customNavigate'
import Empty from '../../../components/Empty.vue'
import { deepClone } from '../../../utils'
import { t } from '../../../utils/i18n'
import { V2NIMFriend } from 'nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK/V2NIMFriendService'
type FriendItem = V2NIMFriend & { appellation: string }
const friendGroupList = ref<
{ key: string; data: { accountId: string; appellation: string }[] }[]
>([])
function handleFriendItemClick(friend: FriendItem) {
customNavigateTo({
url: `/pages/user-card/friend/index?account=${friend.accountId}`,
})
}
const uninstallFriendListWatch = autorun(() => {
const data = uni.$UIKitStore.uiStore.friends
.filter(
(item) =>
!uni.$UIKitStore.relationStore.blacklist.includes(item.accountId)
)
.map((item) => ({
accountId: item.accountId,
appellation: uni.$UIKitStore.uiStore.getAppellation({
account: item.accountId,
}),
}))
friendGroupList.value = deepClone(
friendGroupByPy(
data,
{
firstKey: 'appellation',
},
false
)
)
})
onUnmounted(() => {
uninstallFriendListWatch()
})
</script>
<style lang="scss" scoped>
@import '../../styles/common.scss';
.friend-group-item {
padding-left: 20px;
background-color: #fff;
}
.friend-group-title {
height: 40px;
line-height: 40px;
font-size: 14px;
color: #b3b7bc;
border-bottom: 1rpx solid #dbe0e8;
}
.friend-item {
// margin-top: 16px;
display: flex;
align-items: center;
padding: 12px 0;
.friend-name {
margin-left: 12px;
padding-right: 20px;
color: #333333;
flex: 1;
overflow: hidden; //
text-overflow: ellipsis; //
white-space: nowrap; //
}
}
</style>

@ -0,0 +1,276 @@
<template>
<div class="contact-list-container g_bg_f_5">
<!-- <div
class="dropdown-mark"
v-if="addDropdownVisible"
@touchstart="hideAddDropdown"
/> -->
<div class="navigation-bar">
<div class="logo-box" v-if="false">
<div>{{ t("contactText") }}</div>
</div>
<div :class="buttonClass" v-if="false">
<image src="https://yx-web-nosdn.netease.im/common/9ae07d276ba2833b678a4077960e2d1e/Group 1899.png" class="button-icon" @tap="showAddDropdown" style="width: 40px !important; height: 40px !important; border-radius: 50%" />
<div v-if="addDropdownVisible" class="dropdown-container">
<div class="add-menu-list">
<div class="add-menu-item" @tap="onDropdownClick('addFriend')">
<div :style="{ marginRight: '5px' }">
<Icon type="icon-tianjiahaoyou" />
</div>
{{ t("addFriendText") }}
</div>
<div class="add-menu-item" @tap="onDropdownClick('createGroup')" v-if="false">
<div :style="{ marginRight: '5px' }">
<Icon type="icon-chuangjianqunzu" />
</div>
{{ t("createTeamText") }}
</div>
</div>
</div>
</div>
</div>
<div class="contact-list">
<div class="contact-item-content g_bg_f_5">
<div class="contact-item" @click="onDropdownClick('addFriend')">
<img src="https://matripe-cms.oss-cn-beijing.aliyuncs.com/bocaigongyinglian/add_friend.svg" alt="" style="width: 48px; height: 48px; vertical-align: middle" />
<!-- <Badge :num="unreadSysMsgCount" :style="{ position: 'absolute', top: '5px', left: '51px' }" /> -->
<span class="contact-item-title">添加好友</span>
<Icon iconClassName="more-icon" color="#999" type="icon-jiantou" />
</div>
<div class="contact-item" @click="handleValidMsgClick">
<Icon iconClassName="contact-item-icon contact-valid-icon" :size="48" type="icon-yanzheng" color="#fff" />
<Badge :num="unreadSysMsgCount" :style="{ position: 'absolute', top: '5px', left: '51px' }" />
<span class="contact-item-title"> {{ t("validMsgText") }}</span>
<Icon iconClassName="more-icon" color="#999" type="icon-jiantou" />
</div>
<div class="contact-item" @click="handleBlacklistClick">
<Icon iconClassName="contact-item-icon contact-blacklist-icon" :size="48" type="icon-lahei2" color="#fff" />
<span class="contact-item-title"> {{ t("blacklistText") }}</span>
<Icon iconClassName="more-icon" color="#999" type="icon-jiantou" />
</div>
<div class="contact-item" @click="onDropdownClick('innerAddress')" v-if="corpUserFlag && false">
<img src="https://matripe-cms.oss-cn-beijing.aliyuncs.com/bocaigongyinglian/inner.svg" alt="" style="width: 48px; height: 48px; border-radius: 50%" />
<!-- <Badge :num="unreadSysMsgCount" :style="{ position: 'absolute', top: '5px', left: '51px' }" /> -->
<span class="contact-item-title">公司内部成员</span>
<Icon iconClassName="more-icon" color="#999" type="icon-jiantou" />
</div>
<!-- <div class="contact-item" @click="handleGroupContactClick">
<Icon
iconClassName="contact-item-icon contact-group-icon"
:size="48"
type="icon-team2"
color="#fff"
/>
<span class="contact-item-title"> {{ t('teamMenuText') }}</span>
<Icon iconClassName="more-icon" color="#999" type="icon-jiantou" />
</div> -->
</div>
<FriendList />
</div>
</div>
</template>
<script lang="ts" setup>
import Icon from "../../../components/Icon.vue";
import Badge from "../../../components/Badge.vue";
import FriendList from "./friend-list.vue";
import { onUnmounted, ref } from "../../../utils/transformVue";
import { onHide } from "@dcloudio/uni-app";
import { customNavigateTo } from "../../../utils/customNavigate";
import { autorun } from "mobx";
import { t } from "../../../utils/i18n";
const addDropdownVisible = ref(false);
const unreadSysMsgCount = ref(0);
const uninstallUnreadWatch = autorun(() => {
unreadSysMsgCount.value = uni.$UIKitStore.sysMsgStore.getTotalUnreadMsgsCount();
});
const corpUserFlag = ref(uni.getStorageSync("apply-userinfo").corpUserFlag);
const onDropdownClick = (urlType: "addFriend" | "createGroup") => {
const urlMap = {
//
addFriend: "/pages/Friend/add-friend/index",
//
createGroup: "/pages/Group/group-create/index",
//
innerAddress: "/pages/Contact/BcFriend",
};
addDropdownVisible.value = false;
customNavigateTo({
url: urlMap[urlType],
});
};
const handleValidMsgClick = () => {
uni.$UIKitStore.sysMsgStore.setAllApplyMsgRead();
customNavigateTo({
url: `/pages/Contact/contact-list/valid-list`,
});
};
const handleBlacklistClick = () => {
customNavigateTo({
url: `/pages/Contact/contact-list/black-list`,
});
};
const handleGroupContactClick = () => {
customNavigateTo({
url: `/pages/Contact/contact-list/group-list`,
});
};
const showAddDropdown = () => {
addDropdownVisible.value = true;
};
const hideAddDropdown = () => {
addDropdownVisible.value = false;
};
let buttonClass = "button-box";
// #ifdef MP
buttonClass = "button-box-mp";
// #endif
onUnmounted(() => {
uninstallUnreadWatch();
});
onHide(() => {
addDropdownVisible.value = false;
});
</script>
<style lang="scss" scoped>
@import "../../styles/common.scss";
page {
height: 100vh;
overflow: hidden;
}
.contact-list-container {
height: 100vh;
box-sizing: border-box;
}
.navigation-bar {
// height: 60px;
// border-bottom: 1rpx solid #e9eff5;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
// padding-top: var(--status-bar-height);
.logo-box {
display: flex;
align-items: center;
font-size: 20px;
font-weight: 500;
}
}
.contact-list {
height: calc(100% - 60px - var(--status-bar-height));
box-sizing: border-box;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
.contact-item-content {
padding-bottom: 5px;
}
.contact-item {
height: 72px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
position: relative;
&:not(:last-child) {
border-bottom: 1px solid #f5f8fc;
}
.contact-item-icon {
height: 42px;
width: 42px;
border-radius: 50%;
text-align: center;
line-height: 39px;
font-size: 20px;
color: #fff;
}
.contact-valid-icon {
background-color: #60cfa7;
}
.contact-blacklist-icon {
background-color: #53c3f4;
}
.contact-group-icon {
background-color: #be65d9;
}
.contact-item-title {
margin-left: 12px;
font-size: 17px;
color: #333333;
flex: 1;
}
.more-icon {
margin: 0 16px;
color: #999999;
}
}
}
.dropdown-mark {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
}
.dropdown-container {
position: absolute;
top: -70px;
right: 30px;
min-width: 122px;
min-height: 40px;
background-color: #fff;
border: 1px solid #e6e6e6;
box-shadow: 0px 4px 7px rgba(133, 136, 140, 0.25);
border-radius: 8px;
z-index: 99;
}
.add-menu-list {
padding: 15px 10px;
.add-menu-item {
white-space: nowrap;
font-size: 16px;
padding-left: 5px;
margin-bottom: 10px;
height: 30px;
line-height: 30px;
display: flex;
align-items: center;
&:last-child {
margin-bottom: 0;
}
}
}
</style>

@ -1,8 +1,26 @@
<template>
<view></view>
<ContactList />
</template>
<script lang="ts" setup>
import ContactList from './contact-list/index.vue'
import { onShow } from '@dcloudio/uni-app'
import { setContactTabUnread, setTabUnread } from '../utils/msg';
import { trackInit } from '../utils/reporter'
trackInit('ContactUIKit')
onShow(() => {
setTabUnread()
setContactTabUnread()
})
</script>
<style>
@import "../styles/common.scss";
page {
height: 100vh;
overflow: hidden;
}
</style>

@ -0,0 +1,100 @@
<template>
<div
class="p2p-msg-receipt-wrapper"
v-if="
conversationType ===
V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P &&
p2pMsgReceiptVisible
"
>
<div v-if="p2pMsgRotateDeg == 360" class="icon-read-wrapper">
<Icon type="icon-read" :size="16"></Icon>
</div>
<div v-else class="sector">
<span
class="cover-1"
:style="`transform: rotate(${p2pMsgRotateDeg}deg)`"
></span>
<span
:class="p2pMsgRotateDeg >= 180 ? 'cover-2 cover-3' : 'cover-2'"
></span>
</div>
</div>
</template>
<script lang="ts" setup>
import { defineProps, withDefaults, ref, onMounted, computed } from 'vue'
import Icon from '../../../components/Icon.vue'
import { V2NIMConst } from 'nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK'
import { autorun } from 'mobx'
import { V2NIMConversationForUI } from '@xkit-yx/im-store-v2/dist/types/types'
const props = withDefaults(
defineProps<{
conversation: V2NIMConversationForUI
}>(),
{}
)
// p2p p2p false
const p2pMsgReceiptVisible = uni.$UIKitStore.localOptions.p2pMsgReceiptVisible
const conversationType =
uni.$UIKitNIM.V2NIMConversationIdUtil.parseConversationType(
props.conversation.conversationId
)
const p2pMsgRotateDeg = computed(() => {
return (props?.conversation?.msgReceiptTime || 0) >=
(props?.conversation?.lastMessage?.messageRefer?.createTime || 0)
? 360
: 0
})
</script>
<style scoped lang="scss">
.p2p-msg-receipt-wrapper {
width: 18px;
height: 18px;
margin-right: 3px;
overflow: hidden;
line-height: 18px;
}
.icon-read-wrapper {
margin: 0px 3px 0px 0;
width: 18px;
height: 18px;
overflow: hidden;
display: flex;
align-items: center;
}
.sector {
display: inline-block;
position: relative;
overflow: hidden;
border: 2px solid #4c84ff;
width: 12px;
height: 12px;
background-color: #ffffff;
border-radius: 50%;
margin: 0px 3px 0 0;
.cover-1,
.cover-2 {
position: absolute;
top: 0;
width: 50%;
height: 100%;
background-color: #ffffff;
}
.cover-1 {
background-color: #4c84ff;
transform-origin: right;
}
.cover-3 {
right: 0;
background-color: #4c84ff;
}
}
</style>

@ -0,0 +1,453 @@
<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. 52
if (messageTime.isSame(now, "year")) {
const month = messageTime.month() + 1;
const day = messageTime.date();
return `${month}${day}`;
}
// 5. 202478
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;
&.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;
}
.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>

@ -1,8 +1,345 @@
<template>
<view></view>
<div class="conversation-wrapper">
<!-- 页面初始化的过程中sessionList编译到小程序和h5出现sessionList为undefined的情况即使给了默认值为空数组故在此处进行判断 -->
<div class="conversation-list-wrapper">
<!-- 此处的key如果用session.id会在ios上渲染存在问题会出现会话列表显示undefined -->
<view class="g_bg_f5d g_h_48 g_fs_18 g_fw_600" v-if="conversationList.length > 0" style="padding: 12px 16px; border-bottom: 1rpx solid #d5d5d5; border-top: 1rpx solid #d5d5d5"> </view>
<div v-for="(conversation, index) in conversationList" :key="conversation.renderKey">
<ConversationItem :key="conversation.renderKey" :loading="loading" :showMoreActions="currentMoveSessionId === conversation.conversationId" :conversation="conversation" @delete="handleSessionItemDeleteClick" :heihei="'mmp'+index" @stickyToTop="handleSessionItemStickTopChange" @click="handleSessionItemClick" @leftSlide="handleSessionItemLeftSlide" />
</div>
<!-- <u-skeleton :loading="loading" :animation="true" bgColor="#ededed"></u-skeleton> -->
</div>
<div style="height: 48px; background-color: #ededed"></div>
</div>
</template>
<script lang="ts" setup>
import { onUnmounted, ref, defineExpose } from "../../../utils/transformVue";
import { autorun } from "mobx";
import { onHide } from "@dcloudio/uni-app";
import Icon from "../../../components/Icon.vue";
import NetworkAlert from "../../../components/NetworkAlert.vue";
import Empty from "../../../components/Empty.vue";
import ConversationItem from "./conversation-item.vue";
import { setContactTabUnread, setTabUnread } from "../../../utils/msg";
import { t } from "../../../utils/i18n";
import { customNavigateTo } from "../../../utils/customNavigate";
import { deepClone } from "../../../utils";
import { V2NIMConversation } from "nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK/V2NIMConversationService";
const corpUserFlag = ref(uni.getStorageSync("apply-userinfo").corpUserFlag);
const conversationList = ref<V2NIMConversation[]>([]);
defineExpose({
conversationList,
});
const addDropdownVisible = ref(false);
const loading = ref(false);
const currentMoveSessionId = ref("");
let buttonClass = "button-box";
// #ifdef MP
buttonClass = "button-box-mp";
// #endif
const handleSessionItemLeftSlide = (conversation: V2NIMConversation | null) => {
// conversation null
if (conversation) {
currentMoveSessionId.value = conversation.conversationId;
} else {
currentMoveSessionId.value = "";
}
};
let flag = false;
//
const handleSessionItemClick = async (conversation: V2NIMConversation) => {
if (flag) return;
currentMoveSessionId.value = "";
try {
flag = true;
await uni.$UIKitStore.uiStore.selectConversation(conversation.conversationId);
customNavigateTo({
url: "/pages/Chat/index",
});
} catch {
uni.showToast({
title: t("selectSessionFailText"),
icon: "error",
});
} finally {
flag = false;
}
};
//
const handleSessionItemDeleteClick = async (conversation: V2NIMConversation) => {
try {
await uni.$UIKitStore.conversationStore.deleteConversationActive(conversation.conversationId);
} catch {
uni.showToast({
title: t("deleteSessionFailText"),
icon: "error",
});
}
};
//
const handleSessionItemStickTopChange = async (conversation: V2NIMConversation) => {
if (conversation.stickTop) {
try {
await uni.$UIKitStore.conversationStore.stickTopConversationActive(conversation.conversationId, false);
} catch {
uni.showToast({
title: t("deleteStickTopFailText"),
icon: "error",
});
}
} else {
try {
await uni.$UIKitStore.conversationStore.stickTopConversationActive(conversation.conversationId, true);
} catch {
uni.showToast({
title: t("addStickTopFailText"),
icon: "error",
});
}
}
};
const showAddDropdown = () => {
addDropdownVisible.value = true;
};
const hideAddDropdown = () => {
addDropdownVisible.value = false;
};
const onDropdownClick = (urlType: "addFriend" | "createGroup") => {
const urlMap = {
//
addFriend: "/pages/Friend/add-friend/index",
//
createGroup: "/pages/Group/group-create/index",
};
addDropdownVisible.value = false;
customNavigateTo({
url: urlMap[urlType],
});
};
const jumpToSearch = () => {
customNavigateTo({
url: "/pages/Conversation/conversation-search/index",
});
};
onHide(() => {
addDropdownVisible.value = false;
});
const conversationListWatch = autorun(() => {
const conversations = deepClone(uni.$UIKitStore?.uiStore?.conversations) || [];
// renderKey
const conversationsWithKey = conversations.map((conversation: V2NIMConversation) => ({
...conversation,
renderKey: conversation.conversationId,
}));
//
const stickyList = conversationsWithKey.filter((conv: V2NIMConversation) => conv.stickTop);
const normalList = conversationsWithKey.filter((conv: V2NIMConversation) => !conv.stickTop);
// console.log("stickyList", stickyList);
// console.log("normalList", normalList);
//
const sortStickyList = (list: V2NIMConversation[]) =>
list.sort((a, b) => new Date(b.updateTime).getTime() - new Date(a.updateTime).getTime());
const sortNormalList = (list: V2NIMConversation[]) =>
list.sort((a, b) => new Date(b.lastMessage?.messageRefer.createTime || b.updateTime).getTime() - new Date(a.lastMessage?.messageRefer.createTime || a.updateTime).getTime());
//
conversationList.value = [...sortStickyList(stickyList), ...sortNormalList(normalList)];
setTimeout(() => {
loading.value = false;
}, 2000);
console.log("conversationListWatch", conversationList.value);
setTabUnread();
});
// const conversationListWatch = autorun(() => {
// const conversations = deepClone(uni.$UIKitStore?.uiStore?.conversations) || [];
// // renderKey
// const conversationsWithKey = conversations.map((conversation: V2NIMConversation) => ({
// ...conversation,
// renderKey: conversation.conversationId,
// }));
// //
// const stickyList = conversationsWithKey.filter((conv: V2NIMConversation) => conv.stickTop);
// const normalList = conversationsWithKey.filter((conv: V2NIMConversation) => !conv.stickTop);
// // updateTime
// const sortList = (list: V2NIMConversation[]) => list.sort((a: V2NIMConversation, b: V2NIMConversation) => new Date(b.updateTime).getTime() - new Date(a.updateTime).getTime()); //list
// //
// conversationList.value = [...sortList(stickyList), ...sortList(normalList)];
// setTimeout(() => {
// loading.value = false;
// }, 2000);
// console.log("conversationListWatch", conversationList.value);
// setTabUnread();
// });
const getTotalUnreadMsgsCountWatch = autorun(() => {
// console
console.log(
"unreadSysMsgCount:",
uni.$UIKitStore?.sysMsgStore?.getTotalUnreadMsgsCount()
);
setContactTabUnread();
});
onUnmounted(() => {
conversationListWatch();
getTotalUnreadMsgsCountWatch();
});
</script>
<style lang="scss" scoped>
@import "../../styles/common.scss";
.conversation-wrapper {
overflow: hidden;
}
.navigation-bar {
position: fixed;
// height: 60px;
// border-bottom: 1rpx solid #e9eff5;
// padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
// padding-top: var(--status-bar-height);
background-color: #fff;
width: 100%;
opacity: 1;
z-index: 999;
}
.conversation-search {
display: flex;
align-items: center;
height: 54px;
box-sizing: border-box;
overflow: hidden;
padding: 10px;
}
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
height: 34px;
overflow: hidden;
box-sizing: border-box;
padding: 8px 10px;
background: #f3f5f7;
border-radius: 5px;
}
.search-input {
margin-left: 5px;
color: #999999;
}
.search-icon-wrapper {
height: 22px;
display: flex;
align-items: center;
}
.block {
height: 60px;
width: 100%;
display: block;
padding-top: var(--status-bar-height);
}
.conversation-list-wrapper {
// height: calc(100% - 60px - var(--status-bar-height));
min-height: 85vh;
background-color: #ededed;
box-sizing: border-box;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.logo-box {
display: flex;
align-items: center;
font-size: 20px;
font-weight: 500;
.logo-img {
width: 32px;
height: 32px;
margin-right: 10px;
}
}
.button-icon-add {
position: relative;
right: 20px;
}
.dropdown-mark {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
}
.dropdown-container {
position: absolute;
top: -70px;
right: 30px;
min-width: 122px;
min-height: 40px;
background-color: #fff;
border: 1rpx solid #eee;
box-shadow: 0px 4px 7px rgba(133, 136, 140, 0.25);
border-radius: 8px;
z-index: 99;
}
.add-menu-list {
padding: 15px 10px;
.add-menu-item {
white-space: nowrap;
font-size: 16px;
padding-left: 5px;
margin-bottom: 10px;
height: 30px;
line-height: 30px;
display: flex;
align-items: center;
&:last-child {
margin-bottom: 0;
}
}
}
.conversation-block {
width: 100%;
height: 72px;
}
</style>

@ -0,0 +1,55 @@
.ok-btn {
color: #fff;
background-color: rgb(20, 146, 209);
padding: 10px;
font-size: 16px;
text-align: center;
position: fixed;
bottom: 0;
left: calc(50% - 25vw);
width: 50vw;
border-radius: 3px;
}
.ok-btn-mp {
color: #fff;
background-color: rgb(20, 146, 209);
padding: 10px;
font-size: 16px;
text-align: center;
position: fixed;
bottom: 20px;
left: calc(50% - 25vw);
width: 50vw;
border-radius: 3px;
}
.button-box,.button-box-mp {
display: flex;
align-items: center;
position: fixed;
z-index: 100;
bottom: 120px;
right: 30px;
}
.button-box {
display: flex;
align-items: center;
position: relative;
}
.button-box,
.button-box-mp .button-icon {
width: 40px;
height: 40px;
margin-left: 20px;
}
.button-box{
width: 40px;
height: 40px;
position: fixed;
z-index: 100;
bottom: 70px;
right: 30px;
}

@ -0,0 +1,35 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
"sourceMap": true,
"strictFunctionTypes": false,
"preserveValueImports": false,
"isolatedModules": false,
"baseUrl": ".",
"types": ["node", "webpack-env", "vue/types", "vue-router/types"],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"files": ["*.d.ts", "**/*.ts", "**/*.tsx", "**/*.vue", "**/*.ts", "**/*.tsx"],
"include": [
"*.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.vue",
"**/*.ts",
"**/*.tsx"
],
"exclude": ["node_modules"]
}

@ -12,11 +12,11 @@
>
<view class="" v-show="current == 0">
<view class="g_bg_f">
<!-- <ConversationList ref="contactList" /> -->
<ConversationList ref="contactList" />
</view>
</view>
<view class="g_bg_f" v-if="current == 1">
<!-- <telPanel /> -->
<telPanel />
</view>
</scroll-view>
<view class="g_h_all" v-if="!isLogin">
@ -52,11 +52,11 @@
<script lang="ts" setup>
import { ref, onMounted, computed, getCurrentInstance } from "vue";
// import ConversationList from "../NEUIKit/pages/Conversation/conversation-list/index.vue";
import ConversationList from "../NEUIKit/pages/Conversation/conversation-list/index.vue";
import { trackInit } from "../NEUIKit/utils/reporter.ts";
import { onShow, onLoad } from "@dcloudio/uni-app";
// import telPanel from "../NEUIKit/pages/Contact/contact-list/index.vue";
// import chatPanel from "../NEUIKit/pages/Contact/BcFriend.vue";
import telPanel from "../NEUIKit/pages/Contact/contact-list/index.vue";
import chatPanel from "../NEUIKit/pages/Contact/BcFriend.vue";
const G = getCurrentInstance().appContext.app.config.globalProperties.G;
import gEmpty from "@/components/empty.vue";
// import gMessageCell from "@/components/panel/messageCell.vue";
@ -210,7 +210,7 @@ const updateType = (e) => {
</script>
<style lang="scss">
// @import "../NEUIKit/pages/styles/common.scss";
@import "../NEUIKit/pages/styles/common.scss";
.i-items {
// border-bottom: 1px solid #eee;
@ -229,4 +229,4 @@ const updateType = (e) => {
// border-bottom: none;
// }
}
</style>
</style>

@ -447,7 +447,7 @@
<div class="g_fs_12 g_c_6" style="margin-top: 8px">{{ isSc ? "已收藏" : "收藏" }}</div>
</button>
</div>
<div class="g_flex_none g_flex_column_center" v-if="false">
<div class="g_flex_none g_flex_column_center">
<button class="g_pl_0 g_pr_0 g_bg_f g_mr_12 g_w_36" hover-class="thover" style="line-height: 1; border-radius: 0" @click="goIm">
<icon class="iconfont icon-xiaoxi" style="color: #787878; line-height: 1; margin-top: -5px; font-size: 20px !important; position: relative; top: 3px"></icon>
<div class="g_fs_12 g_c_6" style="margin-top: 8px">IM</div>

@ -1,5 +1,5 @@
// let ajaxUrl = "http://192.168.3.83:8001";
let ajaxUrl = "https://daotian.matripe.com.cn";
let ajaxUrl = "http://192.168.3.83:8001";
// let ajaxUrl = "https://daotian.matripe.com.cn";
let data = {
ajaxUrl: ajaxUrl,
Get ($url = '', $parmas = {}, callback = () => { }, failback = () => { }) {

@ -1,5 +1,5 @@
// let ajaxUrl = "http://192.168.3.83:8001";
let ajaxUrl = "https://daotian.matripe.com.cn";
let ajaxUrl = "http://192.168.3.83:8001";
// let ajaxUrl = "https://daotian.matripe.com.cn";
let data = {
wyyxajaxUrl: ajaxUrl,
wyyxGet ($url = '', $parmas = {}, callback = () => { }, failback = () => { }) {

Loading…
Cancel
Save