166 lines
3.6 KiB
Vue
166 lines
3.6 KiB
Vue
<script setup>
|
||
import { ref } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import axios from 'axios'
|
||
import { ElMessage } from 'element-plus'
|
||
|
||
const props = defineProps({
|
||
inline: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
})
|
||
|
||
const emit = defineEmits(['created'])
|
||
const router = useRouter()
|
||
const apiBase = import.meta.env.VITE_API_BASE || 'http://localhost:8000/api'
|
||
|
||
const creating = ref(false)
|
||
const sessionName = ref('')
|
||
const lastSessionId = ref(localStorage.getItem('ars-last-session') || '')
|
||
const popoverVisible = ref(false)
|
||
|
||
const togglePopover = () => {
|
||
popoverVisible.value = !popoverVisible.value
|
||
}
|
||
|
||
const createSession = async () => {
|
||
const token = localStorage.getItem('ars-token') || ''
|
||
if (!token) {
|
||
router.replace({ name: 'login' })
|
||
return
|
||
}
|
||
creating.value = true
|
||
const payload = {
|
||
session_name: sessionName.value.trim() || '新会话',
|
||
}
|
||
try {
|
||
const { data } = await axios.post(`${apiBase}/sessions`, payload, {
|
||
headers: { Authorization: `Bearer ${token}` },
|
||
})
|
||
lastSessionId.value = data.session_id
|
||
localStorage.setItem('ars-last-session', data.session_id)
|
||
ElMessage.success('新会话已创建')
|
||
sessionName.value = ''
|
||
popoverVisible.value = false
|
||
emit('created', data)
|
||
} catch (err) {
|
||
const message = err.response?.data?.message || '创建会话失败'
|
||
ElMessage.error(message)
|
||
if (err.response?.status === 401) {
|
||
router.replace({ name: 'login' })
|
||
}
|
||
} finally {
|
||
creating.value = false
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<el-popover
|
||
v-model:visible="popoverVisible"
|
||
trigger="manual"
|
||
placement="bottom-start"
|
||
popper-class="mini-popper"
|
||
width="260"
|
||
:teleported="false"
|
||
>
|
||
<template #reference>
|
||
<button
|
||
class="new-chat-btn"
|
||
:class="{ inline: props.inline }"
|
||
aria-label="新建会话"
|
||
data-testid="new-chat-trigger"
|
||
@click.stop="togglePopover"
|
||
>
|
||
<span class="plus">+</span>
|
||
<span class="label">新建聊天</span>
|
||
</button>
|
||
</template>
|
||
<div class="new-chat-card">
|
||
<p class="title">新建 Chat Session</p>
|
||
<el-input
|
||
v-model="sessionName"
|
||
size="large"
|
||
placeholder="可选:会话名称"
|
||
data-testid="session-name"
|
||
/>
|
||
<el-button
|
||
type="primary"
|
||
class="create-btn"
|
||
color="#1d4ed8"
|
||
:loading="creating"
|
||
data-testid="create-session"
|
||
@click="createSession"
|
||
>
|
||
创建
|
||
</el-button>
|
||
<p v-if="lastSessionId" class="last">
|
||
最近会话:<code>{{ lastSessionId }}</code>
|
||
</p>
|
||
</div>
|
||
</el-popover>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.new-chat-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 12px;
|
||
border-radius: 12px;
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
background: rgba(255, 255, 255, 0.88);
|
||
box-shadow: 0 8px 20px rgba(17, 24, 39, 0.12);
|
||
cursor: pointer;
|
||
transition: transform 0.18s ease, box-shadow 0.18s ease;
|
||
color: #111827;
|
||
}
|
||
|
||
.new-chat-btn.inline {
|
||
width: 100%;
|
||
justify-content: center;
|
||
box-shadow: none;
|
||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.new-chat-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 12px 28px rgba(17, 24, 39, 0.15);
|
||
}
|
||
|
||
.plus {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.label {
|
||
font-weight: 700;
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
.new-chat-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.title {
|
||
margin: 0;
|
||
font-weight: 700;
|
||
color: #0f172a;
|
||
}
|
||
|
||
.create-btn {
|
||
width: 100%;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.last {
|
||
margin: 0;
|
||
font-size: 12px;
|
||
color: #4b5563;
|
||
word-break: break-all;
|
||
}
|
||
</style>
|