wujingjing
2024-06-28 b89ed80f20f2e07729a68f2c6ed2ca5134332a17
src/components/chat/Chat.vue
@@ -1,53 +1,52 @@
<template>
   <div class="flex flex-col h-screen">
      <div class="flex flex-nowrap fixed w-full items-baseline top-0 px-6 py-4 bg-gray-100">
         <div class="text-2xl font-bold">ChatGPT</div>
         <div class="ml-4 text-sm text-gray-500">基于 OpenAI 的 ChatGPT 自然语言模型人工智能对话</div>
         <div class="ml-auto px-3 py-2 text-sm cursor-pointer hover:bg-white rounded-md" @click="clickConfig()">设置</div>
      </div>
   <div class="flex flex-col h-full">
      <div class="h-full flex flex-col items-center overflow-y-auto">
         <div ref="chatListDom" class="h-full">
            <div
               class="group flex px-4 py-4 hover:bg-slate-100 rounded-lg"
               v-for="(item, index) of messageList.filter((v) => v.role !== 'system')"
               :key="index"
            >
               <img class="rounded-full size-12 mr-4" :src="roleImageMap[item.role]" alt="" srcset="" />
      <div class="flex-1 mx-2 mt-20 mb-2" ref="chatListDom">
         <div
            class="group flex flex-col px-4 py-3 hover:bg-slate-100 rounded-lg"
            v-for="(item, index) of messageList.filter((v) => v.role !== 'system')"
            :key="index"
         >
            <div class="flex justify-between items-center mb-2">
               <div class="font-bold">{{ roleAlias[item.role] }}:</div>
               <Copy class="invisible group-hover:visible" :content="item.content" />
            </div>
            <div>
               <div class="prose text-sm text-slate-600 leading-relaxed" v-if="item.content" v-html="md.render(item.content)"></div>
               <Loding v-else />
               <div class="flex">
                  <div v-if="item.content">
                     <div
                        :class="{ 'bg-[#d8d8ff]': item.role === RoleEnum.assistant, 'bg-white': item.role === RoleEnum.user }"
                        class="prose text-sm rounded-[6px] p-4 leading-relaxed max-w-[100ch]"
                        v-html="md.render(item.content)"
                     ></div>
                     <div class="">
                        <SvgIcon name="ele-CopyDocument"/>
                        <SvgIcon name="ele-Check"/>
                        <SvgIcon name="ywicon icon-dianzan"/>
                        <SvgIcon name="ywicon icon-buzan"/>
                     </div>
                  </div>
                  <Loding v-else />
               </div>
            </div>
         </div>
      </div>
      <div class="sticky bottom-0 w-full p-6 pb-8 bg-gray-100">
         <div class="-mt-2 mb-2 text-sm text-gray-500" v-if="isConfig">请输入 API Key:</div>
         <div class="flex">
            <input
               class="input"
               :type="isConfig ? 'password' : 'text'"
               :placeholder="isConfig ? 'sk-xxxxxxxxxx' : '请输入'"
               v-model="messageContent"
               @keydown.enter="isTalking || sendOrSave()"
            />
            <button class="btn" :disabled="isTalking" @click="sendOrSave()">
               {{ isConfig ? '保存' : '发送' }}
            </button>
         </div>
      <div class="sticky bottom-0 w-full p-6 pb-8 bg-gray-100 flex justify-center">
         <PlayBar :isTalking="isTalking" v-model="messageContent" @sendClick="sendOrSave" />
      </div>
   </div>
</template>
<script setup lang="ts">
import type { ChatMessage } from './types';
import { ref, watch, nextTick, onMounted } from 'vue';
import cryptoJS from 'crypto-js';
import { nextTick, onMounted, ref, watch } from 'vue';
import Loding from './components/Loding.vue';
import Copy from './components/Copy.vue';
import { md } from './libs/markdown';
import { RoleEnum, type ChatMessage, roleImageMap } from './types';
import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue';
import router from '/@/router';
let apiKey = '';
let isConfig = ref(false);
let isTalking = ref(false);
@@ -57,11 +56,7 @@
const roleAlias = { user: 'ME', assistant: 'ChatGPT', system: 'System' };
const messageList = ref<ChatMessage[]>([
   {
      role: 'system',
      content: '你是 ChatGPT,OpenAI 训练的大型语言模型,尽可能简洁地回答。',
   },
   {
      role: 'assistant',
      role: RoleEnum.assistant,
      content: `你好,我是AI语言模型,我可以提供一些常用服务和信息,例如:
  
  1. 翻译:我可以把中文翻译成英文,英文翻译成中文,还有其他一些语言翻译,比如法语、日语、西班牙语等。
@@ -72,12 +67,18 @@
  
  请告诉我你需要哪方面的帮助,我会根据你的需求给你提供相应的信息和建议。`,
   },
   {
      role: RoleEnum.user,
      content: `你好`,
   },
]);
onMounted(() => {
   if (getAPIKey()) {
      switchConfigStatus();
   }
   const inputValue = history.state.inputValue;
});
const sendChatMessage = async (content: string = messageContent.value) => {
@@ -86,9 +87,9 @@
      if (messageList.value.length === 2) {
         messageList.value.pop();
      }
      messageList.value.push({ role: 'user', content });
      messageList.value.push({ role: RoleEnum.user, content });
      clearMessageContent();
      messageList.value.push({ role: 'assistant', content: '' });
      messageList.value.push({ role: RoleEnum.assistant, content: '' });
      // const { body, status } = await chat(messageList.value, getAPIKey());
      // if (body) {
@@ -97,7 +98,7 @@
      // }
      const a = new Promise<string>((resolve) => {
         setTimeout(() => {
            resolve('nihao ');
            resolve('你好 ');
         }, 500);
      });
@@ -192,7 +193,8 @@
const scrollToBottom = () => {
   if (!chatListDom.value) return;
   scrollTo(0, chatListDom.value.scrollHeight);
   chatListDom.value.lastElementChild.scrollIntoView();
   // scrollTo(0, chatListDom.value.scrollHeight);
};
watch(messageList.value, () => nextTick(() => scrollToBottom()));