Selaa lähdekoodia

refactor(sidepanel): 重构侧边栏 Chat 组件

- 优化了 Chat 组件的布局和样式
- 添加了智能填表和总结按钮
- 改进了输入框的样式和功能
- 调整了卡片内容的样式
- 优化了 Tools 组件的样式
wzg 5 kuukautta sitten
vanhempi
commit
e452249797

+ 10 - 2
src/entrypoints/options/style.css

@@ -13,16 +13,19 @@
   -moz-osx-font-smoothing: grayscale;
   -webkit-text-size-adjust: 100%;
 }
+
 * {
   margin: 0;
   padding: 0;
   box-sizing: border-box;
 }
+
 a {
   font-weight: 500;
   color: #646cff;
   text-decoration: inherit;
 }
+
 a:hover {
   color: #535bf2;
 }
@@ -32,6 +35,7 @@ a {
   color: #646cff;
   text-decoration: inherit;
 }
+
 a:hover {
   color: #535bf2;
 }
@@ -60,9 +64,11 @@ button {
   cursor: pointer;
   transition: border-color 0.25s;
 }
+
 button:hover {
   border-color: #646cff;
 }
+
 button:focus,
 button:focus-visible {
   outline: 4px auto -webkit-focus-ring-color;
@@ -73,7 +79,7 @@ button:focus-visible {
 }
 
 #app {
-  max-width: 1280px;
+  /* max-width: 1280px; */
   width: 100%;
   height: 100vh;
 }
@@ -83,10 +89,12 @@ button:focus-visible {
     color: #213547;
     background-color: #ffffff;
   }
+
   a:hover {
     color: #747bff;
   }
+
   button {
     background-color: #f9f9f9;
   }
-}
+}

+ 108 - 111
src/entrypoints/sidepanel/Chat.vue

@@ -24,72 +24,46 @@
         </div>
       </div>
     </el-scrollbar>
-    <div style="display: flex;gap: 4px;padding: 1rem;">
-      <!-- <el-check-tag :checked="type === '1'" @change="type = '1'">文档总结</el-check-tag> -->
-      <el-check-tag :disabled="!taklToHtml" :checked="type === '2'" @change="() => {
-        if (type !== '2') {
-          inputMessage = '/智能填表 '
-          type = '2'
-        } else {
-          type = ''
-        }
-      }">
-        <el-tooltip content="选择后,在输入框描述填表流程" placement="top">
-          智能填表
-        </el-tooltip>
-      </el-check-tag>
-    </div>
+
     <Tools @read-click="isShowPage = true, taklToHtml = true" @upload-file="handleUpload" />
 
     <div>
-      <div v-show="isShowPage">
-        <span style="margin-left: 12px;margin-bottom: 6px;">
-          <el-tooltip content="开启后,将会根据左侧网页中的内容做出回答" placement="top">
-            <el-switch size="small" v-model="taklToHtml" />
-          </el-tooltip>&nbsp;&nbsp;&nbsp;与页面对话
-        </span>
-
-        <div class="card-content">
-          <img :src="pageInfo?.favIconUrl" style="width: 24px;" />
-          <div class="title-wrapper">
-            <div class="title-scroller" :class="{ 'scroll': isHoveringTitle && titleScroll }">
-              {{ pageInfo?.title }}
+      <!-- 输入区域 -->
+      <div class="input-area">
+        <div v-show="isShowPage" style="border-bottom: 1px solid #F0F0F0;">
+          <div class="card-content">
+            <img :src="pageInfo?.favIconUrl" style="width: 24px;" />
+            <div class="title-wrapper">
+              <div class="title-scroller" :class="{ 'scroll': isHoveringTitle && titleScroll }">
+                {{ pageInfo?.title }}
+              </div>
             </div>
+            <el-icon size="16px" @click="isShowPage = false; taklToHtml = false">
+              <CircleClose />
+            </el-icon>
+          </div>
+          <div class="card-btn">
+            <el-tooltip content="总结当前页面" placement="top">
+              <el-button round @click="handleCardButtonClick" :disabled="!taklToHtml">总结</el-button>
+            </el-tooltip>
+
+            <el-tooltip content="选择后,在输入框描述填表流程" placement="top">
+              <el-button round @click="handelIntelligentFillingClick" :disabled="!taklToHtml">智能填表</el-button>
+            </el-tooltip>
           </div>
-          <el-icon size="16px" @click="isShowPage = false">
-            <CircleClose />
-          </el-icon>
-        </div>
-        <div class="card-btn">
-          <el-button class="op" link @click="handleCardButtonClick" :disabled="!taklToHtml">总结</el-button>
         </div>
-      </div>
-      <!-- 输入区域 -->
-      <div class="input-area">
-        <el-input v-model="inputMessage" type="textarea" :rows="2" placeholder="输入消息..." @keyup.enter="() => {
+
+        <el-input ref="textareaRef" v-model="inputMessage" type="textarea" :rows="3" placeholder="输入消息..." @keyup.enter="() => {
           addMessage(inputMessage.trim(), true)
           inputMessage = ''
         }" />
-        <div>
-          <div style="display: flex;justify-content: space-between;">
-            <!-- <el-upload :before-upload="file => handleUpload(file)" :multiple="false"
-              :class="['upload', { 'can-hover': taklToHtml }]" name="file" :show-file-list="false" :accept="'.xlsx'"
-              :disabled="!taklToHtml">
-              <el-icon size="24" :color="taklToHtml ? 'gray' : '#c0c4cc'" style="cursor: pointer;">
-                <Upload />
-              </el-icon>
-            </el-upload> -->
-
-            <el-button size="small" type="primary" @click="() => {
-          addMessage(inputMessage.trim(), true)
-          inputMessage = ''
-        }"
-              :disabled="!inputMessage.trim() || sendLoading">
-              发送
-            </el-button>
-          </div>
+        <div class="chat_area_op">
+          <el-button type="primary" link @click="handleAsk" :disabled="!inputMessage.trim() || sendLoading">
+            <el-icon size="18" :color="inputMessage.trim() ? 'black' : 'gray'">
+              <Promotion />
+            </el-icon>
+          </el-button>
         </div>
-
       </div>
 
     </div>
@@ -97,23 +71,25 @@
 </template>
 
 <script setup>
-import { ref, onMounted, nextTick, inject } from 'vue'
+import { ref, onMounted, nextTick, inject, useTemplateRef } from 'vue'
 import { ElScrollbar, ElAvatar, ElInput, ElButton } from 'element-plus'
 import avator from '@/public/icon/32.png'
 import moment from 'moment'
-import {  buildExcelUnderstandingPrompt } from '@/utils/ai-service.js'
+import { buildExcelUnderstandingPrompt } from '@/utils/ai-service.js'
 import * as XLSX from "xlsx";
 import { ElMessage } from 'element-plus';
 import { useMsg } from '@/entrypoints/sidepanel/hook/useMsg.ts';
 import Tools from "@/entrypoints/sidepanel/component/tools.vue";
 import { useSummary } from '@/entrypoints/sidepanel/hook/useSummary.ts'
 import { mockData } from "@/entrypoints/sidepanel/mock"
+import { useAutoResizeTextarea } from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts';
 
 // 滚动条引用
 const scrollbar = ref(null);
 const type = ref('');
 const xlsxData = ref({});
 const isShowPage = ref(false);
+const tareRef = useTemplateRef("textareaRef");
 const {
   messages,
   inputMessage,
@@ -129,13 +105,19 @@ const {
 } = useMsg(scrollbar, type, xlsxData, fetchDataAndProcess);
 const { handleCardButtonClick } = useSummary(addMessage, sendRequese);
 
+function handelIntelligentFillingClick() {
+  if (type.value !== '2') {
+    inputMessage.value = '/智能填表 '
+    type.value = '2'
+  } else {
+    type.value = ''
+  }
+}
 
-
-// const pageInfo = ref({})
-
-
-// 输入框内容
-// const inputMessage = ref('')
+function handleAsk() {
+  addMessage(inputMessage.value.trim(), true);
+  inputMessage.value = '';
+}
 
 // 计算标题是否需要滚动
 const titleScroll = computed(() => {
@@ -268,6 +250,7 @@ const isHoveringTitle = ref(false)
 
 // 组件挂载时滚动到底部
 onMounted(async () => {
+  useAutoResizeTextarea(tareRef, inputMessage);
   chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
     if (message.type === 'TO_SIDE_PANEL_PAGE_INFO') {
       pageInfo.value = message.data
@@ -307,14 +290,14 @@ onMounted(async () => {
 })
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 .chat-container {
   height: 100vh;
   display: flex;
   flex-direction: column;
   border: 1px solid #dcdfe6;
   border-radius: 4px;
-  background-color: #fff;
+  background-color: #F0F0F0;
   font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
 }
 
@@ -428,55 +411,10 @@ onMounted(async () => {
   color: #409eff;
 }
 
-.input-area {
-  padding: 16px;
-  color: #7F838A;
-  border-top: 1px solid #eaeaea;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  gap: 12px;
-  background-color: #f9f9f9;
-}
-
-.input-area :deep(.el-textarea__inner) {
-  border-radius: 8px;
-  transition: border-color 0.3s;
-  resize: none;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
-}
-
-.input-area :deep(.el-textarea__inner:focus) {
-  border-color: #409eff;
-  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
-}
-
 .info-card {
   margin: 10px;
 }
 
-.card-content {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  padding: 14px 12px;
-  margin: 0 12px 6px;
-  background: #fff;
-  border-radius: 10px;
-  border: 1px solid rgba(0, 0, 0, 0.08);
-  font-size: 14px;
-  /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
-  transition: transform 0.2s, box-shadow 0.2s; */
-}
-
-.card-btn {
-  padding: 0 12px 4px;
-
-  .op {
-    margin-right: 3px;
-  }
-}
-
 /* .card-content:hover {
   transform: translateY(-2px);
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
@@ -574,4 +512,63 @@ onMounted(async () => {
     opacity: 1;
   }
 }
+
+
+.input-area {
+  padding: 8px 10px;
+  color: black;
+  background-color: #fff;
+  border: 1px solid rgba(102, 102, 102, 0.3);
+  border-radius: 16px;
+  margin: 0 12px 12px;
+
+  .card-content {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    padding: 14px 12px;
+    margin: 0 0 6px;
+    background: #fff;
+    border-radius: 10px;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    font-size: 14px;
+    /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);*/
+    transition: transform 0.4s;
+  }
+
+  .card-btn {
+    padding: 0 0 4px;
+
+    .op {
+      margin-right: 3px;
+    }
+  }
+
+  .chat_area_op {
+    margin-top: 3px;
+    display: flex;
+    justify-content: end;
+  }
+}
+
+.input-area:hover {
+  border-color: rgba(102, 102, 102, 0.4);
+}
+
+.input-area :deep(.el-textarea__inner) {
+  border-radius: 8px;
+  transition: border-color 0.3s;
+  resize: none;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+}
+
+.input-area :deep(.el-textarea__inner) {
+  padding: 0 4px;
+  margin-top: 3px;
+}
+
+.input-area :deep(.el-textarea__inner:focus) {
+  border-color: #409eff;
+  box-shadow: none;
+}
 </style>

+ 4 - 3
src/entrypoints/sidepanel/component/tools.vue

@@ -5,7 +5,7 @@ import { Reading, Upload, Paperclip, Scissor } from "@element-plus/icons-vue";
 
 const value = ref(options[0].value);
 
-const emit = defineEmits(['readClick','uploadFile'])
+const emit = defineEmits(['readClick', 'uploadFile'])
 </script>
 
 <template>
@@ -14,11 +14,12 @@ const emit = defineEmits(['readClick','uploadFile'])
       <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
     </el-select>
     <span class="separator"></span>
-    <el-tooltip effect="dark" content="阅读此页" placement="top">
+    <el-tooltip effect="dark" content="阅读此页,开启后将会根据左侧网页中的内容做出回答" placement="top">
       <el-button :icon="Reading" circle @click="emit('readClick')" />
     </el-tooltip>
     <span class="separator"></span>
-    <el-upload style="display:inline-block" :before-upload="(file:any )=> emit('uploadFile',file)" :multiple="false" name="file" :show-file-list="false" :accept="'.xlsx'">
+    <el-upload style="display:inline-block" :before-upload="(file: any) => emit('uploadFile', file)" :multiple="false"
+      name="file" :show-file-list="false" :accept="'.xlsx'">
       <el-tooltip effect="dark" content="文件上传" placement="top">
         <el-button :icon="Paperclip" circle />
       </el-tooltip>

+ 23 - 0
src/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts

@@ -0,0 +1,23 @@
+import { ref, watch, onMounted } from 'vue';
+
+export function useAutoResizeTextarea(textareaRef: any, text: any) {
+
+  const adjustHeight = () => {
+    const textarea = textareaRef.value.ref;
+    textarea.style.height = 'auto'; // 重置高度
+    textarea.style.height = `${textarea.scrollHeight}px`; // 设置新高度
+    textarea.style['max-height'] = '240px';
+    textarea.style['min-height'] = '54px';
+  };
+  watch(text, (newVal) => {
+    if (newVal === '') {
+      textareaRef.value.ref.style.height = '54px';
+      return;
+    }
+    adjustHeight();
+  });
+
+  onMounted(() => {
+    adjustHeight(); // 初始化时调整高度
+  });
+}

+ 1 - 0
src/entrypoints/sidepanel/hook/useMsg.ts

@@ -2,6 +2,7 @@
 import { ref, reactive } from 'vue';
 import avator from '@/public/icon/32.png';
 import moment from 'moment'
+// import { sendMessage } from '@/utils/ai-service';
 export function useMsg(scrollbar: any, type: any, xlsxData: any, fetchDataAndProcess: Function) {
   const inputMessage = ref('');
   const indexTemp = ref(0);

+ 3 - 3
src/entrypoints/sidepanel/style.css

@@ -69,10 +69,10 @@ button:hover {
   border-color: #646cff;
 }
 
-button:focus,
+/* button:focus,
 button:focus-visible {
   outline: 4px auto -webkit-focus-ring-color;
-}
+} */
 
 .card {
   padding: 2em;
@@ -130,4 +130,4 @@ pre::-webkit-scrollbar-thumb:hover {
   button {
     background-color: #f9f9f9;
   }
-}
+}