Skip to content

Commit

Permalink
feat: ✨️开发用户列表,封装异步组件hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
ZRMYDYCG committed Mar 5, 2025
1 parent 6c934a6 commit e3ec547
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './modules/useAsyncComponent.ts'
export * from './modules/useCurrentInstance.ts'
export * from './modules/useLoadMore.ts'
export * from './modules/usePageList.ts'
35 changes: 35 additions & 0 deletions src/hooks/modules/useAsyncComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { isFunction } from '@/utils/helper/is'
import type { DefineComponent } from 'vue'
import { defineAsyncComponent } from 'vue'

interface AsyncComponentOptions {
component: () => Promise<DefineComponent>
wait?: () => Promise<void>
timeout?: number
}

export const useAsyncComponent = (options: AsyncComponentOptions): { AsyncComponent: DefineComponent } => {
const { component, wait, timeout } = options

const loadDelay = async (wait: () => Promise<void>) => {
if (!isFunction(wait)) {
throw new Error(`wait: ${wait}需要为函数`)
}
await wait()
}

const AsyncComponent = defineAsyncComponent({
// 加载函数
loader: async () => {
if (wait) {
await loadDelay(wait)
}
return component()
},
timeout,
})

return {
AsyncComponent,
}
}
7 changes: 7 additions & 0 deletions src/view/contacts/components/add-contact-dialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts"></script>

<template>
<div></div>
</template>

<style scoped></style>
7 changes: 7 additions & 0 deletions src/view/contacts/components/contact-detail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts"></script>

<template>
<div></div>
</template>

<style scoped></style>
179 changes: 179 additions & 0 deletions src/view/contacts/components/contact-list.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<script setup lang="ts">
import GcColumn from '@/components/Column/index.vue'
import GcList from '@/components/List/index.vue'
import { useCurrentInstance, useLoadMore, usePageList } from '@/hooks'
import { onMounted, ref } from 'vue'
interface ContactItem {
user_id: string | number
nickname: string
avatar: string
}
interface SearchForm {
keywords: string
}
const emit = defineEmits<{
(e: 'change', contactId: string | number, contact: ContactItem): void
(e: 'add-contact'): void
(e: 'show-new-contact'): void
}>()
const { $api } = useCurrentInstance()
// 表单类型
const searchFormMdl = ref<SearchForm>({
keywords: '',
})
// 组件引用类型
const contactListRef = ref<InstanceType<typeof GcList> | null>(null)
const gcColumnRef = ref<InstanceType<typeof GcColumn> | null>(null)
// 联系人列表逻辑
const {
list: contactList,
loadding,
getPageList,
} = usePageList<ContactItem>({
getPageListApi: $api.contact.getContactList,
searchParams: searchFormMdl,
})
// 加载更多逻辑
onMounted(async () => {
await getPageList()
if (contactList.value.length) {
contactListRef.value?.handleChangeItem(contactList.value[0])
}
const container = gcColumnRef.value?.elScrollbar.wrapRef
if (container) {
useLoadMore({
type: 'bottom',
scrollBottomCallback: getPageList,
container,
distance: 150,
})
}
})
// 联系人切换处理
const handleChangeContactListItem = (contactId: string | number, contact: ContactItem) => {
// console.log('联系人item切换', contactId, contact)
emit('change', contactId, contact)
}
</script>

<template>
<div class="contact-list">
<GcColumn ref="gcColumnRef">
<template #header>
<div class="search">
<el-input v-model="searchFormMdl.keywords" placeholder="搜索" clearable>
<template #prefix>
<i class="ri-search-line"></i>
</template>
</el-input>
</div>

<div class="oprate">
<el-button circle size="small" title="添加好友" @click="$emit('add-contact')">
<template #icon>
<i class="ri-zoom-in-line"></i>
</template>
</el-button>

<!-- <el-button circle size="small" title="新的朋友" @click="$emit('show-new-contact')">
<template #icon>
<i class="ri-user-received-line"></i>
</template>
</el-button> -->
</div>
</template>

<template #scroll-header>
<div class="list-title">通讯录</div>
</template>
<GcList
ref="contactListRef"
:list="contactList"
:options="{
key: 'user_id',
}"
@change="handleChangeContactListItem"
>
<template #default="{ item }">
<div class="contact-item flex w-[100%]">
<div class="user flex w-[100%] items-center">
<div class="avater">
<img :src="item.avatar" alt="" />
</div>
<div class="info">
<div class="info-top flex items-center justify-between">
<p class="nickname truncate">{{ item.nickname }}</p>
</div>
</div>
</div>
</div>
</template>
</GcList>
</GcColumn>
</div>
</template>

<style scoped>
.contact-list {
width: 300px;
height: 100vh;
border-right: 1px solid #e0e4ea;
}
.contact-list .gc-column ::v-deep .gc-column__header {
display: flex;
align-items: center;
justify-content: space-between;
height: 70px;
padding: 15px;
border-bottom: 1px solid #e0e4ea;
}
.contact-list .gc-column .gc-column__header .oprate .el-button + .el-button {
margin-left: 5px;
}
.contact-list .gc-column .list-title {
font-size: 14px;
color: #96a1b1;
line-height: 45px;
padding-left: 15px;
margin-bottom: 5px;
}
.contact-list .gc-column ::v-deep .el-scrollbar .gc-list {
padding-bottom: 30px;
}
.contact-list .gc-column ::v-deep .el-scrollbar .gc-list .gc-list__item .contact-item .user .avater {
flex-shrink: 0;
width: 45px;
height: 45px;
margin-right: 15px;
}
.contact-list .gc-column ::v-deep .el-scrollbar .gc-list .gc-list__item .contact-item .user .avater img {
border-radius: 50%;
}
.contact-list .gc-column ::v-deep .el-scrollbar .gc-list .gc-list__item .contact-item .user .info {
flex: 1;
}
.contact-list .gc-column ::v-deep .el-scrollbar .gc-list .gc-list__item .contact-item .user .info-top .nickname {
flex: 1;
width: 0;
font-size: 15px;
font-weight: 600;
}
</style>
7 changes: 7 additions & 0 deletions src/view/contacts/components/set-contect-info-dialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts"></script>

<template>
<div></div>
</template>

<style scoped></style>
69 changes: 67 additions & 2 deletions src/view/contacts/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,72 @@
<script lang="ts"></script>
<script setup lang="ts">
import { useAsyncComponent } from '@/hooks'
import { ref, type Component } from 'vue'
import ContactDetail from './components/contact-detail.vue'
import ContactList from './components/contact-list.vue'
// 类型定义
interface Contact {
id: string
[key: string]: any
}
// 好友选择逻辑
const activeContact = ref<Contact | null>(null)
const handleContactChange = (contactId: string, contact: Contact) => {
activeContact.value = contact
}
// 异步组件类型扩展
interface DialogExposedMethods {
open: () => void
}
// 添加好友弹窗逻辑
let addContactDialogTrigger: (() => void) | null = null
const { AsyncComponent: AddContactDialogAsyncComp } = useAsyncComponent<Component>({
component: () => import('./components/add-contact-dialog.vue'),
wait: () =>
new Promise<void>((resolve) => {
addContactDialogTrigger = resolve
}),
})
const addContactDialogRef = ref<DialogExposedMethods | null>(null)
const handleShowAddContactDialog = async () => {
addContactDialogTrigger?.()
addContactDialogRef.value?.open()
}
// 设置资料弹窗逻辑
let setContactInfoDialogTrigger: (() => void) | null = null
const { AsyncComponent: SetContactInfoDialogAsyncComp } = useAsyncComponent<Component>({
component: () => import('./components/set-contect-info-dialog.vue'),
wait: () =>
new Promise<void>((resolve) => {
setContactInfoDialogTrigger = resolve
}),
})
const setContactInfoDialogRef = ref<DialogExposedMethods | null>(null)
const handleShowSetContactInfoDialog = () => {
setContactInfoDialogTrigger?.()
setContactInfoDialogRef.value?.open()
}
</script>

<template>
<div></div>
<div class="contacts flex">
<!-- 好友列表 -->
<ContactList @change="handleContactChange" @add-contact="handleShowAddContactDialog"></ContactList>
<!-- 好友详情 -->
<ContactDetail
v-if="activeContact"
:contact="activeContact"
@set-contact-info="handleShowSetContactInfoDialog"
></ContactDetail>
<!-- 添加好友弹窗 -->
<component :is="AddContactDialogAsyncComp" ref="addContactDialogRef"></component>
<!-- 设置好友资料弹窗 -->
<component :is="SetContactInfoDialogAsyncComp" ref="setContactInfoDialogRef"></component>
</div>
</template>

<style scoped></style>

0 comments on commit e3ec547

Please sign in to comment.