Initial Web IM deployment
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user