新增新版通讯录功能,接入我的群组tab页;实现部分静态页面和交互

This commit is contained in:
wangyifeng 2025-06-04 17:05:49 +08:00
parent 580439fec8
commit 7f50f09bbe
8 changed files with 490 additions and 9 deletions

View File

@ -240,3 +240,12 @@ export const ServeConvertText = (data) => {
data,
})
}
// 获取用户所在群聊列表(我的群聊)
export const ServeUserGroupChatList = (data) => {
return request({
url: '/api/v1/group/user/list',
method: 'POST',
data,
})
}

View File

@ -92,8 +92,14 @@ export function createApp() {
}
// 通讯录跳转
window.handleContacts = () => {
// 旧版本-按组织架构树的通讯录
// uni.navigateTo({
// url: '/pages/chooseByDeps/index?chooseMode=3&type=true'
// });
// 新版本-按公司别、好友、群组的通讯录
uni.navigateTo({
url: '/pages/chooseByDeps/index?chooseMode=3&type=true'
url: '/pages/addressBook/index?type=true',
});
};

View File

@ -193,6 +193,14 @@
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/addressBook/index",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {

View File

@ -0,0 +1,404 @@
<template>
<div class="address-book-page">
<zPaging ref="zPaging" :show-scrollbar="false" @scrolltolower="doLoadMore">
<template #top>
<div :class="'top_bg'">
<customNavbar
:class="'index_top_navbar'"
:title="$t('index.mine.addressBook')"
:hideHome="navshow"
:hideBack="navshow"
>
<template #left>
<tm-icon
@click="goWebHome"
v-if="navshow"
name="tmicon-angle-left"
style="padding-left: 30rpx;"
></tm-icon>
</template>
</customNavbar>
<div class="pl-[32rpx] pr-[32rpx] pt-[32rpx] pb-[32rpx]">
<customInput
:searchText="searchVal"
@inputSearchText="inputSearchText"
></customInput>
</div>
</div>
<tm-tabs
:list="state.addressBookTabs"
align="center"
:width="750"
:height="300"
:itemWidth="250"
default-name="company"
activeColor="#46299d"
activeFontColor="#46299d"
tabs-line-ani-color="#46299d"
:showTabsLineAni="true"
:showTabsLine="false"
:activeFontSize="32"
:itemFontSize="30"
@update:activeName="updateAddressBookTab"
></tm-tabs>
</template>
<div class="address-book">
<div class="address-book-tabs-panes-list">
<div
class="tabs-panes-each address-book-company"
v-if="state.addressBookActiveTab === 'company'"
>
companycompany
</div>
<div
class="tabs-panes-each address-book-friends"
v-if="state.addressBookActiveTab === 'friends'"
>
friendsfriends
</div>
<div
class="tabs-panes-each address-book-groups"
v-if="state.addressBookActiveTab === 'groups'"
>
<div
class="groups-list-each"
v-for="(item, index) in state.myGroupsList"
:key="index"
>
<div class="groups-info">
<avatarModule
:mode="2"
:avatar="item?.avatar"
:groupType="Number(item?.group_type)"
:customStyle="{ width: '72rpx', height: '72rpx' }"
></avatarModule>
<span class="groups-name">
{{ item?.group_name }}
</span>
<span
class="groups-type"
:style="{
color:
groupTypeMapping[item?.group_type]?.result_type_color,
border:
'1px solid' +
groupTypeMapping[item?.group_type]?.result_type_color,
}"
>
{{ groupTypeMapping[item?.group_type]?.result_type }}
</span>
</div>
<div class="groups-btns">
<div class="groups-btns-each" @click="toGroupChat(item)">
<span>{{ $t('addressBook.btns.enterGroup') }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</zPaging>
</div>
</template>
<script setup>
import customInput from '@/components/custom-input/custom-input.vue'
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import avatarModule from '@/components/avatar-module/index.vue'
import { ref, onMounted, reactive, watch } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { handleSetWebviewStyle } from '@/utils/common'
import { ServeUserGroupChatList, ServeCreateTalkList } from '@/api/chat/index'
import { ServeGetSessionId } from '@/api/search/index'
import { formatTalkItem } from '@/utils/talk'
import { useDialogueStore, useTalkStore } from '@/store'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const navshow = ref(false)
const searchVal = ref('')
const state = reactive({
addressBookTabs: [], //tab
addressBookActiveTab: 'company', //tab
myGroupsListPage: 1, //
myGroupsListPageSize: 10, //
myGroupsList: [], //
hasMoreGroups: true, //
})
onLoad((options) => {
if (options.type) {
navshow.value = true
}
})
const goWebHome = () => {
uni.navigateBack()
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`handleBackHost()`)
}
})
}
onMounted(() => {
state.addressBookTabs = [
{
key: 'company',
title: t('addressBook.tabs.company'),
},
{
key: 'friends',
title: t('addressBook.tabs.friends'),
},
{
key: 'groups',
title: t('addressBook.tabs.groups'),
},
]
handleSetWebviewStyle()
})
//
const inputSearchText = (e) => {
searchVal.value = e
}
//tab
const updateAddressBookTab = (e) => {
state.addressBookActiveTab = e
}
//
const getMyGroupsList = () => {
let params = {
page: state.myGroupsListPage,
page_size: state.myGroupsListPageSize,
group_name: searchVal.value,
}
console.error(params)
ServeUserGroupChatList(params)
.then((res) => {
console.log(res)
if (res?.code === 200) {
if (state.myGroupsListPage === 1) {
state.myGroupsList = res.data?.items || []
} else {
state.myGroupsList = state.myGroupsList.concat(res.data?.items || [])
}
if (state.myGroupsList.length < res.data?.total) {
state.hasMoreGroups = true
} else {
state.hasMoreGroups = false
}
}
})
.catch((err) => {})
}
//
const doLoadMore = (e) => {
if (state.addressBookActiveTab === 'groups' && state.hasMoreGroups) {
state.myGroupsListPage += 1
getMyGroupsList()
}
}
//Id
const getSessionId = (talk_type, receiver_id) => {
return new Promise((resolve, reject) => {
let params = {
talkType: talk_type,
receiverId: receiver_id,
}
const resp = ServeGetSessionId(params)
console.log(resp)
resp.then(({ code, data }) => {
console.log(data)
if (code == 200) {
resolve(data?.sessionId)
} else {
}
})
resp.catch(() => {})
})
}
//
const toGroupChat = async (groupInfo) => {
console.error(groupInfo)
let talk_type = 2
let receiver_id = groupInfo.id
const sessionId = await getSessionId(talk_type, receiver_id)
if (useTalkStore().findTalkIndex(`${talk_type}_${receiver_id}`) === -1) {
ServeCreateTalkList({
talk_type,
receiver_id,
}).then(async ({ code, data }) => {
if (code == 200) {
let item = formatTalkItem(data)
useTalkStore().addItem(item)
}
})
}
useDialogueStore().setDialogue({
name: groupInfo.group_name,
talk_type: 2,
receiver_id: receiver_id,
})
uni.navigateTo({
url: '/pages/dialog/index?sessionId=' + sessionId,
})
}
// -groupType
const groupTypeMapping = {
0: {},
1: {},
2: {
result_type: t('index.mine.department'),
result_type_color: '#377EC6',
},
3: {
result_type: t('index.mine.project'),
result_type_color: '#C1681C',
},
4: {
result_type: t('index.type.company'),
result_type_color: '#7A58DE',
},
}
watch(
() => state.addressBookActiveTab,
(newVal) => {
if (newVal === 'groups') {
getMyGroupsList()
}
},
)
watch(
() => searchVal.value,
(newVal) => {
state.myGroupsListPage = 1
state.myGroupsList = []
if (state.addressBookActiveTab === 'groups') {
getMyGroupsList()
}
},
)
</script>
<style scoped lang="scss">
::v-deep .zp-paging-container-content {
height: 100%;
display: flex;
}
::v-deep .index_top_navbar .tmicon-angle-left {
color: #fff !important;
}
::v-deep .index_top_navbar .text-weight-b {
color: #fff !important;
}
::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child {
background: transparent !important;
border: none !important;
}
.top_bg {
background: url('@/static/image/mine/page_top.png') no-repeat;
background-size: cover;
background-position: bottom center;
}
:deep(.animateAll_tabs_tmui) {
width: 120rpx !important;
height: 10rpx !important;
}
.address-book-page {
.address-book {
flex: 1;
display: flex;
flex-direction: column;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-position: bottom center;
background-attachment: fixed;
width: 100%;
.address-book-tabs-panes-list {
padding: 30rpx 24rpx;
.tabs-panes-each {
gap: 30rpx 0;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.address-book-company {
}
.address-book-friends {
}
.address-book-groups {
.groups-list-each {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
background-color: #fff;
width: 100%;
padding: 24rpx;
border-radius: 8rpx;
.groups-info {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 16rpx;
.groups-name {
font-size: 30rpx;
font-weight: 500;
}
.groups-type {
font-size: 24rpx;
border-radius: 6rpx;
font-weight: bold;
padding: 6rpx 12rpx;
flex-shrink: 0;
}
}
.groups-btns {
flex-shrink: 0;
margin: 0 0 0 16rpx;
.groups-btns-each {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: #46299d;
padding: 16rpx;
border-radius: 8rpx;
span {
color: #fff;
font-size: 24rpx;
font-weight: 400;
line-height: 1;
}
}
}
}
}
}
}
}
</style>

View File

@ -61,13 +61,17 @@
<template #bottom>
<customBtn
:isBottom="true"
:btnText="$t('user.detail.sendMsg')"
:btnText="
state.canSendMsg
? $t('user.detail.sendMsg')
: $t('addressBook.btns.addFriend')
"
:subBtnText="
state.userInfo.sys_id === state.uid
!state.canSendMsg || state.userInfo.sys_id === state.uid
? ''
: $t('user.detail.ringBell')
"
@clickBtn="toTalkUser"
@clickBtn="checkSendPermission"
@clickSubBtn="handleCall"
></customBtn>
</template>
@ -123,6 +127,7 @@ const state = reactive({
isShowPhoneCall: false, //
phoneNumber: '', //
uid: computed(() => userStore.uid), //id
canSendMsg: false, //
})
onLoad((options) => {
@ -237,6 +242,16 @@ const doPhoneCall = () => {
},
})
}
//
const checkSendPermission = () => {
if(state.canSendMsg){
toTalkUser()
} else {
message.success(t('addressBook.message.addSuccess') + ' !')
state.canSendMsg = true
}
}
</script>
<style scoped lang="scss">
.outer-layer {

View File

@ -66,11 +66,11 @@
/>
<template v-slot:label>
<div
class="w-full h-[208rpx] pt-[22rpx] pb-[32rpx] pl-[14rpx] pr-[12rpx]"
class="w-full px-[14rpx]"
>
<div
@click="creatGroupChat"
class="flex items-center pl-[22rpx] mb-[32rpx]"
class="flex items-center pl-[22rpx] py-[32rpx]"
>
<div class="mr-[26rpx] flex items-center">
<tm-image
@ -88,7 +88,25 @@
<div class="divider"></div>
<div
@click="toAddressBookPage"
class="flex items-center pl-[22rpx] mt-[32rpx]"
class="flex items-center pl-[22rpx] py-[32rpx]"
>
<div class="mr-[26rpx] flex items-center">
<tm-image
:width="40"
:height="44"
:src="addFriend"
></tm-image>
</div>
<div
class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold"
>
添加好友
</div>
</div>
<div class="divider"></div>
<div
@click="toAddressBookPage"
class="flex items-center pl-[22rpx] py-[32rpx]"
>
<div class="mr-[26rpx] flex items-center">
<tm-image
@ -144,6 +162,7 @@ import chatItem from './components/chatItem.vue'
import addCircle from '@/static/image/chatList/addCircle.png'
import cahtPopover from '@/static/image/chatList/cahtPopover.png'
import zu3289 from '@/static/image/chatList/zu3289@2x.png'
import addFriend from '@/static/image/chatList/addFriend.png'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { handleSetWebviewStyle } from '@/utils/common'
const paging = ref()
@ -218,8 +237,14 @@ const toSearchPage = () => {
//
const toAddressBookPage = () => {
// -
// uni.navigateTo({
// url: '/pages/chooseByDeps/index?chooseMode=3',
// })
// -
uni.navigateTo({
url: '/pages/chooseByDeps/index?chooseMode=3',
url: '/pages/addressBook/index',
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

View File

@ -193,5 +193,19 @@
"release_hand_to_send": "松开发送",
"release_hand_to_cancel": "松开取消",
"hold_to": "按住",
"speak": "说话"
"speak": "说话",
"addressBook": {
"tabs": {
"company": "组织架构",
"friends": "我的好友",
"groups": "我的群组"
},
"btns": {
"enterGroup": "进入群聊",
"addFriend": "添加好友"
},
"message": {
"addSuccess": "添加成功"
}
}
}