add AIAgent

This commit is contained in:
Timi
2026-09-16 01:39:44 +08:00
parent c29436f845
commit a6f9fabcec
11 changed files with 1266 additions and 5 deletions
+124
View File
@@ -0,0 +1,124 @@
<template>
<section class="ai-chat-panel" aria-label="门店经营助手">
<ai-chat-message-list
ref="messageList"
:messages="messages"
:is-generating="isGenerating"
:quick-question-list="aiQuickQuestionList"
@select-question="selectQuestion"
/>
<div class="bottom-area">
<div v-if="activeToolStatus" class="tool-status">
<t-icon name="loading" size="1rem" />
<span v-text="activeToolStatus" />
</div>
<ai-chat-composer
v-model="draft"
:is-generating="isGenerating"
:last-error="lastError"
:can-retry="canRetry"
@send="submitMessage"
@stop="stopGeneration"
@retry="retryLastMessage"
/>
</div>
</section>
</template>
<script setup lang="ts">
import { useAiChat } from "@/composables/useAiChat";
import AiChatComposer from "@/pages/ai/components/AiChatComposer.vue";
import AiChatMessageList from "@/pages/ai/components/AiChatMessageList.vue";
import { aiQuickQuestionList } from "@/pages/ai/aiChatQuestions";
defineOptions({
name: "AiChatPanel"
});
interface Props {
storeName: string;
sessionKey: string;
storeId?: string;
}
const props = defineProps<Props>();
type MessageListExpose = {
scrollToBottom: () => Promise<void>;
};
const messageListRef = useTemplateRef<MessageListExpose>("messageList");
const draft = shallowRef("");
const { messages, isGenerating, activeToolStatus, lastError, canRetry, sendMessage, stopGeneration, retryLastMessage } = useAiChat({
sessionKey: () => props.sessionKey,
storeId: () => props.storeId,
scrollToBottom
});
async function submitMessage(message: string): Promise<void> {
await sendMessage(message);
}
function selectQuestion(question: string): void {
draft.value = question;
}
async function scrollToBottom(): Promise<void> {
await messageListRef.value?.scrollToBottom();
}
</script>
<style scoped lang="less">
.ai-chat-panel {
height: 100%;
display: flex;
position: relative;
overflow: hidden;
min-height: 0;
background: var(--ai-background);
padding-top: var(--app-nav-height);
flex-direction: column;
.bottom-area {
inset: auto 1rem calc(1rem + env(safe-area-inset-bottom, 0px));
width: auto;
margin: 0 auto;
z-index: 2;
position: absolute;
max-width: 40rem;
.tool-status {
gap: .375rem;
margin-bottom: .5rem;
color: #0f766e;
padding: .5rem .75rem;
border-radius: .75rem;
display: flex;
font-size: .75rem;
background: var(--ai-soft);
align-items: center;
.t-icon {
animation: rotate 1s linear infinite;
}
}
}
}
@keyframes rotate {
to {
transform: rotate(360deg);
}
}
:global(:root) {
--ai-background: rgba(255, 255, 255, .98);
--ai-bubble: #f5f6f8;
--ai-input: white;
--ai-soft: #f0fdfa;
}
:global(html.theme-dark) {
--ai-background: rgba(15, 23, 42, .98);
--ai-bubble: #1e293b;
--ai-input: #111827;
--ai-soft: rgba(20, 184, 166, .12);
}
</style>