Files
pit-router/frontend/src/components/chat/BotSelector.vue
feifei.xu e651f21324 feat: Step 6 - 前端聊天界面 (v0.9.5)
- 创建 Vue.js 3 前端项目 (frontend/)
- 实现核心功能:
  - 登录页面 (LoginView)
  - 首页 - 机器人和会话列表 (HomeView)
  - 聊天页面 (ChatView)
  - 聊天侧边栏 (ChatSidebar)
  - 聊天窗口 (ChatWindow)
  - 机器人选择器 (BotSelector)
- 集成功能:
  - Socket.io WebSocket 连接
  - Pinia 状态管理
  - Axios API 客户端
  - JWT 认证
- 更新版本号到 0.9.5
2026-03-15 10:57:50 +08:00

168 lines
3.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { Bot } from '@/stores/chat'
defineProps<{
bots: Bot[]
}>()
const emit = defineEmits<{
select: [botId: string]
close: []
}>()
</script>
<template>
<div class="modal-overlay" @click.self="emit('close')">
<div class="modal">
<div class="modal-header">
<h3>选择机器人</h3>
<button class="close-btn" @click="emit('close')">×</button>
</div>
<div class="bot-list">
<div
v-for="bot in bots"
:key="bot.id"
class="bot-item"
@click="emit('select', bot.id)"
>
<div class="bot-avatar">
{{ bot.avatar || '🤖' }}
</div>
<div class="bot-info">
<h4>{{ bot.display_name || bot.name }}</h4>
<p v-if="bot.description">{{ bot.description }}</p>
<span class="status" :class="bot.status">
{{ bot.status === 'online' ? '🟢 在线' : '⚪ 离线' }}
</span>
</div>
</div>
<div class="empty" v-if="bots.length === 0">
<p>暂无可用机器人</p>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
}
.modal {
background: var(--bg-primary);
border-radius: var(--border-radius-lg);
width: 100%;
max-width: 480px;
max-height: 80vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modal-header {
padding: var(--spacing-md);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
font-size: var(--font-size-md);
font-weight: 600;
}
.close-btn {
width: 32px;
height: 32px;
border: none;
background: transparent;
font-size: 24px;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.close-btn:hover {
background: var(--bg-secondary);
}
.bot-list {
flex: 1;
overflow-y: auto;
padding: var(--spacing-md);
}
.bot-item {
display: flex;
gap: var(--spacing-md);
padding: var(--spacing-md);
border-radius: var(--border-radius);
cursor: pointer;
transition: background 0.2s;
}
.bot-item:hover {
background: var(--bg-secondary);
}
.bot-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background: var(--bg-tertiary);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
}
.bot-info {
flex: 1;
min-width: 0;
}
.bot-info h4 {
font-size: var(--font-size-md);
font-weight: 600;
margin-bottom: var(--spacing-xs);
}
.bot-info p {
font-size: var(--font-size-sm);
color: var(--text-secondary);
margin-bottom: var(--spacing-xs);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bot-info .status {
font-size: var(--font-size-xs);
color: var(--text-tertiary);
}
.bot-info .status.online {
color: var(--success-color);
}
.empty {
text-align: center;
padding: var(--spacing-xl);
color: var(--text-tertiary);
}
</style>