104 lines
3.4 KiB
Vue
104 lines
3.4 KiB
Vue
<template>
|
||
<aside class="conversation-list">
|
||
<header class="list-header">
|
||
<div>
|
||
<h2>联系人</h2>
|
||
<p>{{ contacts.contacts.length }} 位好友</p>
|
||
</div>
|
||
<div class="header-actions">
|
||
<button class="icon-button" title="添加联系人" @click="openAddContact">+</button>
|
||
<button class="icon-button" title="刷新" @click="contacts.refreshContacts({ force: true })">↻</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="search-box">
|
||
<input v-model.trim="keyword" placeholder="搜索联系人">
|
||
</div>
|
||
|
||
<div class="sidebar-scroll">
|
||
<button
|
||
v-for="item in filteredContacts"
|
||
:key="item.id"
|
||
class="conversation-item"
|
||
:class="{ active: item.id === contacts.activeId }"
|
||
@click="contacts.setActiveContact(item.id)"
|
||
>
|
||
<img v-if="item.portrait" class="conversation-avatar image-avatar" :src="item.portrait" alt="">
|
||
<span v-else class="conversation-avatar">{{ item.avatarText }}</span>
|
||
<span class="conversation-copy">
|
||
<strong>{{ item.title }}</strong>
|
||
<small>{{ item.subtitle }}</small>
|
||
</span>
|
||
</button>
|
||
|
||
<div v-if="!filteredContacts.length" class="empty-list">
|
||
<strong>{{ contacts.loading ? '正在加载' : '暂无联系人' }}</strong>
|
||
<span>{{ contacts.loading ? '请稍候' : '好友同步完成后会显示在这里' }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="addingContact" class="modal-backdrop" @click.self="closeAddContact">
|
||
<form class="modal-panel" @submit.prevent="submitAddContact">
|
||
<header>
|
||
<strong>添加联系人</strong>
|
||
<button type="button" class="icon-button" title="关闭" @click="closeAddContact">×</button>
|
||
</header>
|
||
<label>
|
||
<span>用户ID / 手机号 / 用户名</span>
|
||
<input v-model.trim="addKeyword" autocomplete="off" placeholder="请输入要添加的联系人">
|
||
</label>
|
||
<p v-if="contacts.actionError" class="form-error">{{ contacts.actionError }}</p>
|
||
<p v-if="contacts.actionInfo" class="form-info">{{ contacts.actionInfo }}</p>
|
||
<div class="modal-actions">
|
||
<button type="button" class="ghost-inline-action" @click="closeAddContact">取消</button>
|
||
<button class="primary-inline-action" :disabled="contacts.loading || !addKeyword">
|
||
{{ contacts.loading ? '添加中...' : '添加' }}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</aside>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue';
|
||
import { useContactStore } from '@/stores/contacts';
|
||
|
||
const contacts = useContactStore();
|
||
const keyword = ref('');
|
||
const addingContact = ref(false);
|
||
const addKeyword = ref('');
|
||
|
||
const filteredContacts = computed(() => {
|
||
const value = keyword.value.toLowerCase();
|
||
if (!value) {
|
||
return contacts.contacts;
|
||
}
|
||
return contacts.contacts.filter(item => {
|
||
return item.title.toLowerCase().includes(value)
|
||
|| item.subtitle.toLowerCase().includes(value)
|
||
|| item.uid.toLowerCase().includes(value);
|
||
});
|
||
});
|
||
|
||
function openAddContact() {
|
||
contacts.actionError = '';
|
||
contacts.actionInfo = '';
|
||
addKeyword.value = '';
|
||
addingContact.value = true;
|
||
}
|
||
|
||
function closeAddContact() {
|
||
addingContact.value = false;
|
||
}
|
||
|
||
async function submitAddContact() {
|
||
const added = await contacts.addContact(addKeyword.value);
|
||
if (added) {
|
||
window.setTimeout(() => {
|
||
addingContact.value = false;
|
||
}, 500);
|
||
}
|
||
}
|
||
</script>
|