更新ContactModal.vue组件,添加NRadio和NVirtualList支持,优化联系人选择逻辑,调整搜索过滤器,改进用户界面和交互体验。
This commit is contained in:
parent
fad84e5bf3
commit
94cf0f9f63
2
components.d.ts
vendored
2
components.d.ts
vendored
@ -59,7 +59,9 @@ declare module 'vue' {
|
||||
NoticeTab: typeof import('./src/components/group/manage/NoticeTab.vue')['default']
|
||||
NotificationApi: typeof import('./src/components/common/NotificationApi.vue')['default']
|
||||
NProgress: typeof import('naive-ui')['NProgress']
|
||||
NRadio: typeof import('naive-ui')['NRadio']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
||||
RevokeMessage: typeof import('./src/components/talk/message/RevokeMessage.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
|
@ -1,12 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { NModal, NInput, NScrollbar, NCheckbox, NTabs, NTab } from 'naive-ui'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { NModal, NInput, NScrollbar, NCheckbox, NTabs, NTab, NButton, NIcon, NImage, NRadio, NVirtualList, NEmpty } from 'naive-ui'
|
||||
import { Search, Delete } from '@icon-park/vue-next'
|
||||
import { ServeGetContacts } from '@/api/contact'
|
||||
import { ServeGetGroups } from '@/api/group'
|
||||
import XNModal from '@/components/x-naive-ui/x-n-modal/index.vue'
|
||||
const emit = defineEmits(['close', 'on-submit'])
|
||||
|
||||
import { CloseCircle } from '@vicons/ionicons5'
|
||||
interface Item {
|
||||
id: number
|
||||
type: number
|
||||
@ -17,16 +17,16 @@ interface Item {
|
||||
keyword: string
|
||||
}
|
||||
|
||||
const tabsIndex = ref<number>(1)
|
||||
const isShowBox = ref(true)
|
||||
const loading = ref(true)
|
||||
const items = ref<Item[]>([])
|
||||
const keywords = ref('')
|
||||
const loadGroupStatus = ref(false)
|
||||
|
||||
// 搜索过滤器:不再按类型过滤,将好友和群组融合在一起
|
||||
const searchFilter = computed(() => {
|
||||
return items.value.filter((item: Item) => {
|
||||
return tabsIndex.value == item.type && item.keyword.match(keywords.value) != null
|
||||
return item.keyword.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
})
|
||||
})
|
||||
|
||||
@ -40,6 +40,7 @@ const isCanSubmit = computed(() => {
|
||||
|
||||
const onLoad = () => {
|
||||
onLoadContact()
|
||||
onLoadGroup()
|
||||
}
|
||||
|
||||
const onLoadContact = () => {
|
||||
@ -55,7 +56,7 @@ const onLoadContact = () => {
|
||||
avatar: item.avatar,
|
||||
type: 1,
|
||||
name: item.remark || item.nickname,
|
||||
keyword: item.remark + item.nickname,
|
||||
keyword: (item.remark || '') + item.nickname,
|
||||
remark: item.remark,
|
||||
checked: false
|
||||
}
|
||||
@ -75,6 +76,7 @@ const onLoadGroup = async () => {
|
||||
loading.value = true
|
||||
let { code, data } = await ServeGetGroups()
|
||||
if (code != 200) {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
@ -101,6 +103,13 @@ const onMaskClick = () => {
|
||||
}
|
||||
|
||||
const onTriggerContact = (item: any) => {
|
||||
// 如果是单选模式,先取消所有选中
|
||||
if (selectType.value === 1) {
|
||||
items.value.forEach(contact => {
|
||||
contact.checked = false
|
||||
})
|
||||
}
|
||||
|
||||
let data = items.value.find((val: any) => val.id === item.id)
|
||||
|
||||
if (data) {
|
||||
@ -108,6 +117,18 @@ const onTriggerContact = (item: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
const onRemoveContact = (item: any) => {
|
||||
let data = items.value.find((val: any) => val.id === item.id)
|
||||
|
||||
if (data) {
|
||||
data.checked = false
|
||||
}
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
isShowBox.value = false
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
let data = checkedFilter.value.map((item: any) => {
|
||||
return {
|
||||
@ -119,136 +140,113 @@ const onSubmit = () => {
|
||||
emit('on-submit', data)
|
||||
}
|
||||
|
||||
const onTabs = (value: number) => {
|
||||
tabsIndex.value = value
|
||||
if (value == 2) {
|
||||
onLoadGroup()
|
||||
}
|
||||
// 1 单选 2 多选
|
||||
const selectType = ref(1)
|
||||
const changeSelectType = () => {
|
||||
selectType.value = selectType.value == 1 ? 2 : 1
|
||||
|
||||
// 切换选择模式时清空已选择的联系人
|
||||
items.value.forEach(item => {
|
||||
item.checked = false
|
||||
})
|
||||
}
|
||||
|
||||
onLoad()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<x-n-modal
|
||||
v-model:show="isShowBox"
|
||||
title="选择联系人"
|
||||
class="modal-radius"
|
||||
style="width: 997px; height: 740px;background-color: #F9F9FD"
|
||||
:on-after-leave="onMaskClick"
|
||||
>
|
||||
<section class="el-container launch-box">
|
||||
<aside class="el-aside bdr-r" style="width: 240px">
|
||||
<section class="el-container is-vertical height100">
|
||||
<header class="el-header tabs">
|
||||
<n-tabs type="line" justify-content="space-around" @update:value="onTabs">
|
||||
<n-tab name="1"> 好友 </n-tab>
|
||||
<n-tab name="2"> 群聊 </n-tab>
|
||||
<!-- <n-tab name="企业"> 企业 </n-tab> -->
|
||||
</n-tabs>
|
||||
|
||||
</header>
|
||||
|
||||
<header class="el-header sub-header">
|
||||
<n-input placeholder="搜索" v-model:value="keywords" clearable size="small">
|
||||
<x-n-modal v-model:show="isShowBox" title="合并转发/逐条转发" style="width: 997px; height: 740px;background-color: #F9F9FD"
|
||||
:on-after-leave="onMaskClick" content-style="display: flex; justify-content: center; align-items: center;">
|
||||
<div class="w-927px h-627px bg-#fff rounded-3px px-35px py-20px">
|
||||
<div class="flex items-center justify-between mb-28px">
|
||||
<div class="text-#333639">搜索</div>
|
||||
<div class="w-779px h-34px">
|
||||
<n-input v-model:value="keywords" type="text" clearable placeholder="请输入">
|
||||
<template #prefix>
|
||||
<n-icon :component="Search" />
|
||||
</template>
|
||||
</n-input>
|
||||
</header>
|
||||
|
||||
<main class="el-main" v-loading="loading" loading-text="加载中...">
|
||||
<n-scrollbar>
|
||||
<div class="friend-items">
|
||||
<div
|
||||
class="friend-item pointer"
|
||||
v-for="item in searchFilter"
|
||||
:key="item.id"
|
||||
@click="onTriggerContact(item)"
|
||||
>
|
||||
<div class="avatar">
|
||||
<im-avatar
|
||||
class="pointer"
|
||||
:src="item.avatar"
|
||||
:size="25"
|
||||
:username="item.remark || item.name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<span class="text-ellipsis">{{ item.remark || item.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<n-checkbox size="small" :checked="item.checked" />
|
||||
<div class="flex justify-between">
|
||||
<div class="w-260px h-517px rounded-4px border-1px border-solid border-#E5E5E5 px-12px">
|
||||
<div class="border-b-2px border-b-solid border-b-#FBFBFB h-35px flex items-center justify-end">
|
||||
<n-button text color="#46299D" class="text-14px" @click="changeSelectType">
|
||||
{{ selectType === 1 ? '多选' : '单选' }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div>
|
||||
<n-virtual-list v-if="!loading" style="max-height: 470px" :item-size="65" :items="searchFilter">
|
||||
<template #default="{ item }">
|
||||
<div class="flex items-center border-b-2px border-b-solid h-65px border-b-#FBFBFB"
|
||||
@click="onTriggerContact(item)">
|
||||
<div class="mr-22px">
|
||||
<n-radio v-if="selectType === 1" :checked="item.checked" />
|
||||
<n-checkbox v-else :checked="item.checked" />
|
||||
</div>
|
||||
<div class="mr-10px">
|
||||
<n-image class="w-42px h-42px rounded-full" :src="item.avatar" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-ellipsis">{{ item.name }}</span>
|
||||
<span v-if="item.type == 2" class="badge group ml-2">群</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</n-virtual-list>
|
||||
<div v-else class="flex-center h-470px">
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</main>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<main class="el-main">
|
||||
<section class="el-container is-vertical height100">
|
||||
<main class="el-main o-hidden">
|
||||
<n-scrollbar class="friend-items">
|
||||
<div class="friend-items">
|
||||
<div v-show="!checkedFilter.length" style="padding-top: 100px">
|
||||
<n-empty size="200" description="暂无数据">
|
||||
<div class="w-578px h-517px rounded-4px border-1px border-solid border-#E5E5E5 px-12px">
|
||||
<div class="border-b-2px border-b-solid border-b-#FBFBFB h-35px flex items-center text-14px text-#000">
|
||||
发送给
|
||||
</div>
|
||||
<div class="h-350px border-b-2px border-b-solid border-b-#FBFBFB">
|
||||
<div v-if="checkedFilter.length > 0">
|
||||
<n-virtual-list style="max-height: 350px" :item-size="65" :items="checkedFilter">
|
||||
<template #default="{ item }">
|
||||
<div class="flex items-center border-b-2px border-b-solid h-65px border-b-#FBFBFB pr-20px">
|
||||
<div class="mr-10px">
|
||||
<n-image class="w-42px h-42px rounded-full" :src="item.avatar" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-ellipsis">{{ item.name }}</span>
|
||||
<span v-if="item.type == 2" class="badge group ml-2">群</span>
|
||||
</div>
|
||||
<n-button class="ml-auto" text color="#C7C7C9" @click="onRemoveContact(item)">
|
||||
<n-icon :component="CloseCircle" size="18" />
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-virtual-list>
|
||||
</div>
|
||||
<div v-else class="flex-center h-350px">
|
||||
<n-empty size="medium" description="暂无选择联系人">
|
||||
<template #icon>
|
||||
<img src="@/assets/image/no-data.svg" alt="" />
|
||||
</template>
|
||||
</n-empty>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="friend-item pointer"
|
||||
v-for="item in checkedFilter"
|
||||
:key="item.id"
|
||||
@click="onTriggerContact(item)"
|
||||
>
|
||||
<div class="avatar">
|
||||
<im-avatar
|
||||
class="pointer"
|
||||
:src="item.avatar"
|
||||
:size="25"
|
||||
:username="item.remark || item.name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<span class="text-ellipsis">
|
||||
{{ item.remark || item.name }}
|
||||
<div class="flex flex-col items-center justify-center h-120px">
|
||||
<div class="text-14px text-#999999 mb-23px">
|
||||
<span>[{{ selectType === 1 ? '合并转发' : '逐条转发' }}]</span>
|
||||
<span v-if="checkedFilter.length > 0">
|
||||
{{ checkedFilter.map(item => item.name).join('、') }}的会话记录
|
||||
</span>
|
||||
<span v-if="item.type == 2" class="badge group">群</span>
|
||||
<span v-else>请选择联系人</span>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<n-icon :size="16" :component="Delete" />
|
||||
<div class="flex justify-center items-center">
|
||||
<n-button color="#C7C7C9" class="w-250px h-34px text-14px text-#fff mr-10px" @click="onCancel">取消</n-button>
|
||||
<n-button color="#46299D" class="w-250px h-34px text-14px text-#fff"
|
||||
@click="onSubmit" :disabled="isCanSubmit">发送</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</main>
|
||||
</section>
|
||||
</main>
|
||||
</section>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer">
|
||||
<div>
|
||||
<span>已选择({{ checkedFilter.length }})</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<n-button type="tertiary" @click="isShowBox = false"> 取消 </n-button>
|
||||
<n-button type="primary" class="mt-l15" @click="onSubmit" :disabled="isCanSubmit">
|
||||
确定
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</x-n-modal>
|
||||
</template>
|
||||
|
||||
@ -257,74 +255,27 @@ onLoad()
|
||||
font-weight: unset;
|
||||
}
|
||||
|
||||
.launch-box {
|
||||
height: 410px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.sub-header {
|
||||
height: 50px;
|
||||
padding: 10px 15px;
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.friend-items {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 0 15px;
|
||||
|
||||
.friend-item {
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 5px 0;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 30px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 auto;
|
||||
padding-left: 8px;
|
||||
.text-ellipsis {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
justify-content: flex-start;
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
flex-shrink: 0;
|
||||
width: 30px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
text-overflow: ellipsis;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
&.group {
|
||||
color: #3370ff !important;
|
||||
background-color: #e1eaff !important;
|
||||
font-size: 12px;
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user