diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 0c8572c6a76..43ed27c5c2b 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -502,7 +502,7 @@ Workspace 智能体分页方法只接收分页与查询条件,从当前资源 ### 对话接口 Admin 调试接口在 `admin/workspace/conversation.ts`,正式 Chat 接口在 `chat/conversation.ts`, -使用各自请求客户端。面板 `common/get-api.ts` 仅按 ChatType 选择完整 API,不拼 URL。 +使用各自请求客户端。对话面板由各视图(`view/debug`、`view/chat`、`view/portal`)选择对应 API,不拼 URL。 Chat 协议及认证接口见 [chat/README.md](chat/README.md)。 历史消息和流式聚合复用 `@/api/types` 的 `ChatContent`,定义在 `types/chat-log.ts`; diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 9b1abfd58d0..56481f9c3b4 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -975,7 +975,9 @@ Workspace API 内部使用 `getWorkspaceId()`,System 共享 API 不接收工 正式对话的 `message-list` 根据会话 Store 的 `isShared` 展示分享选择态,同轮提问与回答同步选择, 内容只读且选中时显示背景;无记录 ID 的问答不可选。列表底部提供当前已加载轮次的全选、已选数量、 取消及创建链接,创建时按消息顺序提交记录 ID;成功退出分享,失败保留选择。分享期间主面板隐藏输入框, -消息列表卸载时退出分享态。分享入口仅在 `SHAREABLE_VIEWS`(Chat、Portal)显示,调试、日志等视图不提供分享服务。 +消息列表卸载时退出分享态。分享入口按 `view/index.ts` 的 `useShowShare()` 显示:Chat、Portal 视图按智能体的 +`show_share` provide `SHOW_SHARE_KEY`(门户还要求已选定智能体),调试、日志等视图不提供,即不显示。 +门户侧栏与历史对话抽屉的分享入口跨多个智能体,暂不受 `show_share` 控制。 分享选择放在 `message-list/share/`:`index.ts` 的 `createShareLinkStore({ messages, share, setShared })` 维护 每条消息的记录 ID、已选记录、数量与全选状态、单条切换、全选、进入(`enterShare`)、提交(`copyShareLink`) 及退出(`exitShare`);提交成功后退出,失败保留选择。分享模式开关仍由会话 Store 的 `isShared` 维护, diff --git a/ui/src/conversation-panel/components/content/items/question/index.vue b/ui/src/conversation-panel/components/content/items/question/index.vue index 8448fb0b629..a0fced537d5 100644 --- a/ui/src/conversation-panel/components/content/items/question/index.vue +++ b/ui/src/conversation-panel/components/content/items/question/index.vue @@ -13,13 +13,13 @@ import { copyText } from '@/utils/clipboard' import { MESSAGE_LIST_KEY } from '../../../message-list/index' import { CONVERSATION_LIST_KEY } from '../../../../left-sidebar/conversation-list/index' -import { useViewType } from '../../../../view/index' -import { SHAREABLE_VIEWS, useShareLinkStoreOptional, useShareRecordId } from '../../../message-list/share/index' +import { useShowShare } from '../../../../view/index' +import { useShareLinkStoreOptional, useShareRecordId } from '../../../message-list/share/index' defineOptions({ name: 'ConversationQuestionContent' }) -// 仅提供分享服务的视图(对话、门户)显示分享入口;进入分享时预先勾选所属轮次。 -const showShare = SHAREABLE_VIEWS.includes(useViewType()) +// 智能体允许分享时显示分享入口;进入分享时预先勾选所属轮次。 +const showShare = useShowShare() const shareLink = useShareLinkStoreOptional() const shareRecordId = useShareRecordId() diff --git a/ui/src/conversation-panel/components/header/chat/index.vue b/ui/src/conversation-panel/components/header/chat/index.vue index 1604d1b1c50..315b15c738b 100644 --- a/ui/src/conversation-panel/components/header/chat/index.vue +++ b/ui/src/conversation-panel/components/header/chat/index.vue @@ -3,12 +3,11 @@ import { computed } from 'vue' import { useConversationListStore } from '../../../left-sidebar/conversation-list/index' import { useMessageListStore } from '../../message-list/index' import { useShareLinkStoreOptional } from '../../message-list/share/index' +import { useShowShare } from '../../../view/index' const props = defineProps<{ /** 新建对话;不传时在当前应用中新建(门户在没有当前智能体时改为选择智能体) */ newConversation?: () => void - /** 显示分享入口;仅对话端传入 */ - shareable?: boolean }>() const list = useConversationListStore() @@ -17,7 +16,7 @@ const msgs = useMessageListStore() const shareLink = useShareLinkStoreOptional() // 回答生成中或没有消息时不可分享 const canShare = computed(() => msgs.messages.value.length > 0 && !msgs.streamLoading.value) - +const showShare = useShowShare() const handleNewConversation = () => (props.newConversation ? props.newConversation() : list.newConversation()) @@ -44,12 +43,11 @@ const handleNewConversation = () => (props.newConversation ? props.newConversati - + - diff --git a/ui/src/conversation-panel/components/header/portal/index.vue b/ui/src/conversation-panel/components/header/portal/index.vue index 9663054669b..316394ba67b 100644 --- a/ui/src/conversation-panel/components/header/portal/index.vue +++ b/ui/src/conversation-panel/components/header/portal/index.vue @@ -2,12 +2,11 @@ import { computed } from 'vue' import { useConversationListStore } from '../../../left-sidebar/conversation-list/index' import { useMessageListStore } from '../../message-list/index' +import { useShowShare } from '../../../view/index' const props = defineProps<{ /** 新建对话;不传时在当前应用中新建(门户在没有当前智能体时改为选择智能体) */ newConversation?: () => void - /** 显示分享入口;仅对话端传入 */ - shareable?: boolean }>() const emit = defineEmits<{ share: [] }>() @@ -16,6 +15,7 @@ const list = useConversationListStore() const msgs = useMessageListStore() // 回答生成中或没有消息时不可分享 const canShare = computed(() => msgs.messages.value.length > 0 && !msgs.streamLoading.value && !msgs.loading.value) +const showShare = useShowShare() const handleNewConversation = () => (props.newConversation ? props.newConversation() : list.newConversation()) @@ -48,7 +48,7 @@ const handleNewConversation = () => (props.newConversation ? props.newConversati - + diff --git a/ui/src/conversation-panel/components/message-footer/index.vue b/ui/src/conversation-panel/components/message-footer/index.vue index 3f6312d45fc..729bc57e16d 100644 --- a/ui/src/conversation-panel/components/message-footer/index.vue +++ b/ui/src/conversation-panel/components/message-footer/index.vue @@ -10,8 +10,8 @@ import type { ChatMessage, ChatRecordMeta } from '../../core/types' import { useExecutionDetailStore } from '../../right-sidebar/execution-detail/index' import { MESSAGE_LIST_KEY } from '../message-list/index' -import { SHAREABLE_VIEWS, useShareLinkStoreOptional } from '../message-list/share/index' -import { useViewType } from '../../view/index' +import { useShareLinkStoreOptional } from '../message-list/share/index' +import { useShowShare } from '../../view/index' const props = defineProps<{ message: ChatMessage }>() const emit = defineEmits<{ resize: [] }>() @@ -25,8 +25,8 @@ onMounted(() => { }) onBeforeUnmount(() => footerObserver?.disconnect()) -// 仅提供分享服务的视图(对话、门户)显示分享入口;进入分享时预先勾选当前回答所属轮次。 -const showShare = SHAREABLE_VIEWS.includes(useViewType()) +// 智能体允许分享时显示分享入口;进入分享时预先勾选当前回答所属轮次。 +const showShare = useShowShare() const shareLink = useShareLinkStoreOptional() // 回答生成中禁用分享。 const messageList = inject(MESSAGE_LIST_KEY, null) diff --git a/ui/src/conversation-panel/components/message-list/share/index.ts b/ui/src/conversation-panel/components/message-list/share/index.ts index 03d0aa4751b..8eb9378f9ed 100644 --- a/ui/src/conversation-panel/components/message-list/share/index.ts +++ b/ui/src/conversation-panel/components/message-list/share/index.ts @@ -1,9 +1,5 @@ import { computed, inject, ref, type InjectionKey, type Ref } from 'vue' import type { ChatMessage } from '../../../core/types' -import { VIEW_TYPE, type ViewType } from '../../../view/index' - -/** 提供分享服务的视图;其他视图不显示进入分享选择的入口。 */ -export const SHAREABLE_VIEWS: readonly ViewType[] = [VIEW_TYPE.CHAT, VIEW_TYPE.PORTAL] export interface ShareLinkDeps { /** 当前会话消息:只读,由消息列表 Store 维护 */ diff --git a/ui/src/conversation-panel/core/enums.ts b/ui/src/conversation-panel/core/enums.ts deleted file mode 100644 index afdb09fbdbd..00000000000 --- a/ui/src/conversation-panel/core/enums.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** 对话面板运行模式。 */ -export const CHAT_TYPE = { - CHAT: 'CHAT', - DEBUG: 'DEBUG', -} as const diff --git a/ui/src/conversation-panel/core/types.ts b/ui/src/conversation-panel/core/types.ts index 7b567badf2d..c02bce01053 100644 --- a/ui/src/conversation-panel/core/types.ts +++ b/ui/src/conversation-panel/core/types.ts @@ -1,4 +1,3 @@ -import type { CHAT_TYPE } from './enums' import type { ChatFeedback, ChatApplicationProfile, ChatContent, ChatLogRecord, ChatUserProfile, Dict } from '@/api/types' /** @@ -53,8 +52,6 @@ interface StreamContentBatch { /** SSE 支持批量内容分片,也支持直接发送单个内容项。 */ export type StreamChunk = StreamContentBatch | ChatContent -export type ChatType = (typeof CHAT_TYPE)[keyof typeof CHAT_TYPE] - // 通用「发起/续跑一次对话」的参数。message-input、表单节点等任何能发起对话的地方共用。 export interface SendMessageOptions { chatId?: string // 目标会话 id;新建会话时由调用方(message-input)惰性生成后传入,缺省沿用当前已加载会话 diff --git a/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue b/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue index 2b8fc82859b..0dbbc311943 100644 --- a/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue +++ b/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue @@ -10,7 +10,7 @@ import AccountMenu from '../account-menu/index.vue' import { useMessageListStore } from '../../components/message-list/index' import { useShareLinkStoreOptional } from '../../components/message-list/share/index' -import { useViewType, VIEW_TYPE, type ViewType } from '../../view/index' +import { useShowShare, useViewType, VIEW_TYPE, type ViewType } from '../../view/index' defineOptions({ name: 'ConversationList' }) @@ -22,6 +22,7 @@ const SHOW_PROFILE: readonly ViewType[] = [VIEW_TYPE.CHAT, VIEW_TYPE.PORTAL] const msgs = useMessageListStore() // 会话菜单分享默认全选,内容分享由内容入口指定记录。 const shareLink = useShareLinkStoreOptional() +const showShare = useShowShare() const canShare = computed(() => msgs.messages.value.length > 0 && !msgs.streamLoading.value && !msgs.loading.value) const { appInfo, conversations, currentChatId, selectConversation, newConversation, deleteChat, toggleLeftSide, hasMore, loadMore } = @@ -123,7 +124,7 @@ function handleClearConversations() { diff --git a/ui/src/conversation-panel/main/chat-panel/index.vue b/ui/src/conversation-panel/main/chat-panel/index.vue index 8eed9dadb8a..faf4d3d6603 100644 --- a/ui/src/conversation-panel/main/chat-panel/index.vue +++ b/ui/src/conversation-panel/main/chat-panel/index.vue @@ -6,13 +6,6 @@ import MessageInput from '../../components/message-input/index.vue' import { useConversationListStore } from '../../left-sidebar/conversation-list/index' import { useMessageInputStore } from '../../components/message-input/index' -defineProps<{ - /** 标题栏显示分享入口;仅对话端传入 */ - shareable?: boolean -}>() - -const emit = defineEmits<{ share: [] }>() - const list = useConversationListStore() const input = useMessageInputStore() // 拖拽到面板任意处 → 交给 message-input 接收文件 @@ -25,7 +18,7 @@ const disclaimerText = computed(() => (list.appInfo.value?.disclaimer ? list.app
- +
- +
diff --git a/ui/src/conversation-panel/view/chat/index.vue b/ui/src/conversation-panel/view/chat/index.vue index 262cffa4076..749cdf96ed8 100644 --- a/ui/src/conversation-panel/view/chat/index.vue +++ b/ui/src/conversation-panel/view/chat/index.vue @@ -13,7 +13,7 @@ import { MESSAGE_INPUT_KEY } from '../../components/message-input/index' import { EXECUTION_DETAIL_KEY } from '../../right-sidebar/execution-detail/index' import { SHARE_LINK_KEY } from '../../components/message-list/share/index' import { createChatConversation, createChatUserProfileGetter } from './index' -import { VIEW_KEY, VIEW_TYPE, DEVICE_KEY, CHAT_USER_PROFILE_KEY, createDeviceType } from '../index.ts' +import { VIEW_KEY, VIEW_TYPE, DEVICE_KEY, CHAT_USER_PROFILE_KEY, SHOW_SHARE_KEY, createDeviceType } from '../index.ts' const bundle = createChatConversation() @@ -38,6 +38,11 @@ provide(MESSAGE_INPUT_KEY, bundle.input) provide(EXECUTION_DETAIL_KEY, bundle.detail) provide(SHARE_LINK_KEY, bundle.share) provide(VIEW_KEY, VIEW_TYPE.CHAT) +// 智能体开启「允许分享对话记录」时显示标题栏、会话菜单、提问与回答上的分享入口 +provide( + SHOW_SHARE_KEY, + computed(() => Boolean(bundle.list.appInfo.value?.show_share)), +) // HTML 内容 iframe 内 chatUserProfile() 取当前对话用户 provide(CHAT_USER_PROFILE_KEY, createChatUserProfileGetter()) @@ -83,7 +88,7 @@ watch([() => bundle.list.currentChatId.value, () => bundle.list.composerResetSig @mask-click="closeDrawers" > - + diff --git a/ui/src/conversation-panel/view/index.ts b/ui/src/conversation-panel/view/index.ts index d6b74cdfbbc..028b3f9c213 100644 --- a/ui/src/conversation-panel/view/index.ts +++ b/ui/src/conversation-panel/view/index.ts @@ -49,3 +49,10 @@ export const CHAT_USER_PROFILE_KEY: InjectionKey = Symbol('c export function useChatUserProfileGetter(): GetChatUserProfile { return inject(CHAT_USER_PROFILE_KEY, () => Promise.resolve(null)) } + +/** 是否显示进入分享选择的入口;Chat、Portal 视图按智能体的 show_share 提供,调试、日志等视图不提供即不显示。 */ +export const SHOW_SHARE_KEY: InjectionKey>> = Symbol('conversation-show-share') + +export function useShowShare(): Readonly> { + return inject(SHOW_SHARE_KEY, () => computed(() => false), true) +} diff --git a/ui/src/conversation-panel/view/portal/index.vue b/ui/src/conversation-panel/view/portal/index.vue index c492eb30314..2d261b4cc55 100644 --- a/ui/src/conversation-panel/view/portal/index.vue +++ b/ui/src/conversation-panel/view/portal/index.vue @@ -17,7 +17,7 @@ import { PORTAL_CONVERSATION_LIST_KEY } from '../../left-sidebar/portal-conversa import { SHARE_LINK_KEY } from '../../components/message-list/share/index' import { createPortalConversation } from './index' import { createChatUserProfileGetter } from '../chat/index' -import { VIEW_KEY, VIEW_TYPE, DEVICE_KEY, CHAT_USER_PROFILE_KEY, createDeviceType } from '../index.ts' +import { VIEW_KEY, VIEW_TYPE, DEVICE_KEY, CHAT_USER_PROFILE_KEY, SHOW_SHARE_KEY, createDeviceType } from '../index.ts' const applicationSelectorRef = ref>() const bundle = createPortalConversation({ openApplicationSelector: () => applicationSelectorRef.value?.open() }) @@ -44,6 +44,11 @@ provide(EXECUTION_DETAIL_KEY, bundle.detail) provide(SHARE_LINK_KEY, bundle.share) provide(PORTAL_CONVERSATION_LIST_KEY, bundle.portalList) provide(VIEW_KEY, VIEW_TYPE.PORTAL) +// 当前智能体开启「允许分享对话记录」时显示标题栏、提问与回答上的分享入口 +provide( + SHOW_SHARE_KEY, + computed(() => Boolean(bundle.portalList.currentApplicationId.value && bundle.list.appInfo.value?.show_share)), +) // HTML 内容 iframe 内 chatUserProfile() 取当前对话用户 provide(CHAT_USER_PROFILE_KEY, createChatUserProfileGetter()) // 响应式:窄屏 drawer,宽屏 push