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 @@
+
\ 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 @@
+
+
+
+
+ (selectedAIKey = item.uid)"
+ :class="{ active: selectedAIKey === item.uid }"
+ @click="handleAI(item)"
+ align="center"
+ class="AI-item">
+
+
+
+ {{ item.name }}
+
+
+
+
+ Beta
+
+ 128k
+
+
+
+
+
+
+
@@ -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 @@