Initial Web IM deployment

This commit is contained in:
winchen
2026-06-30 10:13:48 +07:00
commit 1cbc3c4cf0
67 changed files with 55851 additions and 0 deletions
+103
View File
@@ -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>