feat: 优化输入框placeholder动态显示逻辑,支持基于知识库和网络搜索状态的个性化提示
This commit is contained in:
@@ -121,6 +121,26 @@ const { t } = useI18n();
|
||||
const displayedKbs = computed(() => selectedKbs.value.slice(0, 2));
|
||||
const remainingCount = computed(() => Math.max(0, selectedKbs.value.length - 2));
|
||||
|
||||
// 根据不同状态组合计算输入框的 placeholder
|
||||
const inputPlaceholder = computed(() => {
|
||||
const hasKnowledge = allSelectedItems.value.length > 0;
|
||||
const hasWebSearch = isWebSearchEnabled.value && isWebSearchConfigured.value;
|
||||
|
||||
if (hasKnowledge && hasWebSearch) {
|
||||
// 有知识库 + 有网络搜索
|
||||
return t('input.placeholderKbAndWeb');
|
||||
} else if (hasKnowledge) {
|
||||
// 有知识库 + 无网络搜索
|
||||
return t('input.placeholderWithContext');
|
||||
} else if (hasWebSearch) {
|
||||
// 无知识库 + 有网络搜索
|
||||
return t('input.placeholderWebOnly');
|
||||
} else {
|
||||
// 无知识库 + 无网络搜索(纯模型对话)
|
||||
return t('input.placeholder');
|
||||
}
|
||||
});
|
||||
|
||||
// 加载知识库列表
|
||||
const loadKnowledgeBases = async () => {
|
||||
try {
|
||||
@@ -1185,7 +1205,7 @@ onBeforeRouteUpdate((to, from, next) => {
|
||||
<t-textarea
|
||||
ref="textareaRef"
|
||||
v-model="query"
|
||||
:placeholder="allSelectedItems.length > 0 ? $t('input.placeholderWithContext') : $t('input.placeholder')"
|
||||
:placeholder="inputPlaceholder"
|
||||
name="description"
|
||||
:autosize="true"
|
||||
@keydown="onKeydown"
|
||||
|
||||
@@ -748,8 +748,10 @@ export default {
|
||||
},
|
||||
input: {
|
||||
addModel: 'Add Model',
|
||||
placeholder: 'Ask questions based on the knowledge base',
|
||||
placeholder: 'Ask questions directly to the model',
|
||||
placeholderWithContext: 'Enter your question, will answer based on selected knowledge bases/files above',
|
||||
placeholderWebOnly: 'Enter your question, will answer with web search',
|
||||
placeholderKbAndWeb: 'Enter your question, will answer based on knowledge base and web search',
|
||||
agentMode: 'Agent Mode',
|
||||
normalMode: 'Normal Mode',
|
||||
normalModeDesc: 'Knowledge base RAG Q&A',
|
||||
|
||||
@@ -1671,8 +1671,10 @@ export default {
|
||||
},
|
||||
input: {
|
||||
addModel: 'Добавить модель',
|
||||
placeholder: 'Задайте вопрос на основе базы знаний',
|
||||
placeholder: 'Задайте вопрос напрямую модели',
|
||||
placeholderWithContext: 'Введите вопрос, ответ будет основан на выбранных выше базах знаний/файлах',
|
||||
placeholderWebOnly: 'Введите вопрос, ответ будет основан на веб-поиске',
|
||||
placeholderKbAndWeb: 'Введите вопрос, ответ будет основан на базе знаний и веб-поиске',
|
||||
agentMode: 'Agent режим',
|
||||
normalMode: 'Обычный режим',
|
||||
normalModeDesc: 'RAG-вопросы и ответы по базе знаний',
|
||||
|
||||
@@ -1427,8 +1427,10 @@ export default {
|
||||
},
|
||||
input: {
|
||||
addModel: "添加模型",
|
||||
placeholder: "基于知识库提问",
|
||||
placeholder: "直接向模型提问",
|
||||
placeholderWithContext: "输入问题,将基于上方选中的知识库/文件回答",
|
||||
placeholderWebOnly: "输入问题,将结合网络搜索回答",
|
||||
placeholderKbAndWeb: "输入问题,将基于知识库和网络搜索回答",
|
||||
agentMode: "Agent 模式",
|
||||
normalMode: "普通模式",
|
||||
normalModeDesc: "基于知识库的 RAG 问答",
|
||||
|
||||
@@ -32,6 +32,7 @@ export const useMenuStore = defineStore('menuStore', () => {
|
||||
|
||||
const isFirstSession = ref(false)
|
||||
const firstQuery = ref('')
|
||||
const firstMentionedItems = ref<any[]>([])
|
||||
|
||||
const applyMenuTranslations = () => {
|
||||
menuArr.forEach(item => {
|
||||
@@ -90,14 +91,16 @@ export const useMenuStore = defineStore('menuStore', () => {
|
||||
isFirstSession.value = payload
|
||||
}
|
||||
|
||||
const changeFirstQuery = (payload: string) => {
|
||||
const changeFirstQuery = (payload: string, mentionedItems: any[] = []) => {
|
||||
firstQuery.value = payload
|
||||
firstMentionedItems.value = mentionedItems
|
||||
}
|
||||
|
||||
return {
|
||||
menuArr,
|
||||
isFirstSession,
|
||||
firstQuery,
|
||||
firstMentionedItems,
|
||||
clearMenuArr,
|
||||
updatemenuArr,
|
||||
updataMenuChildren,
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
<template>
|
||||
<div class="bot_msg">
|
||||
<div style="display: flex;flex-direction: column; gap:8px">
|
||||
<!-- 显示@的知识库和文件(非 Agent 模式下显示) -->
|
||||
<div v-if="!session.isAgentMode && mentionedItems && mentionedItems.length > 0" class="mentioned_items">
|
||||
<span
|
||||
v-for="item in mentionedItems"
|
||||
:key="item.id"
|
||||
class="mentioned_tag"
|
||||
:class="[
|
||||
item.type === 'kb' ? (item.kb_type === 'faq' ? 'faq-tag' : 'kb-tag') : 'file-tag'
|
||||
]"
|
||||
>
|
||||
<span class="tag_icon">
|
||||
<t-icon v-if="item.type === 'kb'" :name="item.kb_type === 'faq' ? 'chat-bubble-help' : 'folder'" />
|
||||
<t-icon v-else name="file" />
|
||||
</span>
|
||||
<span class="tag_name">{{ item.name }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<docInfo :session="session"></docInfo>
|
||||
<AgentStreamDisplay :session="session" :user-query="userQuery" v-if="session.isAgentMode"></AgentStreamDisplay>
|
||||
<deepThink :deepSession="session" v-if="session.showThink && !session.isAgentMode"></deepThink>
|
||||
@@ -75,6 +92,11 @@ const props = defineProps({
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
mentionedItems: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
isFirstEnter: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
@@ -249,6 +271,75 @@ onBeforeUnmount(() => {
|
||||
<style lang="less" scoped>
|
||||
@import '../../../components/css/markdown.less';
|
||||
|
||||
// @提及的知识库和文件标签样式
|
||||
.mentioned_items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
justify-content: flex-start;
|
||||
max-width: 100%;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.mentioned_tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
max-width: 200px;
|
||||
cursor: default;
|
||||
transition: all 0.15s;
|
||||
background: var(--td-bg-color-secondarycontainer, #f3f3f3);
|
||||
border: 1px solid transparent;
|
||||
color: var(--td-text-color-primary, #333);
|
||||
|
||||
/* KB - Document (Greenish tint) */
|
||||
&.kb-tag {
|
||||
background: rgba(16, 185, 129, 0.08);
|
||||
color: #059669;
|
||||
|
||||
.tag_icon {
|
||||
color: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
/* KB - FAQ (Blueish tint) */
|
||||
&.faq-tag {
|
||||
background: rgba(0, 82, 217, 0.08);
|
||||
color: #0052d9;
|
||||
|
||||
.tag_icon {
|
||||
color: #0052d9;
|
||||
}
|
||||
}
|
||||
|
||||
/* File (Orange tint) */
|
||||
&.file-tag {
|
||||
background: rgba(237, 123, 47, 0.08);
|
||||
color: #e65100;
|
||||
|
||||
.tag_icon {
|
||||
color: #ed7b2f;
|
||||
}
|
||||
}
|
||||
|
||||
.tag_icon {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tag_name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
// 内容包装器 - 与 Agent 模式的 answer 样式一致
|
||||
.content-wrapper {
|
||||
background: #ffffff;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<usermsg :content="session.content" :mentioned_items="session.mentioned_items"></usermsg>
|
||||
</div>
|
||||
<div v-if="session.role == 'assistant'">
|
||||
<botmsg :content="session.content" :session="session" :user-query="getUserQuery(id)" @scroll-bottom="scrollToBottom"
|
||||
<botmsg :content="session.content" :session="session" :user-query="getUserQuery(id)" :mentioned-items="getUserMentionedItems(id)" @scroll-bottom="scrollToBottom"
|
||||
:isFirstEnter="isFirstEnter"></botmsg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,7 +61,7 @@ const useSettingsStoreInstance = useSettingsStore();
|
||||
const uiStore = useUIStore();
|
||||
const { navigateToKnowledgeBaseList } = useKnowledgeBaseCreationNavigation();
|
||||
const { t } = useI18n();
|
||||
const { menuArr, isFirstSession, firstQuery } = storeToRefs(usemenuStore);
|
||||
const { menuArr, isFirstSession, firstQuery, firstMentionedItems } = storeToRefs(usemenuStore);
|
||||
const { output, onChunk, isStreaming, isLoading, error, startStream, stopStream } = useStream();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -93,6 +93,18 @@ const getUserQuery = (index) => {
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// 获取用户消息的 mentioned_items(用于 bot 消息显示)
|
||||
const getUserMentionedItems = (index) => {
|
||||
if (index <= 0) {
|
||||
return [];
|
||||
}
|
||||
const previous = messagesList[index - 1];
|
||||
if (previous && previous.role === 'user') {
|
||||
return previous.mentioned_items || [];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
watch([() => route.params], (newvalue) => {
|
||||
isFirstEnter.value = true;
|
||||
if (newvalue[0].chatid) {
|
||||
@@ -790,7 +802,7 @@ onMounted(async () => {
|
||||
checkmenuTitle(session_id.value)
|
||||
if (firstQuery.value) {
|
||||
scrollLock.value = true;
|
||||
sendMsg(firstQuery.value);
|
||||
sendMsg(firstQuery.value, '', firstMentionedItems.value || []);
|
||||
usemenuStore.changeFirstQuery('');
|
||||
} else {
|
||||
scrollLock.value = false;
|
||||
|
||||
@@ -39,11 +39,11 @@ const uiStore = useUIStore();
|
||||
const { t } = useI18n();
|
||||
const { navigateToKnowledgeBaseList } = useKnowledgeBaseCreationNavigation();
|
||||
|
||||
const sendMsg = (value: string) => {
|
||||
createNewSession(value);
|
||||
const sendMsg = (value: string, modelId: string, mentionedItems: any[]) => {
|
||||
createNewSession(value, mentionedItems);
|
||||
}
|
||||
|
||||
async function createNewSession(value: string) {
|
||||
async function createNewSession(value: string, mentionedItems: any[] = []) {
|
||||
const selectedKbs = settingsStore.settings.selectedKnowledgeBases || [];
|
||||
const selectedFiles = settingsStore.settings.selectedFiles || [];
|
||||
|
||||
@@ -63,7 +63,7 @@ async function createNewSession(value: string) {
|
||||
try {
|
||||
const res = await createSessions(sessionData);
|
||||
if (res.data && res.data.id) {
|
||||
await navigateToSession(res.data.id, value);
|
||||
await navigateToSession(res.data.id, value, mentionedItems);
|
||||
} else {
|
||||
console.error('[createChat] Failed to create session');
|
||||
MessagePlugin.error(t('createChat.messages.createFailed'));
|
||||
@@ -74,7 +74,7 @@ async function createNewSession(value: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const navigateToSession = async (sessionId: string, value: string) => {
|
||||
const navigateToSession = async (sessionId: string, value: string, mentionedItems: any[]) => {
|
||||
const now = new Date().toISOString();
|
||||
let obj = {
|
||||
title: t('createChat.newSessionTitle'),
|
||||
@@ -87,7 +87,7 @@ const navigateToSession = async (sessionId: string, value: string) => {
|
||||
};
|
||||
usemenuStore.updataMenuChildren(obj);
|
||||
usemenuStore.changeIsFirstSession(true);
|
||||
usemenuStore.changeFirstQuery(value);
|
||||
usemenuStore.changeFirstQuery(value, mentionedItems);
|
||||
router.push(`/platform/chat/${sessionId}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user