Browse Source

feat: 新增对话界面,新增引导对话框

hotchicken1996 1 month ago
parent
commit
9b7b8df950

BIN
client/src/assets/imgs/ai/fire.png


BIN
client/src/assets/imgs/ai/headSculpture.png


BIN
client/src/assets/imgs/ai/refresh.png


BIN
client/src/assets/imgs/ai/send.png


+ 38 - 0
client/src/views/OaSystem/aiQA/components/AChat.vue

@@ -0,0 +1,38 @@
+<script setup lang="ts">
+import headSculpture from '@/assets/imgs/ai/headSculpture.png'
+</script>
+
+<template>
+  <div class="answer-chat-item-body">
+    <img :src="headSculpture" alt="" class="head-sculpture" />
+    <div class="content">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.answer-chat-item-body {
+  width: 100%;
+  display: flex;
+  margin-bottom: 20px;
+
+  .head-sculpture {
+    flex-shrink: 0;
+    width: 48px;
+    height: 48px;
+    border-radius: 50%;
+    margin-right: 6px;
+  }
+
+  .content {
+    padding: 20px;
+    background: linear-gradient(180deg, #f1f7ff 0%, #fbfcff 100%);
+    border-radius: 8px 8px 8px 8px;
+    border: 1px solid #edf2fa;
+    font-weight: 400;
+    font-size: 16px;
+    color: #2d333c;
+  }
+}
+</style>

+ 107 - 3
client/src/views/OaSystem/aiQA/components/ChatContent.vue

@@ -1,9 +1,60 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import AChat from '@/views/OaSystem/aiQA/components/AChat.vue'
+import ExampleChat from '@/views/OaSystem/aiQA/components/ExampleChat.vue'
+import sendImg from '@/assets/imgs/ai/send.png'
+import QChat from '@/views/OaSystem/aiQA/components/QChat.vue'
+
+interface ChatContentProps {
+  currentQuestion: string
+  changeQuestion: (q: string) => void
+}
+
+const props = defineProps<ChatContentProps>()
+const { currentQuestion } = toRefs(props)
+const { changeQuestion } = props
+
+const textarea = ref('')
+
+const handleClear = () => {
+  changeQuestion('')
+}
+
+const handleSend = () => {
+  changeQuestion(textarea.value)
+  textarea.value = ''
+}
+</script>
 
 <template>
   <div class="chat-content">
-    <div class="message-body"></div>
-    <div class="input-body"></div>
+    <div class="message-body">
+      <template v-if="currentQuestion">
+        <QChat>{{ currentQuestion }}</QChat>
+        <AChat> 生成中 </AChat>
+      </template>
+      <template v-else>
+        <!--   引导对话   -->
+        <AChat>
+          <ExampleChat />
+        </AChat>
+      </template>
+    </div>
+    <div class="input-body">
+      <div class="input-header">
+        <div class="clear-btn" @click="handleClear"> 清空对话</div>
+      </div>
+      <el-input
+        class="input"
+        v-model="textarea"
+        :autosize="{ minRows: 4, maxRows: 20 }"
+        type="textarea"
+        resize="none"
+        placeholder="请输入问题,可以通过回车换行"
+      />
+      <div class="q-btn" @click="handleSend">
+        <img :src="sendImg" alt="发送" />
+      </div>
+    </div>
   </div>
 </template>
 
@@ -12,15 +63,68 @@
   width: 100%;
   height: 100%;
   padding: 25px 50px;
+  display: flex;
+  flex-flow: column;
 
   .message-body {
+    flex-grow: 1;
+    overflow-y: auto;
   }
 
   .input-body {
+    position: relative;
+    padding: 15px 20px;
+    min-height: 120px;
+    flex-shrink: 0;
+    margin-top: 20px;
     width: calc(100% - 50px);
     margin-left: 50px;
     background: #f4f7fd;
     border-radius: 8px 8px 8px 8px;
+    display: flex;
+
+    .input-header {
+      position: absolute;
+      top: -30px;
+      width: calc(100% - 50px);
+      display: flex;
+      justify-content: flex-end;
+
+      .clear-btn {
+        cursor: pointer;
+        font-size: 16px;
+        color: #8a94a4;
+      }
+    }
+
+    .input {
+      width: calc(100% - 80px);
+
+      :deep(.el-textarea__inner) {
+        border: none !important;
+        box-shadow: none !important;
+        background: transparent;
+      }
+    }
+
+    .q-btn {
+      width: 76px;
+      height: 48px;
+      background: #2e77e6;
+      border-radius: 30px 30px 30px 30px;
+      overflow: hidden;
+      cursor: pointer;
+      align-self: flex-end;
+
+      img {
+        width: 100%;
+        height: 100%;
+      }
+
+      &:hover {
+        opacity: 0.9;
+      }
+    }
   }
 }
 </style>

+ 111 - 0
client/src/views/OaSystem/aiQA/components/ExampleChat.vue

@@ -0,0 +1,111 @@
+<script setup lang="ts">
+import fireIcon from '@/assets/imgs/ai/fire.png'
+import refreshIcon from '@/assets/imgs/ai/refresh.png'
+
+const entries = [
+  '村庄规划的编制要点是什么?',
+  '耕地占补平衡方案写作案例?',
+  '低效用地再开发方案编制要点?',
+  '批而未供核查举证方式有哪些?',
+  '批而未供核查举证方式有哪些?',
+  '批而未供核查举证方式有哪些?',
+  '浙江省工业用地调查技术导则?',
+  '土地储备计划编制案例?'
+]
+</script>
+
+<template>
+  <div class="example-chat">
+    <h3 class="title"> 你好,我是智能助理-小智 </h3>
+    <p> 今天又是充满能量的一天!我可以为你提供24小时智能机器人咨询服务,有什么可以帮助你的么? </p>
+    <p>
+      想知道如何进行提问?
+      <a class="a-btn">点击查看</a>
+    </p>
+    <div class="entry-header">
+      <span>大家都在问:</span>
+      <a class="a-btn"> <img class="icon" :src="refreshIcon" alt="" />换一批</a>
+    </div>
+    <div class="example-entries">
+      <div class="example-entry" v-for="entry in entries" :key="entry">
+        <img class="icon" :src="fireIcon" alt="" />
+        <span>{{ entry }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.example-chat {
+  font-size: 16px;
+  color: #2d333c;
+
+  p {
+    margin: 10px 0;
+  }
+
+  .title {
+    font-weight: bold;
+    font-size: 20px;
+    color: #27375f;
+    margin-bottom: 20px;
+  }
+
+  .a-btn {
+    cursor: pointer;
+    display: inline-flex;
+    align-items: center;
+    color: #2e77e6;
+
+    .icon {
+      width: 16px;
+      height: 16px;
+      border-radius: 50%;
+      margin-right: 8px;
+    }
+  }
+
+  .entry-header {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    margin-top: 40px;
+    font-size: 16px;
+  }
+
+  .example-entries {
+    width: 100%;
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    margin-top: 20px;
+    grid-gap: 20px;
+
+    .example-entry {
+      width: calc(25% - 16px);
+      padding: 12px 20px;
+      border-radius: 8px;
+      background-color: #e9f0fb;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+
+      span {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+
+      .icon {
+        width: 24px;
+        height: 24px;
+        border-radius: 50%;
+      }
+
+      &:hover {
+        background-color: #f4f6f9;
+      }
+    }
+  }
+}
+</style>

+ 48 - 0
client/src/views/OaSystem/aiQA/components/QChat.vue

@@ -0,0 +1,48 @@
+<script setup lang="ts">
+import avatarImg from '@/assets/imgs/avatar.gif'
+import { useUserStoreWithOut } from '@/store/modules/user'
+
+const userStore = useUserStoreWithOut()
+const user = userStore.$state
+</script>
+
+<template>
+  <div class="question-chat-item-body">
+    <div class="head-sculpture">
+      <ElAvatar :src="user.user.avatar ?? avatarImg" alt="" class="user-icon" />
+    </div>
+    <div class="content">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.question-chat-item-body {
+  width: 100%;
+  display: flex;
+  margin-bottom: 20px;
+
+  .head-sculpture {
+    flex-shrink: 0;
+    width: 48px;
+    height: 48px;
+    border-radius: 50%;
+    margin-right: 6px;
+    padding: 3px;
+
+    .user-icon {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .content {
+    padding: 10px 30px;
+    background: #4b87e2;
+    border-radius: 8px 8px 8px 8px;
+    font-size: 16px;
+    color: #ffffff;
+  }
+}
+</style>

+ 10 - 0
client/src/views/OaSystem/aiQA/components/historyChat.vue

@@ -41,6 +41,7 @@ const handleNewChat = (): void => {
   display: flex;
   flex-direction: column;
   align-items: center;
+  flex-shrink: 0;
 
   .new-chat-btn {
     flex-shrink: 0;
@@ -61,6 +62,10 @@ const handleNewChat = (): void => {
       height: 22px;
       margin-right: 10px;
     }
+
+    &:hover {
+      opacity: 0.9;
+    }
   }
 
   .search-content {
@@ -93,12 +98,17 @@ const handleNewChat = (): void => {
       border-radius: 8px 8px 8px 8px;
       font-size: 16px;
       color: #2d333c;
+      cursor: pointer;
 
       .icon {
         width: 16px;
         height: 16px;
         margin-right: 8px;
       }
+
+      &:hover {
+        background-color: #e9f0fb;
+      }
     }
   }
 

+ 11 - 3
client/src/views/OaSystem/aiQA/index.vue

@@ -1,15 +1,23 @@
 <script setup lang="ts">
 import HistoryChat from '@/views/OaSystem/aiQA/components/historyChat.vue'
+import ChatContent from '@/views/OaSystem/aiQA/components/ChatContent.vue'
 
 defineOptions({ name: 'AIQA' })
-/** 初始化 **/
-onMounted(() => {})
+
+const currentQuestion = ref('')
+
+const changeQuestion = (q: string): void => {
+  console.log('changeQuestion: ', q)
+  currentQuestion.value = q
+}
 </script>
 
 <template>
   <div class="ai-q-a-page">
     <HistoryChat />
-    <div class="right-part"></div>
+    <div class="right-part">
+      <ChatContent :currentQuestion="currentQuestion" :changeQuestion="changeQuestion" />
+    </div>
   </div>
 </template>