initialize project with Vue 3, Vite, and Element Plus; set up basic routing, components, configuration, and project structure
This commit is contained in:
165
src/components/NewChatButton.vue
Normal file
165
src/components/NewChatButton.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user