diff --git a/public/AI/QW.png b/public/AI/QW.png new file mode 100644 index 00000000..2a629076 Binary files /dev/null and b/public/AI/QW.png differ diff --git a/public/AI/deepseek.png b/public/AI/deepseek.png new file mode 100644 index 00000000..6cc5227b Binary files /dev/null and b/public/AI/deepseek.png differ diff --git a/public/AI/openai.svg b/public/AI/openai.svg new file mode 100644 index 00000000..1d9f6065 --- /dev/null +++ b/public/AI/openai.svg @@ -0,0 +1 @@ +OpenAI \ No newline at end of file diff --git a/public/icon.js b/public/icon.js index d973347d..eb4848ef 100644 --- a/public/icon.js +++ b/public/icon.js @@ -1 +1 @@ -!function(e){var t,n,d,o,i,a,r='';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window); \ No newline at end of file +!function(e){var t,n,d,o,i,a,r='';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window); \ No newline at end of file diff --git a/src/components/rightBox/MsgInput.vue b/src/components/rightBox/MsgInput.vue index f8658f26..7e73f7c9 100644 --- a/src/components/rightBox/MsgInput.vue +++ b/src/components/rightBox/MsgInput.vue @@ -19,16 +19,16 @@ v-if="isEntering" @click.stop="messageInputDom.focus()" class="absolute select-none top-8px left-6px w-fit text-(12px #777)"> - 聊点什么吧... + 输入 / 唤起 AI 助手 -
+
{{ item.name }} @@ -57,6 +57,41 @@
+ +
+ + + +
+ @@ -139,8 +174,10 @@ const arrow = ref(false) /** 输入框dom元素 */ const messageInputDom = ref() const activeItem = ref(inject('activeItem') as MockItem) -/** 虚拟列表 */ -const virtualListInst = ref() +/** ait 虚拟列表 */ +const virtualListInstAit = useTemplateRef('virtualListInst-ait') +/** AI 虚拟列表 */ +const virtualListInstAI = useTemplateRef('virtualListInst-AI') /** 是否处于输入状态 */ const isEntering = computed(() => { return msgInput.value === '' @@ -161,15 +198,19 @@ const recordSelectionRange = () => (lastEditRange = getEditorRange()) const { inputKeyDown, handleAit, + handleAI, handleInput, send, personList, disabledSend, ait, + aiDialogVisible, + selectedAIKey, msgInput, chatKey, menuList, - selectedAitKey + selectedAitKey, + groupedAIModels } = useMsgInput(messageInputDom) /** 当切换聊天对象时,重新获取焦点 */ @@ -184,19 +225,34 @@ watch(activeItem, () => { watch(personList, (newList) => { if (newList.length > 0) { /** 先设置滚动条滚动到第一个 */ - virtualListInst.value?.scrollTo({ key: newList[0].uid }) + virtualListInstAit.value?.scrollTo({ key: newList[0].uid }) selectedAitKey.value = newList[0].uid } }) +/** 当AI列表发生变化的时候始终select第一个 */ +watch(groupedAIModels, (newList) => { + if (newList.length > 0) { + /** 先设置滚动条滚动到第一个 */ + virtualListInstAI.value?.scrollTo({ key: newList[0].uid }) + selectedAIKey.value = newList[0].uid + } +}) + /** 处理键盘上下键切换提及项 */ -const handleAitKeyChange = (direction: 1 | -1) => { - const currentIndex = personList.value.findIndex((item) => item.uid === selectedAitKey.value) - const newIndex = Math.max(0, Math.min(currentIndex + direction, personList.value.length - 1)) - selectedAitKey.value = personList.value[newIndex].uid +const handleAitKeyChange = ( + direction: 1 | -1, + list: Ref, + virtualListInst: VirtualListInst, + key: Ref +) => { + const currentIndex = list.value.findIndex((item) => item.uid === key.value) + const newIndex = Math.max(0, Math.min(currentIndex + direction, list.value.length - 1)) + key.value = list.value[newIndex].uid // 获取新选中项在列表中的索引,并滚动到该位置(使用key来进行定位) - virtualListInst.value?.scrollTo({ index: newIndex }) + virtualListInst?.scrollTo({ index: newIndex }) } + const closeMenu = (event: any) => { /** 需要判断点击如果不是.context-menu类的元素的时候,menu才会关闭 */ if (!event.target.matches('#message-input, #message-input *')) { @@ -205,20 +261,30 @@ const closeMenu = (event: any) => { } onMounted(async () => { - onKeyStroke('Enter', (e) => { + onKeyStroke('Enter', () => { if (ait.value && selectedAitKey.value > -1) { - e.preventDefault() const item = personList.value.find((item) => item.uid === selectedAitKey.value) as CacheUserItem handleAit(item) + } else if (aiDialogVisible.value && Number(selectedAIKey.value) > -1) { + const item = groupedAIModels.value.find((item) => item.uid === selectedAIKey.value) + handleAI(item) } }) onKeyStroke('ArrowUp', (e) => { e.preventDefault() - handleAitKeyChange(-1) + if (ait.value) { + handleAitKeyChange(-1, personList, virtualListInstAit.value!, selectedAitKey) + } else if (aiDialogVisible.value) { + handleAitKeyChange(-1, groupedAIModels, virtualListInstAI.value!, selectedAIKey) + } }) onKeyStroke('ArrowDown', (e) => { e.preventDefault() - handleAitKeyChange(1) + if (ait.value) { + handleAitKeyChange(1, personList, virtualListInstAit.value!, selectedAitKey) + } else if (aiDialogVisible.value) { + handleAitKeyChange(1, groupedAIModels, virtualListInstAI.value!, selectedAIKey) + } }) // TODO: 暂时已经关闭了独立窗口聊天功能 emit('aloneWin') diff --git a/src/components/rightBox/chatBox/ChatHeader.vue b/src/components/rightBox/chatBox/ChatHeader.vue index 6c7473a1..fb246033 100644 --- a/src/components/rightBox/chatBox/ChatHeader.vue +++ b/src/components/rightBox/chatBox/ChatHeader.vue @@ -14,10 +14,10 @@ class="size-20px color-#13987f select-none outline-none"> - - + +

- {{ activeItem.activeStatus === OnlineEnum.ONLINE ? '在线' : '离线' }} + {{ isOnline ? '在线' : '离线' }}

@@ -190,7 +190,7 @@