copilot助手完成
This commit is contained in:
		| @@ -2,12 +2,17 @@ | |||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: "AppCopilot", |   name: "AppCopilot", | ||||||
|  |   props: { | ||||||
|  |     instance: Function, | ||||||
|  |     dict: Object, | ||||||
|  |     permissions: Function | ||||||
|  |   }, | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <template> | <template> | ||||||
|   <section class="AppCopilot"> |   <section class="AppCopilot"> | ||||||
|     <ai-copilot/> |     <ai-copilot :http="instance"/> | ||||||
|   </section> |   </section> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,26 +5,27 @@ import ThinkingBar from "./components/thinkingBar.vue"; | |||||||
| export default { | export default { | ||||||
|   name: "AiCopilot", |   name: "AiCopilot", | ||||||
|   props: { |   props: { | ||||||
|  |     http: Function, | ||||||
|     title: {default: "Copilot小助理"} |     title: {default: "Copilot小助理"} | ||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       show: false, |       show: false, | ||||||
|       expand: false, |       expand: false, | ||||||
|       loading: true, |       loading: false, | ||||||
|       prompt: "", |       prompt: "", | ||||||
|       history: [ |       history: [ | ||||||
|         {avatar: "https://cdn.sinoecare.com/i/2024/06/04/665ec6f5ef213.png", msg: "你好", uid: "ai"}, |         // {avatar: "https://cdn.sinoecare.com/i/2024/06/04/665ec6f5ef213.png", msg: "你好", uid: "ai"}, | ||||||
|         { |         // { | ||||||
|           avatar: "", |         //   avatar: "", | ||||||
|           msg: "AI 聊天机器人 ChatGPT 近日突然出现闪崩,响应超时或无法正常工作,故障长达近 7 小时。全球大量用户处于焦虑等待,因为许多人对此已经产生了依赖,工作不能自理。一些备选工具如 Perplexity、Claude 等也遭遇故障。摩根士丹利的数据显示,ChatGPT 故障后,谷歌 AI 聊天机器人 Gemini 搜索量激增 60%,达 327058 次,显示出用户把它视为 ChatGPT 的直接替代选项\n" + |         //   msg: "AI 聊天机器人 ChatGPT 近日突然出现闪崩,响应超时或无法正常工作,故障长达近 7 小时。全球大量用户处于焦虑等待,因为许多人对此已经产生了依赖,工作不能自理。一些备选工具如 Perplexity、Claude 等也遭遇故障。摩根士丹利的数据显示,ChatGPT 故障后,谷歌 AI 聊天机器人 Gemini 搜索量激增 60%,达 327058 次,显示出用户把它视为 ChatGPT 的直接替代选项\n" + | ||||||
|               "\n" + |         //       "\n" + | ||||||
|               "作者:RTE开发者社区\n" + |         //       "作者:RTE开发者社区\n" + | ||||||
|               "链接:https://juejin.cn/post/7377025870630862874\n" + |         //       "链接:https://juejin.cn/post/7377025870630862874\n" + | ||||||
|               "来源:稀土掘金\n" + |         //       "来源:稀土掘金\n" + | ||||||
|               "著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。", |         //       "著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。", | ||||||
|           uid: "me" |         //   uid: "me" | ||||||
|         }, |         // }, | ||||||
|       ] |       ] | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| @@ -33,9 +34,43 @@ export default { | |||||||
|   }, |   }, | ||||||
|   components: {ThinkingBar, ChatContent}, |   components: {ThinkingBar, ChatContent}, | ||||||
|   methods: { |   methods: { | ||||||
|  |     getHistory(cb) { | ||||||
|  |       this.http.post("/app/appaicopilotinfo/list").then(res => { | ||||||
|  |         if (res?.data) { | ||||||
|  |           if (cb) cb(res.data.records) | ||||||
|  |           else this.history = res.data.records | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     handleHotkey(evt) { | ||||||
|  |       if (evt.ctrlKey && evt.key == "Enter") { | ||||||
|  |         this.handleSend() | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     handleSend() { |     handleSend() { | ||||||
|  |       const concatenateStr = (content, i = 0) => { | ||||||
|  |         this.history.at(-1).content += content.slice(i, i + 1) | ||||||
|  |         if (++i < content.length) setTimeout(() => concatenateStr(content, i), 50) | ||||||
|  |       } | ||||||
|  |       this.$debounce(() => { | ||||||
|  |         const message = {appType: "2", userType: 0, content: this.prompt} | ||||||
|  |         this.history.push(message) | ||||||
|  |         this.loading = true | ||||||
|  |         this.prompt = "" | ||||||
|  |         this.http.post("/app/appaicopilotinfo/add", message).then(res => { | ||||||
|  |           if (res?.data?.length >= 2) { | ||||||
|  |             const last = res.data.at(-1) | ||||||
|  |             this.history.push({...last, content: ""}) | ||||||
|  |             concatenateStr(last.content) | ||||||
|  |           } | ||||||
|  |         }).finally(() => { | ||||||
|  |           this.loading = false | ||||||
|  |         }) | ||||||
|  |       }, 100) | ||||||
|     } |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.getHistory() | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
| @@ -55,7 +90,7 @@ export default { | |||||||
|           <chat-content class="fill" :list="history"/> |           <chat-content class="fill" :list="history"/> | ||||||
|           <div class="sendBox flex gap-14"> |           <div class="sendBox flex gap-14"> | ||||||
|             <el-input type="textarea" class="fill input" autosize resize="none" v-model="prompt" placeholder="请输入..." |             <el-input type="textarea" class="fill input" autosize resize="none" v-model="prompt" placeholder="请输入..." | ||||||
|                       @change="handleSend"/> |                       @keydown.native="handleHotkey" :disabled="loading" :placeholder="loading?'正在思考中...':'请输入'"/> | ||||||
|             <div class="sendBtn" @click="handleSend"/> |             <div class="sendBtn" @click="handleSend"/> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
| @@ -134,7 +169,7 @@ export default { | |||||||
|       .right { |       .right { | ||||||
|         width: 420px; |         width: 420px; | ||||||
|         height: 100%; |         height: 100%; | ||||||
|         padding: 14px; |         padding: 14px 0 14px 14px; | ||||||
|         align-items: stretch; |         align-items: stretch; | ||||||
|         border-left: 1px solid transparent; |         border-left: 1px solid transparent; | ||||||
|  |  | ||||||
| @@ -154,6 +189,7 @@ export default { | |||||||
|  |  | ||||||
|       :deep(.sendBox) { |       :deep(.sendBox) { | ||||||
|         width: 100%; |         width: 100%; | ||||||
|  |         padding-right: 14px; | ||||||
|  |  | ||||||
|         .input > textarea { |         .input > textarea { | ||||||
|           width: 100%; |           width: 100%; | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| <template> | <template> | ||||||
|   <section class="chatContent"> |   <el-scrollbar class="chatContent"> | ||||||
|     <div class="chat-wrapper" v-for="item in list" :key="item.id"> |     <div class="chat-wrapper" v-for="item in list" :key="item.id"> | ||||||
|       <div class="chat-text" :class="{right:item.uid == 'me'}"> |       <div class="chat-text" :class="{right:item.userType == '0'}"> | ||||||
|         <img class="avatar" :src="item.avatar" alt=""/> |         <img class="avatar" :src="avatar(item)" alt=""/> | ||||||
|         <div class="content" v-text="item.msg"/> |         <div class="content" v-text="item.content"/> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </section> |   </el-scrollbar> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| @@ -16,38 +16,36 @@ export default { | |||||||
|   props: { |   props: { | ||||||
|     list: {default: () => []} |     list: {default: () => []} | ||||||
|   }, |   }, | ||||||
|  |   computed: { | ||||||
|  |     lastMessage: v => v.list.at(-1)?.content | ||||||
|  |   }, | ||||||
|   watch: { |   watch: { | ||||||
|     list: { |     lastMessage() { | ||||||
|       deep: true, handler() { |       this.scrollBottom() | ||||||
|         this.scrollBottom() |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     scrollBottom() { |     scrollBottom() { | ||||||
|       this.$el.scrollTop = this.$el.scrollHeight - this.$el.clientHeight |       const content = this.$el.querySelector(".el-scrollbar__wrap") | ||||||
|  |       if (content) { | ||||||
|  |         content.scrollTop = content.scrollHeight - content.clientHeight | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|     optimizeMessage(msg = "") { |     avatar(item) { | ||||||
|       return msg.trim() |       return item.avatar || (item.userType == '0' ? 'https://cdn.sinoecare.com/i/2024/06/17/666fdb275be82.png' : | ||||||
|  |           'https://cdn.sinoecare.com/i/2024/06/04/665ec6f5ef213.png') | ||||||
|     } |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.scrollBottom() | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| .chatContent { | .chatContent { | ||||||
|   overflow-y: auto; |   :deep(.el-scrollbar__wrap) { | ||||||
|  |     overflow-x: hidden; | ||||||
|   &::-webkit-scrollbar { |  | ||||||
|     width: 3px; |  | ||||||
|     /* 设置滚动条宽度 */ |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   &::-webkit-scrollbar-thumb { |  | ||||||
|     background-color: rgb(66, 70, 86); |  | ||||||
|     /* 设置滚动条滑块的背景色 */ |  | ||||||
|     border-radius: 50%; |  | ||||||
|     /* 设置滑块的圆角 */ |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .chat-text { |   .chat-text { | ||||||
| @@ -55,6 +53,8 @@ export default { | |||||||
|     gap: 12px; |     gap: 12px; | ||||||
|     font-size: 14px; |     font-size: 14px; | ||||||
|     color: #333333; |     color: #333333; | ||||||
|  |     margin-bottom: 8px; | ||||||
|  |     padding-right: 14px; | ||||||
|  |  | ||||||
|     .content { |     .content { | ||||||
|       position: relative; |       position: relative; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user