front/src/views/system/aiAssistant/AiChatPanel.vue
zzh 25445bc146 feat: 完善日夜间主题与揽收下单体验
统一暗色模式页面/顶栏/Handsontable 选中色,调整下单到仓方式文案与提示,CustomLeftMenu 卡片统一 60px;补充揽收单、问题件处理与 H5 相关页面。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 11:49:23 +08:00

155 lines
3.7 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.

<template>
<div class="xhs-page-panel ai-chat-panel">
<div class="table-toolbar">
<div class="toolbar-left">
<div>
<p class="xhs-panel-title">AI 助手 · 对话测试</p>
<p class="xhs-panel-desc">选择已配置密钥的提供商,发送消息验证连通性(Enter 发送)。</p>
</div>
</div>
<div class="toolbar-right">
<a-select
v-model="providerKeyProxy"
placeholder="选择 AI 提供商"
style="width: 220px"
allow-clear
>
<a-option
v-for="p in enabledProviders"
:key="p.providerKey"
:value="p.providerKey"
:label="p.displayName || p.providerKey"
/>
</a-select>
<a-button :disabled="loading" @click="emit('clear')">清空</a-button>
<a-button type="primary" :loading="loading" :disabled="!canSend" @click="emit('send')">
发送
</a-button>
</div>
</div>
<div class="panel-body">
<div ref="chatBoxRef" class="chat-box">
<div v-for="(msg, idx) in messages" :key="idx" :class="['chat-msg', msg.role]">
<span class="role">{{ msg.role === 'user' ? '我' : 'AI' }}</span>
<pre class="content">{{ msg.content }}</pre>
</div>
<div v-if="!messages.length" class="empty-chat">选择提供商后开始对话</div>
</div>
<a-textarea
:model-value="inputText"
placeholder="输入问题,Enter 发送"
:auto-size="{ minRows: 3, maxRows: 8 }"
@update:model-value="(v: string) => emit('update:inputText', v)"
@keydown.enter.exact.prevent="emit('send')"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import type { AiProviderItem } from './providerCatalog';
const props = defineProps<{
providers: AiProviderItem[];
providerKey: string;
inputText: string;
messages: Array<{ role: string; content: string }>;
loading?: boolean;
}>();
const emit = defineEmits<{
(e: 'update:providerKey', v: string): void;
(e: 'update:inputText', v: string): void;
(e: 'send'): void;
(e: 'clear'): void;
}>();
const chatBoxRef = ref<HTMLElement>();
const enabledProviders = computed(() =>
props.providers.filter((p) => p.enabled && Boolean(p.apiKey)),
);
const providerKeyProxy = computed({
get: () => props.providerKey,
set: (v: string) => emit('update:providerKey', v),
});
const canSend = computed(
() => Boolean(props.providerKey && props.inputText.trim() && !props.loading),
);
watch(
() => props.messages.length,
async () => {
await nextTick();
if (chatBoxRef.value) {
chatBoxRef.value.scrollTop = chatBoxRef.value.scrollHeight;
}
},
);
</script>
<style scoped lang="less">
@import '@/views/xhs/_shared/xhs-page.less';
.ai-chat-panel {
padding: 16px;
}
.toolbar-left {
flex-direction: column;
align-items: flex-start !important;
gap: 0 !important;
}
.panel-body {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.chat-box {
flex: 1;
min-height: 280px;
overflow-y: auto;
padding: 12px;
background: var(--app-card-bg);
border-radius: 8px;
border: 1px solid var(--color-border-2);
}
.chat-msg {
margin-bottom: 12px;
.role {
font-size: 12px;
color: var(--color-text-3);
}
.content {
margin: 4px 0 0;
white-space: pre-wrap;
word-break: break-word;
font-family: inherit;
font-size: 14px;
color: var(--color-text-2);
}
&.user .content {
color: var(--color-text-1);
}
}
.empty-chat {
padding: 40px 0;
text-align: center;
font-size: 13px;
color: var(--color-text-3);
}
</style>