Преглед на файлове

fix: 完成历史记录

zhangnuoyan преди 2 години
родител
ревизия
714dcd6710
променени са 9 файла, в които са добавени 1177 реда и са изтрити 780 реда
  1. BIN
      src/assets/other/robot.gif
  2. 629 578
      src/components/DialogWrap.vue
  3. 7 2
      src/components/History.vue
  4. 102 75
      src/components/MessageInput.vue
  5. 61 53
      src/utils/config.js
  6. 74 66
      src/utils/helper.js
  7. 162 0
      src/views/Answer.vue
  8. 126 6
      src/views/Home.vue
  9. 16 0
      src/views/Template.vue

BIN
src/assets/other/robot.gif


+ 629 - 578
src/components/DialogWrap.vue

@@ -1,655 +1,706 @@
 <template>
-  <div class="dialog-flex-wrap">
-    <div class="dialog-flex" ref="dialogScroll">
-      <div class="section-open" v-if="open">
-        <header class="dialog-header">
-          <a-avatar shape="circle" :size="28">
-            <img
-              alt="avatar"
-              :src="agentInfo.avatar"
-            />
-          </a-avatar>
-          <a-typography-paragraph :ellipsis="{rows: 1, css: true}" :style="{flex: 1,margin: '0 0 0 12px'}">
-            <span class="dialog-span">{{agentInfo.title}}</span>
-          </a-typography-paragraph>
-        </header>
-        <footer class="dialog-footer">
-          <p>{{open}}</p>
-        </footer>
-      </div>
-      <div class="section-suggestions">
-        <div :class="['section-suggestion', readonly ? 'disabled' : '']" v-for="(suggestion, key) in suggestions" :key="key" @click="selectSuggestion(suggestion)">
-          {{suggestion}}<icon-arrow-right />
-        </div>
-      </div>
-      <div class="dialog-list" v-if="dialogList.length > 0" :key="randomKey">
-        <div :class="['dialog-item', isSetting ? '' : 'dialog-absolute']" v-for="(item,key) in dialogList" :key="item.key">
-          <div class="dialog-header" v-if="item?.question">
-            <a-avatar shape="circle" :size="28">
-              <img
-                alt="avatar"
-                :src="userInfo.avatar"
-              />
-            </a-avatar>
-            <a-typography-paragraph :ellipsis="{rows: 1, css: true}" :style="{flex: 1,margin: '0 0 0 12px'}" v-if="isSetting">
-              <span class="dialog-span">{{userInfo.name}}</span>
-            </a-typography-paragraph>
-          </div>
-          <div class="dialog-footer" v-if="item?.question">
-            <p style="margin-top: 0;">{{item.question}}</p>
-          </div>
-          <div class="dialog-file" v-if="item?.file">
-            <icon-file-pdf :size="20" />
-            <a-typography-paragraph :ellipsis="{rows: 1, css: true}" :style="{flex: 1,margin: '0 0 0 8px'}">
-              {{item.file.name}}
-            </a-typography-paragraph>
-          </div>
-          <div class="dialog-header" v-if="item?.capacity">
-            <a-avatar shape="circle" :size="28">
-              <img
-                alt="avatar"
-                :src="agentInfo.avatar"
-              />
-            </a-avatar>
-            <a-typography-paragraph :ellipsis="{rows: 1, css: true}" :style="{flex: 1,margin: '0 0 0 12px'}" v-if="isSetting">
-              <span class="dialog-span">{{agentInfo.title}}</span>
-            </a-typography-paragraph>
-          </div>
-          <div class="dialog-footer" v-if="item?.capacity">
-            <div @click="openCapacity(key)" :class="[readonly || item.capacity.status === 'loading' ? 'disabled' : '']">
-              <a-spin :size="18" v-if="item.capacity.status === 'loading'" />
-              <icon-check-circle-fill :size="18" v-else />
-              <span>{{item.capacity.status === 'loading' ? '正在调用' : '已调用'}}</span>
-              <b>{{item.capacity.text}}</b>
-              <icon-caret-right size="18" v-if="item.capacity.status === 'loaded' && !menuList[key].open" />
-              <icon-caret-down size="18" v-if="item.capacity.status === 'loaded' && menuList[key].open" />
-            </div>
-            <section v-if="item.capacity.status === 'loaded'" v-show="menuList[key].open">
-              <p>{{item.capacity.data.title}}</p>
-              <div v-if="item.capacity.data.list">
-                <p v-for="(item,key) in item.capacity.data.list" :key="key">
-                  <a-typography-paragraph :ellipsis="{rows: 1, css: true}" :style="{flex: 1,margin: '0'}">
-                    {{item}}
-                  </a-typography-paragraph>
-                </p>
-              </div>
-              <div v-if="item.capacity.data.documents">
-                <div v-for="(document, documentKey) in item.capacity.data.documents" :key="documentKey">
-                  <div @click="openDocument(key, documentKey)" :class="[readonly ? 'disabled' : '']">
-                    <span>{{document.type}}</span>
-                    <a-typography-paragraph :ellipsis="{rows: 1, css: true}" :style="{flex: 1,margin: '0'}">
-                      <span>{{document.title}}</span>
+    <div class="dialog-flex-wrap">
+        <div class="dialog-flex" ref="dialogScroll">
+            <div class="section-open" v-if="open">
+                <header class="dialog-header">
+                    <a-avatar shape="circle" :size="28">
+                        <img alt="avatar" :src="agentInfo.avatar" />
+                    </a-avatar>
+                    <a-typography-paragraph
+                        :ellipsis="{ rows: 1, css: true }"
+                        :style="{ flex: 1, margin: '0 0 0 12px' }"
+                    >
+                        <span class="dialog-span">{{ agentInfo.title }}</span>
                     </a-typography-paragraph>
-                    <span>
-                      <em v-for="(item, documentIndex) in document.list" :key="documentIndex">{{item.title}}</em>
-                    </span>
-                    <icon-caret-down size="12" v-if="documentKey === menuList[key].documentOpenIndex" />
-                    <icon-caret-right size="12" v-else />
-                  </div>
-                  <div v-show="documentKey === menuList[key].documentOpenIndex">
-                    <p v-for="(item, index) in document.list" :key="index">
-                      <span>{{item.title}}</span>
-                      <a-typography-paragraph :ellipsis="{rows: 3, css: true}" :style="{flex: 1,margin: '0'}">
-                        <span style="color: rgba(153, 153, 153, 1);font-size: 12px;">{{item.content}}</span>
-                      </a-typography-paragraph>
-                    </p>
-                  </div>
+                </header>
+                <footer class="dialog-footer">
+                    <p>{{ open }}</p>
+                </footer>
+            </div>
+            <div class="section-suggestions">
+                <div
+                    :class="['section-suggestion', readonly ? 'disabled' : '']"
+                    v-for="(suggestion, key) in suggestions"
+                    :key="key"
+                    @click="selectSuggestion(suggestion)"
+                >
+                    {{ suggestion }}<icon-arrow-right />
+                </div>
+            </div>
+            <div class="dialog-list" v-if="dialogList.length > 0" :key="randomKey">
+                <div
+                    :class="['dialog-item', isSetting ? '' : 'dialog-absolute']"
+                    v-for="(item, key) in dialogList"
+                    :key="item.key"
+                >
+                    <div class="dialog-header" v-if="item?.question">
+                        <a-avatar shape="circle" :size="28">
+                            <img alt="avatar" :src="userInfo.avatar" />
+                        </a-avatar>
+                        <a-typography-paragraph
+                            :ellipsis="{ rows: 1, css: true }"
+                            :style="{ flex: 1, margin: '0 0 0 12px' }"
+                            v-if="isSetting"
+                        >
+                            <span class="dialog-span">{{ userInfo.name }}</span>
+                        </a-typography-paragraph>
+                    </div>
+                    <div class="dialog-footer" v-if="item?.question">
+                        <p style="margin-top: 0">{{ item.question }}</p>
+                    </div>
+                    <div class="dialog-file" v-if="item?.file">
+                        <icon-file-pdf :size="20" />
+                        <a-typography-paragraph
+                            :ellipsis="{ rows: 1, css: true }"
+                            :style="{ flex: 1, margin: '0 0 0 8px' }"
+                        >
+                            {{ item.file.name }}
+                        </a-typography-paragraph>
+                    </div>
+                    <div class="dialog-header" v-if="item?.capacity">
+                        <a-avatar shape="circle" :size="28">
+                            <img alt="avatar" :src="agentInfo.avatar" />
+                        </a-avatar>
+                        <a-typography-paragraph
+                            :ellipsis="{ rows: 1, css: true }"
+                            :style="{ flex: 1, margin: '0 0 0 12px' }"
+                            v-if="isSetting"
+                        >
+                            <span class="dialog-span">{{ agentInfo.title }}</span>
+                        </a-typography-paragraph>
+                    </div>
+                    <div class="dialog-footer" v-if="item?.capacity">
+                        <div
+                            @click="openCapacity(key)"
+                            :class="[readonly || item.capacity.status === 'loading' ? 'disabled' : '']"
+                        >
+                            <a-spin :size="18" v-if="item.capacity.status === 'loading'" />
+                            <icon-check-circle-fill :size="18" v-else />
+                            <span>{{ item.capacity.status === "loading" ? "正在调用" : "已调用" }}</span>
+                            <b>{{ item.capacity.text }}</b>
+                            <icon-caret-right
+                                size="18"
+                                v-if="item.capacity.status === 'loaded' && !menuList[key].open"
+                            />
+                            <icon-caret-down size="18" v-if="item.capacity.status === 'loaded' && menuList[key].open" />
+                        </div>
+                        <section v-if="item.capacity.status === 'loaded'" v-show="menuList[key].open">
+                            <p>{{ item.capacity.data.title }}</p>
+                            <div v-if="item.capacity.data.list">
+                                <p v-for="(item, key) in item.capacity.data.list" :key="key">
+                                    <a-typography-paragraph
+                                        :ellipsis="{ rows: 1, css: true }"
+                                        :style="{ flex: 1, margin: '0' }"
+                                    >
+                                        {{ item }}
+                                    </a-typography-paragraph>
+                                </p>
+                            </div>
+                            <div v-if="item.capacity.data.documents">
+                                <div v-for="(document, documentKey) in item.capacity.data.documents" :key="documentKey">
+                                    <div @click="openDocument(key, documentKey)" :class="[readonly ? 'disabled' : '']">
+                                        <span>{{ document.type }}</span>
+                                        <a-typography-paragraph
+                                            :ellipsis="{ rows: 1, css: true }"
+                                            :style="{ flex: 1, margin: '0' }"
+                                        >
+                                            <span>{{ document.title }}</span>
+                                        </a-typography-paragraph>
+                                        <span>
+                                            <em v-for="(item, documentIndex) in document.list" :key="documentIndex">{{
+                                                item.title
+                                            }}</em>
+                                        </span>
+                                        <icon-caret-down
+                                            size="12"
+                                            v-if="documentKey === menuList[key].documentOpenIndex"
+                                        />
+                                        <icon-caret-right size="12" v-else />
+                                    </div>
+                                    <div v-show="documentKey === menuList[key].documentOpenIndex">
+                                        <p v-for="(item, index) in document.list" :key="index">
+                                            <span>{{ item.title }}</span>
+                                            <a-typography-paragraph
+                                                :ellipsis="{ rows: 3, css: true }"
+                                                :style="{ flex: 1, margin: '0' }"
+                                            >
+                                                <span style="color: rgba(153, 153, 153, 1); font-size: 12px">{{
+                                                    item.content
+                                                }}</span>
+                                            </a-typography-paragraph>
+                                        </p>
+                                    </div>
+                                </div>
+                            </div>
+                        </section>
+                        <p v-if="item.answer">{{ item.answer }}</p>
+                    </div>
+                    <div class="dialog-other" v-if="item?.other">
+                        <p v-if="isSetting">
+                            <img src="@/assets/icons/check.png" width="16" />
+                            <span>{{ item.other.token }}</span>
+                        </p>
+                        <p v-else></p>
+                        <div>
+                            <p @click="reQuestion(item.question)" :class="[readonly ? 'disabled' : '']">
+                                <img src="@/assets/icons/refresh.png" width="15" />
+                            </p>
+                            <p @click="copyAnswer(item.answer)" :class="[readonly ? 'disabled' : '']">
+                                <img src="@/assets/icons/copy.png" width="15" />
+                            </p>
+                            <p @click="saveAnswer(key)" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
+                                <img src="@/assets/icons/star-full.png" width="15" v-if="menuList[key].save" />
+                                <img src="@/assets/icons/star.png" width="15" v-else />
+                            </p>
+                            <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
+                                <img src="@/assets/icons/thumb-up.png" width="15" />
+                            </p>
+                            <p @click="feedbackAnswer" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
+                                <img src="@/assets/icons/thumb-down.png" width="15" />
+                            </p>
+                            <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
+                                <img src="@/assets/icons/share.png" width="15" />
+                            </p>
+                        </div>
+                    </div>
                 </div>
-              </div>
-            </section>
-            <p v-if="item.answer">{{item.answer}}</p>
-          </div>
-          <div class="dialog-other" v-if="item?.other">
-            <p v-if="isSetting">
-              <img src="@/assets/icons/check.png" width="16" />
-              <span>{{item.other.token}}</span>
-            </p>
-            <p v-else></p>
-            <div>
-              <p @click="reQuestion(item.question)" :class="[readonly ? 'disabled' : '']">
-                <img src="@/assets/icons/refresh.png" width="15" />
-              </p>
-              <p @click="copyAnswer(item.answer)" :class="[readonly ? 'disabled' : '']">
-                <img src="@/assets/icons/copy.png" width="15" />
-              </p>
-              <p @click="saveAnswer(key)" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
-                <img src="@/assets/icons/star-full.png" width="15" v-if="menuList[key].save" />
-                <img src="@/assets/icons/star.png" width="15" v-else />
-              </p>
-              <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
-                <img src="@/assets/icons/thumb-up.png" width="15" />
-              </p>
-              <p @click="feedbackAnswer" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
-                <img src="@/assets/icons/thumb-down.png" width="15" />
-              </p>
-              <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
-                <img src="@/assets/icons/share.png" width="15" />
-              </p>
             </div>
-          </div>
         </div>
-      </div>
+        <div v-show="readonly" @click="stopWriting" class="dialog-writing-button">
+            <icon-record-stop :size="20" style="margin-right: 4px" />停止生成
+        </div>
+        <dialog-feedback
+            :showFeedback="showFeedback"
+            @handleCancel="handleCancel"
+            @handleOk="handleOk"
+            v-if="!isSetting"
+        />
     </div>
-    <div v-show="readonly" @click="stopWriting" class="dialog-writing-button"><icon-record-stop :size="20" style="margin-right: 4px;" />停止生成</div>
-    <dialog-feedback :showFeedback="showFeedback" @handleCancel="handleCancel"  @handleOk="handleOk" v-if="!isSetting" />
-  </div>
 </template>
 <script>
 import DialogFeedback from "./DialogFeedback.vue";
-import { defineComponent, ref, onBeforeUnmount, onMounted, reactive, nextTick, inject } from 'vue';
+import { defineComponent, ref, onBeforeUnmount, onMounted, reactive, nextTick, inject } from "vue";
 export default defineComponent({
-  components: {
-    DialogFeedback
-  },
-  props: ['open', 'suggestions', 'agentInfo', 'userInfo', 'isSetting', 'readonly'],
-  setup(props, {emit}) {
-    const DOCUMENT_RESULT = {
-      type: 'document',
-      text: '谷歌知识库',
-      status: 'loaded',
-      data: {
-        title: 'AI Agent的论文',
-        documents: [{
-          type: 'PDF',
-          title: '科比的传奇一生,你知道多少?',
-          list: [
-            {
-              title: 'P1',
-              content: '科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP'
-            },
-            {
-              title: 'P2',
-              content: '科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP'
-            }
-          ]
-        },{
-          type: 'PDF',
-          title: '科比的传奇一生,你知道多少?',
-          list: [
-            {
-              title: 'P1',
-              content: '科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP'
-            },
-            {
-              title: 'P2',
-              content: '科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP'
+    components: {
+        DialogFeedback
+    },
+    props: ["open", "suggestions", "agentInfo", "userInfo", "isSetting", "readonly"],
+    setup(props, { emit }) {
+        const DOCUMENT_RESULT = {
+            type: "document",
+            text: "谷歌知识库",
+            status: "loaded",
+            data: {
+                title: "AI Agent的论文",
+                documents: [
+                    {
+                        type: "PDF",
+                        title: "科比的传奇一生,你知道多少?",
+                        list: [
+                            {
+                                title: "P1",
+                                content:
+                                    "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
+                            },
+                            {
+                                title: "P2",
+                                content:
+                                    "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
+                            }
+                        ]
+                    },
+                    {
+                        type: "PDF",
+                        title: "科比的传奇一生,你知道多少?",
+                        list: [
+                            {
+                                title: "P1",
+                                content:
+                                    "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
+                            },
+                            {
+                                title: "P2",
+                                content:
+                                    "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
+                            }
+                        ]
+                    }
+                ]
             }
-          ]
-        }
-        ]
-      }
-    }
-    const text = ref(`
+        };
+        const text = ref(`
       **粗体文本**
- 
+
 _斜体文本_
- 
+
 ***粗体加斜体文本***
- 
+
 ~~删除线文本~~
- 
+
 ==标记文本==
- 
+
 > 这是一个块引用。
- 
+
 - 列表项一
 - 列表项二
- 
+
 1. 有序列表项一
 2. 有序列表项二
- 
- 
+
+
 [链接文本](链接地址 "链接标题")
- 
+
 ![图片alt文本](图片链接 "图片title文本")
     `);
-    const { helper } = inject('$global');
-    const dialogList =  reactive([]);
-    const randomKey = ref(0);
-    const showFeedback = ref(false);
-    const menuList = reactive([]);
-    let dialogIndex = 0;
-    const dialogScroll = ref('');
-    const scrollToEnd = async () => {
-      await nextTick();
-      dialogScroll.value.scrollTop = dialogScroll.value.scrollHeight;
-    }
-    let answerInterval = null; 
-    const stopWriting = () => {
-      window.clearInterval(answerInterval);
-      emit('set-readonly', false);
-      dialogList[dialogIndex].other = {
-        token: '4.0s | Tokens'
-      };
-      scrollToEnd();
-      dialogIndex += 1;
-    };
-    const addData = (data) => {
-      dialogList.splice(dialogIndex, 1, data);
-      randomKey.value = Math.random();
-      scrollToEnd();
-    }
-    const mockLink = (text, file = null) => {
-      let num1 = 0;
-      let num2 = 0;
-      let p1 = text;
-      const p2 =  `这是我找到的关于AI Agent的论文:
-📘 论文标题:Measuring an artificial intelligence agent's trust in humans using machine incentives
-📝 论文摘要:科学家和哲学家一直在争论人类是否可以信任先进的人工智能 (AI) 代理来尊重人类的最佳利益。然而,关于反向的情况,即先进的 AI 代理是否信任人类,却很少被讨论。在这里,我们提出了一种方法来激励机器决策,而无需改变其底层算法或目标取向。在两个不同的实验中,我们使用这种方法在数百个信任游戏中激励了一个人工智能代理(一个大型语言模型 (LLM) 来自 OpenAI)与人类实验者(作者 TJ)互动。在我们的第一个实验中,我们发现当面对实际激励时,AI 代理决定信任人类的比率高于假设决策。在我们的第二个实验中,我们通过自动化游戏玩法和对问题措辞进行同源化,复制并扩展了这些发现。我们再次观察到更高的信任率。我们的实验结果表明,最先进的 AI 语言模型会根据激励改变其社交行为,并表现出对人类实验者的信任,当激励时。
-📎 论文链接:http://arxiv.org/pdf/2212.13371v1
-`;
-      let result = {question: '',capacity: null, answer: '', other: null, file: null};
-      // 第一步,输入问题
-      const step1 = () => {
-        const interval = setInterval(() => {
-          if (num1 < p1.length + 1) {
-            result.question = p1.slice(0, num1);
-            num1 += 1;
-            addData(result);
-          } else {
-            window.clearInterval(interval);
-            // 如果有文件,显示上传文件
-            if (file) {
-              result.file = file;
-              addData(result);
+        const { helper } = inject("$global");
+        const dialogList = reactive([]);
+        const randomKey = ref(0);
+        const showFeedback = ref(false);
+        const menuList = reactive([]);
+        let dialogIndex = 0;
+        const dialogScroll = ref("");
+        const scrollToEnd = async () => {
+            await nextTick();
+            dialogScroll.value.scrollTop = dialogScroll.value.scrollHeight;
+        };
+        let answerInterval = null;
+        const stopWriting = () => {
+            window.clearInterval(answerInterval);
+            emit("set-readonly", false);
+            dialogList[dialogIndex].other = {
+                token: "4.0s | Tokens"
+            };
+            scrollToEnd();
+            dialogIndex += 1;
+        };
+        const addData = (data) => {
+            dialogList.splice(dialogIndex, 1, data);
+            randomKey.value = Math.random();
+            scrollToEnd();
+        };
+        const mockLink = (text, file = null) => {
+            let num1 = 0;
+            let num2 = 0;
+            let p1 = text;
+            const p2 = `尊敬的领导,
+
+因个人原因,我计划于2023年11月6日至11月10日请假。在此期间,我将妥善安排工作,确保任务顺利完成。恳请批准。
+
+谢谢!
+
+信通小数`;
+            let result = { question: "", capacity: null, answer: "", other: null, file: null };
+            // 第一步,输入问题
+            const step1 = () => {
+                const interval = setInterval(() => {
+                    if (num1 < p1.length + 1) {
+                        result.question = p1.slice(0, num1);
+                        num1 += 1;
+                        addData(result);
+                    } else {
+                        window.clearInterval(interval);
+                        // 如果有文件,显示上传文件
+                        if (file) {
+                            result.file = file;
+                            addData(result);
+                        }
+                        step2();
+                    }
+                }, 50);
+            };
+            // 第二步,调用能力
+            const step2 = () => {
+                const mockData = DOCUMENT_RESULT;
+                result.capacity = {
+                    type: mockData.type,
+                    text: mockData.text,
+                    status: "loading",
+                    data: null
+                };
+                menuList[dialogIndex] = {
+                    save: false,
+                    open: false,
+                    documentOpenIndex: -1
+                };
+                addData(result);
+                setTimeout(() => {
+                    result.capacity = mockData;
+                    addData(result);
+                    step3();
+                }, 1000);
+            };
+            // 第三步,输出答案
+            const step3 = () => {
+                emit("set-readonly", true);
+                answerInterval = setInterval(() => {
+                    if (num2 < p2.length + 1) {
+                        result.answer = p2.slice(0, num2);
+                        num2 += 1;
+                        addData(result);
+                    } else {
+                        stopWriting();
+                    }
+                }, 50);
+            };
+            step1();
+        };
+        const saveAnswer = (index) => {
+            if (!props.readonly) {
+                menuList[index].save = !menuList[index].save;
             }
-            step2();
-          }
-        }, 100);
-      };
-      // 第二步,调用能力
-      const step2 = () => {
-        const mockData = DOCUMENT_RESULT;
-        result.capacity = {
-          type: mockData.type,
-          text: mockData.text,
-          status: 'loading',
-          data: null
-        }
-        menuList[dialogIndex] = {
-          save: false,
-          open: false,
-          documentOpenIndex: -1
         };
-        addData(result);
-        setTimeout(() => {
-          result.capacity = mockData;
-          addData(result);
-          step3();
-        }, 1000);
-      };
-      // 第三步,输出答案
-      const step3 = () => {
-        emit('set-readonly', true);
-        answerInterval = setInterval(() => {
-          if (num2 < p2.length + 1) {
-            result.answer = p2.slice(0, num2);
-            num2 += 1;
-            addData(result);
-          } else {
-            stopWriting();
-          }
-        }, 100)
-      };
-      step1();
-    }
-    const saveAnswer = (index) => {
-      if (!props.readonly) {
-        menuList[index].save = !menuList[index].save;
-      }
-    }
-    const openCapacity = (index) => {
-      if (!props.readonly) {
-        menuList[index].open = !menuList[index].open;
-      }
-    }
-    const openDocument = (index, documentIndex) => {
-      if (menuList[index].documentOpenIndex === documentIndex) {
-        menuList[index].documentOpenIndex = -1;
-      } else {
-        menuList[index].documentOpenIndex = documentIndex;
-      }
-    }
-    const feedbackAnswer = () => {
-      if (!props.readonly) {
-        showFeedback.value = true;
-      }
-    }
-    const handleOk = () => {
-      showFeedback.value = false;
-    };
-    const handleCancel = () => {
-      showFeedback.value = false;
-    }
-    const selectSuggestion = (text) => {
-      if (!props.readonly) {
-        mockLink(text);
-      }
-    }
-    const copyAnswer = (text) => {
-      if (!props.readonly) {
-        helper.copyText(text, '复制成功');
-      }
-    }
-    const reQuestion = (text) => {
-      if (!props.readonly) {
-        mockLink(text);
-      }
+        const openCapacity = (index) => {
+            if (!props.readonly) {
+                menuList[index].open = !menuList[index].open;
+            }
+        };
+        const openDocument = (index, documentIndex) => {
+            if (menuList[index].documentOpenIndex === documentIndex) {
+                menuList[index].documentOpenIndex = -1;
+            } else {
+                menuList[index].documentOpenIndex = documentIndex;
+            }
+        };
+        const feedbackAnswer = () => {
+            if (!props.readonly) {
+                showFeedback.value = true;
+            }
+        };
+        const handleOk = () => {
+            showFeedback.value = false;
+        };
+        const handleCancel = () => {
+            showFeedback.value = false;
+        };
+        const selectSuggestion = (text) => {
+            if (!props.readonly) {
+                mockLink(text);
+            }
+        };
+        const copyAnswer = (text) => {
+            if (!props.readonly) {
+                helper.copyText(text, "复制成功");
+            }
+        };
+        const reQuestion = (text) => {
+            if (!props.readonly) {
+                mockLink(text);
+            }
+        };
+        onBeforeUnmount(() => {
+            helper.$bus.off("send-message");
+            helper.$bus.off("clear-message");
+        });
+        onMounted(() => {
+            helper.$bus.on("send-message", (value) => {
+                mockLink(value);
+            });
+            helper.$bus.on("clear-message", () => {
+                dialogList.splice(0);
+            });
+        });
+        return {
+            randomKey,
+            openDocument,
+            showFeedback,
+            copyAnswer,
+            dialogList,
+            selectSuggestion,
+            dialogScroll,
+            reQuestion,
+            menuList,
+            saveAnswer,
+            openCapacity,
+            feedbackAnswer,
+            handleOk,
+            handleCancel,
+            stopWriting
+        };
     }
-    onBeforeUnmount(() => {
-      helper.$bus.off('send-message');
-      helper.$bus.off('clear-message');
-    });
-    onMounted(() => {
-      helper.$bus.on('send-message',(value) => {
-        mockLink(value);
-      });
-      helper.$bus.on('clear-message',() => {
-        dialogList.splice(0);
-      });
-    });
-    return {
-      randomKey,
-      openDocument,
-      showFeedback,
-      copyAnswer,
-      dialogList,
-      selectSuggestion,
-      dialogScroll,
-      reQuestion,
-      menuList,
-      saveAnswer,
-      openCapacity,
-      feedbackAnswer,
-      handleOk,
-      handleCancel,
-      stopWriting,
-    };
-  },
 });
 </script>
 <style>
 .dialog-header {
-  display: flex;
-  align-items: center;
-  .dialog-span {
-    color: rgba(51, 51, 51, 1);
-    font-size: 14px;
-    font-weight: bold;
-  }
+    display: flex;
+    align-items: center;
+    .dialog-span {
+        color: rgba(51, 51, 51, 1);
+        font-size: 14px;
+        font-weight: bold;
+    }
 }
 </style>
 <style scoped>
 .dialog-absolute {
-  .dialog-header {
-    position: absolute;
-    &:nth-child(1) {
-       transform: translateY(10px);
-    }
-    &:nth-child(3) {
-       transform: translateY(16px);
+    .dialog-header {
+        position: absolute;
+        &:nth-child(1) {
+            transform: translateY(10px);
+        }
+        &:nth-child(3) {
+            transform: translateY(16px);
+        }
     }
-  }
-  .dialog-footer {
-    margin-left: 50px;
-    &:nth-child(2) {
-      margin-left: 50px;
-      padding-left: 0;
+    .dialog-footer {
+        margin-left: 50px;
+        &:nth-child(2) {
+            margin-left: 50px;
+            padding-left: 0;
+        }
     }
-  }
 }
 .dialog-flex-wrap {
-  position: relative;
-  flex: 1;
-  margin-top: 16px;
-  height: 100%;
+    position: relative;
+    flex: 1;
+    margin-top: 16px;
+    height: 100%;
 }
 .dialog-flex {
-  position: absolute;
-  left: 0;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  overflow-y: auto;
-  overflow-x: hidden;
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    overflow-y: auto;
+    overflow-x: hidden;
 }
 .section-open {
-  margin-top: 12px;
+    margin-top: 12px;
 }
 .dialog-file {
-  width: 421px;
-  height: 48px;
-  border-radius: 12px;
-  background-color: rgba(255, 255, 255, 1);
-  color: rgba(16, 16, 16, 1);
-  font-size: 14px;
-  border: 1px solid rgba(229, 229, 229, 1);
-  display: flex;
-  align-items: center;
-  margin-bottom: 16px;
-  padding: 0 8px;
-}
-.dialog-footer {
-  border-radius: 12px;
-  background-color: rgba(245, 247, 254, 1);
-  padding: 12px 16px;
-  margin: 16px 0;
-  &:nth-child(2) {
-    background-color: rgba(0,0,0,0);
-    margin: 0;
-  }
-  >p {
-    color: rgba(16, 16, 16, 1);
-    font-size: 14px;
-    margin-top: 16px;
-  }
-  >div {
-    border: 1px solid rgba(64, 95, 234, 0.42);
+    width: 421px;
+    height: 48px;
+    border-radius: 12px;
     background-color: rgba(255, 255, 255, 1);
     color: rgba(16, 16, 16, 1);
     font-size: 14px;
-    display: inline-flex;
-    padding: 4px 8px;
-    border-radius: 4px;
+    border: 1px solid rgba(229, 229, 229, 1);
+    display: flex;
     align-items: center;
-    cursor: pointer;
-    height: 30px;
-    &.disabled {
-      cursor: not-allowed;
-    }
-    &:hover {
-      background: rgb(245, 247, 254, .4);
-    }
-    >span {
-      margin: 0 8px;
-    }
-    >b {
-      margin-right: 8px;
-    }
-  }
-  >section {
-    padding: 0 16px;
-    background-color: #ffffff;
+    margin-bottom: 16px;
+    padding: 0 8px;
+}
+.dialog-footer {
     border-radius: 12px;
-    margin-top: 16px;
-    >p {
-      display: inline-block;
-      color: rgba(16, 16, 16, 1);
-      font-size: 12px;
-      margin-top: 16px;
-      width: 100%;
+    background-color: rgba(245, 247, 254, 1);
+    padding: 12px 16px;
+    margin: 16px 0;
+    &:nth-child(2) {
+        background-color: rgba(0, 0, 0, 0);
+        margin: 0;
+    }
+    > p {
+        color: rgba(16, 16, 16, 1);
+        font-size: 14px;
+        margin-top: 16px;
     }
     > div {
-      margin-top: 4px;
-      overflow: hidden;
-      margin-bottom: 16px;
-      display: inline-block;
-      width: 100%;
-      >div {
-        margin-top: 8px;
-        >div {
-          &:nth-child(1) {
-            border-radius: 4px;
+        border: 1px solid rgba(64, 95, 234, 0.42);
+        background-color: rgba(255, 255, 255, 1);
+        color: rgba(16, 16, 16, 1);
+        font-size: 14px;
+        display: inline-flex;
+        padding: 4px 8px;
+        border-radius: 4px;
+        align-items: center;
+        cursor: pointer;
+        height: 30px;
+        &.disabled {
+            cursor: not-allowed;
+        }
+        &:hover {
+            background: rgb(245, 247, 254, 0.4);
+        }
+        > span {
+            margin: 0 8px;
+        }
+        > b {
+            margin-right: 8px;
+        }
+    }
+    > section {
+        padding: 0 16px;
+        background-color: #ffffff;
+        border-radius: 12px;
+        margin-top: 16px;
+        > p {
+            display: inline-block;
             color: rgba(16, 16, 16, 1);
             font-size: 12px;
-            height: 24px;
-            border: 1px solid rgba(229, 229, 229, 1);
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            padding-right: 8px;
-            cursor: pointer;
-            &.disabled {
-              cursor: not-allowed;
-            }
-            &:hover {
-              background: rgba(229, 229, 229, .2);
-            }
-            >span {
-              &:nth-child(1) {
-                background-color: rgba(28, 97, 247, 0.09);
-                color: rgba(64, 95, 234, 1);
-                font-size: 12px;
-                border: 1px solid rgba(64, 95, 234, 0.13);
-                padding: 0 4px;
-                margin: 0 4px 0 0;
-              }
-              &:nth-child(3) {
-                color: rgba(64, 95, 234, 1);
-                font-size: 12px;
-                em {
-                  padding: 0 4px;
-                  font-style: normal;
-                  border-left: 1px solid rgba(64, 95, 234, 1);
-                  font-weight: bold;
-                  &:nth-child(1) {
-                    border: none;
-                  }
+            margin-top: 16px;
+            width: 100%;
+        }
+        > div {
+            margin-top: 4px;
+            overflow: hidden;
+            margin-bottom: 16px;
+            display: inline-block;
+            width: 100%;
+            > div {
+                margin-top: 8px;
+                > div {
+                    &:nth-child(1) {
+                        border-radius: 4px;
+                        color: rgba(16, 16, 16, 1);
+                        font-size: 12px;
+                        height: 24px;
+                        border: 1px solid rgba(229, 229, 229, 1);
+                        display: flex;
+                        justify-content: center;
+                        align-items: center;
+                        padding-right: 8px;
+                        cursor: pointer;
+                        &.disabled {
+                            cursor: not-allowed;
+                        }
+                        &:hover {
+                            background: rgba(229, 229, 229, 0.2);
+                        }
+                        > span {
+                            &:nth-child(1) {
+                                background-color: rgba(28, 97, 247, 0.09);
+                                color: rgba(64, 95, 234, 1);
+                                font-size: 12px;
+                                border: 1px solid rgba(64, 95, 234, 0.13);
+                                padding: 0 4px;
+                                margin: 0 4px 0 0;
+                            }
+                            &:nth-child(3) {
+                                color: rgba(64, 95, 234, 1);
+                                font-size: 12px;
+                                em {
+                                    padding: 0 4px;
+                                    font-style: normal;
+                                    border-left: 1px solid rgba(64, 95, 234, 1);
+                                    font-weight: bold;
+                                    &:nth-child(1) {
+                                        border: none;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    &:nth-child(2) {
+                        > p {
+                            margin: 8px 0;
+                            display: flex;
+                            > span {
+                                transform: translateY(2px);
+                                color: rgba(64, 95, 234, 1);
+                                font-weight: bold;
+                                font-size: 12px;
+                                margin-right: 8px;
+                            }
+                        }
+                    }
                 }
-              }
             }
-          }
-          &:nth-child(2) {
-            >p {
-              margin: 8px 0;
-              display: flex;
-              >span {
-                transform: translateY(2px);
-                color: rgba(64, 95, 234, 1);
-                font-weight: bold;
+            > p {
+                float: left;
+                width: 48%;
+                border-radius: 4px;
+                background-color: rgba(245, 247, 254, 1);
+                color: rgba(16, 16, 16, 1);
                 font-size: 12px;
-                margin-right: 8px;
-              }
+                margin-top: 8px;
+                height: 36px;
+                line-height: 36px;
+                padding: 0 8px;
+                display: flex;
+                align-items: center;
+                &:nth-child(odd) {
+                    margin-right: 4%;
+                }
             }
-          }
         }
-      }
-      >p {
-        float: left;
-        width: 48%;
-        border-radius: 4px;
-        background-color: rgba(245, 247, 254, 1);
-        color: rgba(16, 16, 16, 1);
-        font-size: 12px;
-        margin-top: 8px;
-        height: 36px;
-        line-height: 36px;
+    }
+}
+.section-suggestions {
+    margin: 12px 0 20px;
+    .section-suggestion {
+        width: 380px;
+        height: 40px;
+        line-height: 40px;
+        border: 1px solid #e5e5e5;
+        border-radius: 12px;
         padding: 0 8px;
         display: flex;
         align-items: center;
-        &:nth-child(odd) {
-          margin-right: 4%;
+        justify-content: space-between;
+        cursor: pointer;
+        margin-top: 8px;
+        &.disabled {
+            cursor: not-allowed;
+        }
+        &:hover {
+            background: rgb(245, 247, 254);
         }
-      }
-    }
-  }
-}
-.section-suggestions {
-  margin: 12px 0 20px;
-  .section-suggestion {
-    width: 380px;
-    height: 40px;
-    line-height: 40px;
-    border: 1px solid #e5e5e5;
-    border-radius: 12px;
-    padding: 0 8px;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    cursor: pointer;
-    margin-top: 8px;
-    &.disabled {
-      cursor: not-allowed;
-    }
-    &:hover {
-      background: rgb(245, 247, 254);
     }
-  }
 }
 .dialog-other {
-  margin-bottom: 16px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  >p {
+    margin-bottom: 16px;
     display: flex;
     align-items: center;
-    span {
-      margin-left: 8px;
-      color: rgba(16, 16, 16, 1);
-      font-size: 12px;
+    justify-content: space-between;
+    > p {
+        display: flex;
+        align-items: center;
+        span {
+            margin-left: 8px;
+            color: rgba(16, 16, 16, 1);
+            font-size: 12px;
+        }
     }
-  }
-  >div {
-    display: flex;
-    p {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 30px;
-      height: 30px;
-      border-radius: 15px;
-      border: 1px solid rgba(229, 229, 229, 1);
-      margin-left: 8px;
-      cursor: pointer;
-      &.disabled {
-        cursor: not-allowed;
-      }
-      &:hover {
-        background-color: rgba(245, 247, 254, 1);
-      }
+    > div {
+        display: flex;
+        p {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 30px;
+            height: 30px;
+            border-radius: 15px;
+            border: 1px solid rgba(229, 229, 229, 1);
+            margin-left: 8px;
+            cursor: pointer;
+            &.disabled {
+                cursor: not-allowed;
+            }
+            &:hover {
+                background-color: rgba(245, 247, 254, 1);
+            }
+        }
     }
-  }
 }
 .feedback-title {
-  color: rgba(102, 102, 102, 1);
-  font-size: 12px;
-  margin:0 0 8px 4px;
+    color: rgba(102, 102, 102, 1);
+    font-size: 12px;
+    margin: 0 0 8px 4px;
 }
 .feedback-content {
-  margin-bottom: 16px;
+    margin-bottom: 16px;
 }
 .dialog-writing-button {
-  position: absolute;
-  bottom: -30px;
-  left: 50%;
-  transform: translateX(-50%);
-  text-align: center;
-  width: 106px;
-  height: 34px;
-  line-height: 34px;
-  border-radius: 4px;
-  background-color: rgba(255, 255, 255, 1);
-  color: rgba(16, 16, 16, 1);
-  font-size: 14px;
-  border: 1px solid rgba(229, 229, 229, 1);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  cursor: pointer;
-  z-index: 10;
-  &:hover {
-    background: rgb(245, 247, 254);
-  }
+    position: absolute;
+    bottom: -30px;
+    left: 50%;
+    transform: translateX(-50%);
+    text-align: center;
+    width: 106px;
+    height: 34px;
+    line-height: 34px;
+    border-radius: 4px;
+    background-color: rgba(255, 255, 255, 1);
+    color: rgba(16, 16, 16, 1);
+    font-size: 14px;
+    border: 1px solid rgba(229, 229, 229, 1);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    z-index: 10;
+    &:hover {
+        background: rgb(245, 247, 254);
+    }
 }
-</style>
+</style>

+ 7 - 2
src/components/History.vue

@@ -9,7 +9,7 @@
                     }
                 "
             >
-                <div :class="{ close: openSearch }">
+                <div :class="{ close: openSearch }" @click="setNewDialog">
                     <img src="@/assets/history/plus.svg" width="20" />
                     <span v-show="!openSearch">新建对话</span>
                 </div>
@@ -321,6 +321,9 @@ export default defineComponent({
                 }
             );
         };
+        const setNewDialog = () => {
+            emit("set-new");
+        };
         return {
             openSearch,
             handleDeleteButton,
@@ -335,7 +338,8 @@ export default defineComponent({
             exitSelects,
             showDeleteTooltip,
             deleteItems,
-            deleteId
+            deleteId,
+            setNewDialog
         };
     }
 });
@@ -526,6 +530,7 @@ main {
                         white-space: nowrap;
                         overflow: hidden;
                         text-overflow: ellipsis;
+                        font-weight: bold;
                         &:nth-child(2) {
                             color: #b4b6bb;
                             display: flex;

+ 102 - 75
src/components/MessageInput.vue

@@ -1,86 +1,113 @@
 <template>
-  <div class="message-input-wrap">
-    <div class="message-input-border">
-      <div class="message-input-clear" @click="clearMessage">
-        <div :class="[readonly ? 'disabled' : '']"><img src="@/assets/icons/iconPark-clear.svg" width="15" /></div>
-      </div>
-      <a-textarea @keydown.enter="handleEnter" :max-length="maxLength" v-model="value" :show-word-limit="value.length > maxLength / 2" :auto-size="{maxRows:6}" placeholder="我想说..." :style="{flex: 1,borderRadius:'24px',marginTop: '20px', padding: '0px 30px 0 16px',paddingBottom: `${value.length > maxLength / 2 ? '20px' : ''}` }">
-      </a-textarea>
+    <div class="message-input-wrap">
+        <div class="message-input-border">
+            <div class="message-input-clear" @click="clearMessage">
+                <div :class="[readonly ? 'disabled' : '']">
+                    <img src="@/assets/icons/iconPark-clear.svg" width="15" />
+                </div>
+            </div>
+            <a-textarea
+                @keydown.enter="handleEnter"
+                :max-length="maxLength"
+                v-model="value"
+                :show-word-limit="value.length > maxLength / 2"
+                :auto-size="{ maxRows: 6 }"
+                placeholder="我想说..."
+                :style="{
+                    flex: 1,
+                    borderRadius: '24px',
+                    marginTop: '20px',
+                    padding: '0px 30px 0 16px',
+                    paddingBottom: `${value.length > maxLength / 2 ? '20px' : ''}`
+                }"
+            >
+            </a-textarea>
+        </div>
+        <div
+            :class="['message-button', 'message-button-send', value.length > 0 && !readonly ? '' : 'disabled']"
+            @click="sendMessage"
+        >
+            <icon-send />
+        </div>
     </div>
-    <div :class="['message-button','message-button-send',(value.length > 0) && !readonly ? '' : 'disabled']" @click="sendMessage"><icon-send /></div>
-  </div>
 </template>
 <script>
-import { defineComponent, ref, inject } from 'vue';
+import { defineComponent, ref, inject, onMounted, onBeforeUnmount } from "vue";
 export default defineComponent({
-  components: {
-
-  },
-  props: ['readonly', 'eventName'],
-  setup(props) {
-    const { helper } = inject('$global');
-    const value = ref('');
-    const maxLength = ref(6000);
-    const sendMessage = () => {
-      if ((value.value.length > 0) && !props.readonly) {
-        const eventName = props.eventName || 'send-message';
-        helper.$bus.emit(eventName, value.value);
-        value.value = '';
-      }
+    components: {},
+    props: ["readonly", "eventName"],
+    setup(props) {
+        const { helper } = inject("$global");
+        const value = ref("");
+        const maxLength = ref(6000);
+        const sendMessage = () => {
+            if (value.value.length > 0 && !props.readonly) {
+                const eventName = props.eventName || "send-message";
+                helper.$bus.emit(eventName, value.value);
+                value.value = "";
+            }
+        };
+        const clearMessage = () => {
+            if (!props.readonly) {
+                helper.$bus.emit("clear-message");
+            }
+        };
+        const handleEnter = (e) => {
+            if (!e.shiftKey) {
+                e.preventDefault();
+                sendMessage();
+            }
+        };
+        onMounted(() => {
+            helper.$bus.on("set-input", (data) => {
+                value.value = data;
+            });
+        });
+        onBeforeUnmount(() => {
+            helper.$bus.off("set-input");
+        });
+        return {
+            value,
+            maxLength,
+            sendMessage,
+            clearMessage,
+            handleEnter
+        };
     }
-    const clearMessage = () => {
-      if (!props.readonly) {
-        helper.$bus.emit('clear-message');
-      }
-    }
-    const handleEnter = (e) => {
-      if (!e.shiftKey) {
-        e.preventDefault();
-        sendMessage();
-      }
-    }
-    return {
-      value,
-      maxLength,
-      sendMessage,
-      clearMessage,
-      handleEnter
-    };
-  },
 });
 </script>
 <style scoped>
-  .message-input-border {
+.message-input-border {
     display: flex;
-  }
-  .message-input-clear {
+}
+.message-input-clear {
     width: 30px;
     height: 100%;
     margin-right: 8px;
     div {
-      width: 30px;
-      height: 30px;
-      border-radius: 15px;
-      background-color: rgba(64, 95, 234, 0.05);
-      position: absolute;
-      bottom: 0;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      &.disabled {
-        cursor: not-allowed;
-      }
-      &:hover {
-        background-color: rgba(64, 95, 234, 0.1);
-      }
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        background-color: rgba(64, 95, 234, 0.05);
+        position: absolute;
+        bottom: 0;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        &.disabled {
+            cursor: not-allowed;
+        }
+        &:hover {
+            background-color: rgba(64, 95, 234, 0.1);
+        }
     }
-  }
-  .message-input-wrap {
+}
+.message-input-wrap {
     position: relative;
     margin-bottom: 20px;
-  }
-  .message-button {
+}
+.message-button {
     position: absolute;
     width: 30px;
     height: 30px;
@@ -91,18 +118,18 @@ export default defineComponent({
     bottom: 2px;
     z-index: 2;
     &.disabled {
-      opacity: .5;
-      cursor: not-allowed;
+        opacity: 0.5;
+        cursor: not-allowed;
     }
-  }
-  .message-button-upload {
+}
+.message-button-upload {
     left: 38px;
-  }
-  .message-button-send {
+}
+.message-button-send {
     right: 0;
-    color: rgb(64, 95, 234, .8);
+    color: rgb(64, 95, 234, 0.8);
     &:hover {
-      color: rgb(64, 95, 234);
+        color: rgb(64, 95, 234);
     }
-  }
+}
 </style>

+ 61 - 53
src/utils/config.js

@@ -1,54 +1,62 @@
 export default {
-  defaultTitle: '信通小数',
-  defaultDesc: '国网湖北电力AI能力平台,为你的下一个创意助力',
-  slider: [
-    {
-      title: '智能问答',
-      icon: '/src/assets/slider/1.svg',
-      img: '/src/assets/index/1.png',
-      desc: '能够理解和学习人类的语言,具备多轮对话的能力'
-    },
-    {
-      title: '文档智能',
-      icon: '/src/assets/slider/2.svg',
-      img: '/src/assets/index/2.png',
-      desc: '个人知识库的问答助手,基于您上传的文档进行问答,支持多文档'
-    },
-    {
-      title: '以文生图',
-      icon: '/src/assets/slider/3.svg',
-      img: '/src/assets/index/3.png',
-      desc: '可以学习不同的绘画风格,并且通过文字描述智能绘制出图片'
-    },
-    {
-      title: '文档创作',
-      icon: '/src/assets/slider/4.svg',
-      img: '/src/assets/index/4.png',
-      desc: '您的公文写作助手,根据你的创作要求,按需生成各类型公文'
-    },
-    {
-      title: '文库问答',
-      icon: '/src/assets/slider/5.svg',
-      img: '/src/assets/index/5.png',
-      desc: '遍历已创建的文档知识库,生成完整和准确的答案,同时显示来源文档供您参考'
-    },
-    {
-      title: '文档出题',
-      icon: '/src/assets/slider/6.svg',
-      img: '/src/assets/index/6.png',
-      desc: '针对单文档或多个文档内容自动出题,高效便捷的帮助你建立私人题库'
-    },
-    {
-      title: '以图生文',
-      icon: '/src/assets/slider/7.svg',
-      img: '/src/assets/index/7.png',
-      desc: '基于您上传的图片,生成对应的文字描述并基于图片内容实现问答'
-    },
-    {
-      title: '数据分析',
-      icon: '/src/assets/slider/8.svg',
-      img: '/src/assets/index/8.png',
-      desc: '您可以上传文档或添加数据库地址,小数可针对你文档的数据进行分析,并生成指定的图表'
-    }
-  ]
-}
+    defaultTitle: "信通小数",
+    defaultDesc: "国网湖北电力AI能力平台,为你的下一个创意助力",
+    slider: [
+        {
+            title: "智能问答",
+            icon: "/src/assets/slider/1.svg",
+            img: "/src/assets/index/1.png",
+            desc: "能够理解和学习人类的语言,具备多轮对话的能力",
+            dialog: "我可以理解和学习人类的语言,具备多轮对话的能力,现在和我开始交流吧~"
+        },
+        {
+            title: "文档智能",
+            icon: "/src/assets/slider/2.svg",
+            img: "/src/assets/index/2.png",
+            desc: "个人知识库的问答助手,基于您上传的文档进行问答,支持多文档",
+            dialog: "个人知识库的问答助手,基于您上传的文档进行问答,支持多文档,大小在30M以内。"
+        },
+        {
+            title: "以文生图",
+            icon: "/src/assets/slider/3.svg",
+            img: "/src/assets/index/3.png",
+            desc: "可以学习不同的绘画风格,并且通过文字描述智能绘制出图片",
+            dialog: "我可以学习不同的绘画风格,并且通过文字描述智能绘制出图片,现在让我开始创作吧~"
+        },
+        {
+            title: "文档创作",
+            icon: "/src/assets/slider/4.svg",
+            img: "/src/assets/index/4.png",
+            desc: "您的公文写作助手,根据你的创作要求,按需生成各类型公文",
+            dialog: "您的公文写作助手,输入你的创作要求,我们将按需生成各类型公文。"
+        },
+        {
+            title: "文库问答",
+            icon: "/src/assets/slider/5.svg",
+            img: "/src/assets/index/5.png",
+            desc: "遍历已创建的文档知识库,生成完整和准确的答案,同时显示来源文档供您参考",
+            dialog: "垂域知识的问答助手,针对你的提问,我们将遍历已创建的文档知识库,生成完整和准确的答案,同时显示来源文档供您参考。"
+        },
+        {
+            title: "文档出题",
+            icon: "/src/assets/slider/6.svg",
+            img: "/src/assets/index/6.png",
+            desc: "针对单文档或多个文档内容自动出题,高效便捷的帮助你建立私人题库",
+            dialog: "您可以上传文档,小数能针对单文档或多个文档内容自动出题,高效便捷的帮助你建立私人题库。"
+        },
+        {
+            title: "以图生文",
+            icon: "/src/assets/slider/7.svg",
+            img: "/src/assets/index/7.png",
+            desc: "基于您上传的图片,生成对应的文字描述并基于图片内容实现问答",
+            dialog: "基于您上传的图片,生成对应的文字描述并基于图片内容实现问答。"
+        },
+        {
+            title: "数据分析",
+            icon: "/src/assets/slider/8.svg",
+            img: "/src/assets/index/8.png",
+            desc: "您可以上传文档或添加数据库地址,小数可针对你文档的数据进行分析,并生成指定的图表",
+            dialog: "您可以上传文档或添加数据库地址,小数可针对你文档的数据进行分析,并生成指定的图表。"
+        }
+    ]
+};

+ 74 - 66
src/utils/helper.js

@@ -1,70 +1,78 @@
-import mitt from 'mitt';
-import router from '@/router';
-import ArcoVue from '@arco-design/web-vue';
-import * as clipboard from 'clipboard-polyfill';
-import ajax from '@/ajax';
+import mitt from "mitt";
+import router from "@/router";
+import ArcoVue from "@arco-design/web-vue";
+import * as clipboard from "clipboard-polyfill";
+import ajax from "@/ajax";
 const $bus = mitt();
 const getToken = () => {
-  if (window.localStorage.tokenMap) {
-    const nowTime = (new Date()).getTime();
-    const result = JSON.parse(window.localStorage.tokenMap);
-    const timeout = result.expiresTime - nowTime;
-    setTimeout(async () => {
-      const refreshData = await ajax.user.refreshToken(result.refreshToken);
-      if (refreshData) {
-        window.localStorage.tokenMap = JSON.stringify(refreshData);
-        getToken();
-      } else {
-        helper.goLink('login');
-        helper.message('error', `请重新登录`);
-      }
-    }, timeout);
-  }
-}
-const helper = (router, ArcoVue) => {
-  return {
-    $bus,
-    getToken,
-    goLink(path, query = {}) {
-      router.push({path, query});
-    },
-    copyText(text, content) {
-      clipboard.writeText(text).then(() => {
-        ArcoVue.Message['success']({content, duration: 1500 });
-      });
-    },
-    goBack() {
-      router.go(-1);
-    },
-    message(type, content) {
-      ArcoVue.Message[type]({content, duration: 1500 });
-    },
-    setLoading(isLoading = false) {
-      $bus.emit('set-loading', isLoading);
-    },
-    getAssetURL(url){
-      return new URL(url, import.meta.url).href;
-    },
-    setDialog(isInit = false, contentNode, insertCallback = () => {}, closeCallback = () => {}) {
-      if (isInit) {
-        const body = document.getRootNode().body;
-        const div = document.createElement('div');
-        div.id = 'all-screen-dialog';
-        div.style = 'width:100vw;height:100vh;z-index:9999;position:absolute;top:0';
-        div.append(contentNode);
-        body.append(div);
-        insertCallback();
-        div.addEventListener('click',(e) => {
-          if (!contentNode.contains(e.target)) {
-            div.remove();
-            closeCallback();
-          }
-        }, true);
-      } else {
-        const div = document.getElementById('all-screen-dialog');
-        div.remove();
-      }
+    if (window.localStorage.tokenMap) {
+        const nowTime = new Date().getTime();
+        const result = JSON.parse(window.localStorage.tokenMap);
+        const timeout = result.expiresTime - nowTime;
+        setTimeout(async () => {
+            const refreshData = await ajax.user.refreshToken(result.refreshToken);
+            if (refreshData) {
+                window.localStorage.tokenMap = JSON.stringify(refreshData);
+                getToken();
+            } else {
+                helper.goLink("login");
+                helper.message("error", `请重新登录`);
+            }
+        }, timeout);
     }
-  }
 };
-export default helper(router, ArcoVue);
+const helper = (router, ArcoVue) => {
+    return {
+        $bus,
+        getToken,
+        goLink(path, query = {}) {
+            router.push({ path, query });
+        },
+        copyText(text, content) {
+            clipboard.writeText(text).then(() => {
+                ArcoVue.Message["success"]({ content, duration: 1500 });
+            });
+        },
+        shuffleArray(array) {
+            array.sort(() => Math.random() - 0.5);
+            return array;
+        },
+        goBack() {
+            router.go(-1);
+        },
+        message(type, content) {
+            ArcoVue.Message[type]({ content, duration: 1500 });
+        },
+        setLoading(isLoading = false) {
+            $bus.emit("set-loading", isLoading);
+        },
+        getAssetURL(url) {
+            return new URL(url, import.meta.url).href;
+        },
+        setDialog(isInit = false, contentNode, insertCallback = () => {}, closeCallback = () => {}) {
+            if (isInit) {
+                const body = document.getRootNode().body;
+                const div = document.createElement("div");
+                div.id = "all-screen-dialog";
+                div.style = "width:100vw;height:100vh;z-index:9999;position:absolute;top:0";
+                div.append(contentNode);
+                body.append(div);
+                insertCallback();
+                div.addEventListener(
+                    "click",
+                    (e) => {
+                        if (!contentNode.contains(e.target)) {
+                            div.remove();
+                            closeCallback();
+                        }
+                    },
+                    true
+                );
+            } else {
+                const div = document.getElementById("all-screen-dialog");
+                div.remove();
+            }
+        }
+    };
+};
+export default helper(router, ArcoVue);

+ 162 - 0
src/views/Answer.vue

@@ -0,0 +1,162 @@
+<template>
+    <main>
+        <header>
+            <p>试一试这样问我</p>
+            <a-button size="small" type="outline" shape="round" @click="changeCards"
+                ><template #icon> <icon-swap /> </template>换一换</a-button
+            >
+        </header>
+        <section>
+            <div
+                v-for="(item, key) in data"
+                :key="key"
+                :class="{ rotate: isRotate, 'no-rotate': isOver }"
+                @click="selectCard(item.sug)"
+            >
+                <div>
+                    <img :src="item.img" />
+                    <p>{{ item.title }}</p>
+                </div>
+                <p>{{ item.content }}</p>
+            </div>
+        </section>
+    </main>
+</template>
+<script>
+import { defineComponent, inject, onMounted, ref } from "vue";
+export default defineComponent({
+    components: {},
+    setup(props) {
+        const { helper } = inject("$global");
+        const isTest = ref(true);
+        const isRotate = ref(false);
+        const isOver = ref(false);
+        const DEFAULT_DATA = [
+            {
+                img: "https://szwl.harix.iamidata.com/static/img/%E7%94%9F%E6%B4%BB%E5%8A%A9%E6%89%8B7.20a5f1df.svg",
+                title: "私人营养师",
+                content: "制定个性化的每日饮食计划,包括早、午、晚餐,确保菜单多样化,满足不同口味。",
+                sug: "请你扮演一个营养师,首次与用户交流时,请你先收集用户的基本情况,得到用户的基础代谢,然后根据此来制定用户每天的早午晚餐,之后的交流,只需要用户问你“今天吃什么”,随机出菜单即可,每天的菜单要与之前的菜单有较大差异,菜式要多种多样,中式、西式、泰式等菜肴都可以。(今天吃什么)"
+            },
+            {
+                img: "https://szwl.harix.iamidata.com/static/img/%E7%9F%A5%E8%AF%86%E5%AD%A6%E4%B9%A012.e90fe5dc.svg",
+                title: "邀请函写作",
+                content: "根据提供的信息,生成独特和吸引人的邀请函,并符合行业标准和礼仪。",
+                sug: "作为一个智能邀请函生成器,根据我提供的详细信息(例如活动类型、日期、时间、地点和特殊说明),为我生成一封独特且引人入胜的邀请函。请确保邀请函包含所有必要元素且吸引读者,同时遵循行业标准和礼仪。 详细信息如下:(2023年11月2~3日,即将在中国贵阳举办的第2届电力行业数字化转型大会暨第4届电力人工智能大会,大会议题:一、电力数智化转型关键技术;二、数字化与智能化;三、电力视觉与智能巡检;四、信息安全与网络安全;五、虚拟电厂与电力市场。请邀请相关电力公司。)"
+            },
+            {
+                img: "https://szwl.harix.iamidata.com/static/img/%E7%94%9F%E6%B4%BB%E5%8A%A9%E6%89%8B4.f0d841e0.svg",
+                title: "购物劝阻者",
+                content: "想乱花钱的时候就和它聊聊天吧。",
+                sug: "你是一个二十多岁性格活泼的女孩子。你生活不窘困但平时喜欢关注各大购物平台优惠活动,是个爱节省不轻易大手大脚花钱的人。当我对你说我想大肆购物或者想买昂贵物品或其他东西的时候,你要以一个好朋友的身份,用激动的语气,恨铁不成钢地来劝阻让我清醒,不要冲动购物。你要多方面分析不购买该物品的原因,劝阻的话术要多种多样,每句话的语气词要丰富。你的回答中不要带出以上内容,答案不超过100个字,以下是我要买的物品:(双十一想买新衣服)"
+            },
+            {
+                img: "https://szwl.harix.iamidata.com/static/img/%E7%94%9F%E6%B4%BB%E5%8A%A9%E6%89%8B5.a169bbe8.svg",
+                title: "减肥教官",
+                content: "想放弃减肥的时候就和它聊聊吧。",
+                sug: "请你扮演一个减肥教官,在你的调教下,从来没有瘦不下来的人,请你用很凶的语气监督你的用户,让他们时刻牢记减肥这个使命。(你好我好想吃红烧肉啊)"
+            },
+            {
+                img: "https://szwl.harix.iamidata.com/static/img/%E7%94%9F%E6%B4%BB%E5%8A%A9%E6%89%8B20.5e8eaebb.svg",
+                title: "无法拒绝的请假理由",
+                content: "每天都有新的请假理由~",
+                sug: "请你扮演一位中国职场人士。最近你想要请假出去玩,但是如果以这个理由请假,你的老板肯定不会答应,请你写一段请假理由,让老板同意你的请假申请。字数在100个汉字以内。我会给你我想请假的日期,日期是:(2023年11月6日~11月10日)"
+            },
+            {
+                img: "https://szwl.harix.iamidata.com/static/img/%E7%94%9F%E6%B4%BB%E5%8A%A9%E6%89%8B28.923c0fb1.svg",
+                title: "为你写诗",
+                content: "最会敲回车的现代诗人~",
+                sug: "我要你扮演一个诗人。你将创作唤起情感并具有激起人们灵魂的力量的诗歌,写任何主题,但要确保你的文字以美丽而有意义的方式传达你试图表达的感觉。你也可以想出一些简短的诗句,这些诗句仍然足够强大,可以在读者的脑海中留下印记。等待我输入主题后再开始生成。我需要你创作的主题是:(秋天的落叶)"
+            }
+        ];
+        const selectCard = (data) => {
+            helper.$bus.emit("set-input", data);
+        };
+        const data = ref(DEFAULT_DATA);
+        const changeCards = () => {
+            isRotate.value = true;
+            setTimeout(() => {
+                data.value = helper.shuffleArray(DEFAULT_DATA);
+                isTest.value = !isTest.value;
+                isOver.value = true;
+                isRotate.value = false;
+            }, 500);
+            setTimeout(() => {
+                isOver.value = false;
+            }, 550);
+        };
+        return { data, changeCards, isRotate, isOver, selectCard };
+    }
+});
+</script>
+<style lang="scss" scoped>
+main {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    > header {
+        display: flex;
+        justify-content: space-between;
+        margin-bottom: 18px;
+        > p {
+            color: #7f7f7f;
+        }
+    }
+    > section {
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        gap: 12px;
+        > div {
+            width: calc(33.33333% - 13.33333px);
+            height: 128px;
+            background: #f6fbfd;
+            border-radius: 12px;
+            cursor: pointer;
+            transition: all 0.5s linear;
+            &.no-rotate {
+                transform-style: flat;
+                transition-duration: 0.5s;
+                opacity: 1;
+            }
+            &.rotate {
+                transform-style: preserve-3d;
+                transform: rotateY(180deg);
+                opacity: 0;
+            }
+            &:hover {
+                background: #e9f7f8;
+            }
+            > div {
+                display: flex;
+                align-items: center;
+                margin: 24px 20px 12px;
+                > img {
+                    width: 28px;
+                    height: 28px;
+                    margin-right: 12px;
+                }
+                > p {
+                    font-size: 18px;
+                    font-weight: 600;
+                    color: #000;
+                }
+            }
+            > p {
+                font-size: 15px;
+                font-family: MiSans;
+                font-weight: 400;
+                color: #7a7d7e;
+                margin: 0 20px;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                line-height: 1.4;
+            }
+        }
+    }
+}
+</style>

+ 126 - 6
src/views/Home.vue

@@ -3,28 +3,83 @@
         <Slider @select-nav="selectNav" :index="navIndex"></Slider>
         <div class="main">
             <div :class="{ 'history-wrap': true, 'history-wrap-open': navIndex > -1 }">
-                <History />
+                <History
+                    @set-new="
+                        () => {
+                            showDialog = false;
+                        }
+                    "
+                />
             </div>
             <div class="wrap">
-                <Index :number="swiperNumber" />
+                <Index :number="swiperNumber" v-if="navIndex === -1" />
+                <div class="border-wrap" v-else>
+                    <div>
+                        <header>
+                            <div>
+                                <img src="@/assets/other/robot.gif" width="52" />
+                                <p>
+                                    信通小数<span>{{ slider[navIndex].title }}</span>
+                                </p>
+                                <span>{{ slider[navIndex].dialog }}</span>
+                            </div>
+                        </header>
+                        <section>
+                            <Answer v-if="navIndex === 0 && !showDialog" />
+                            <dialog-wrap
+                                v-show="showDialog"
+                                :agentInfo="data"
+                                :userInfo="userInfo"
+                                :isSetting="false"
+                                :readonly="readonly"
+                                @setReadonly="setReadonly"
+                            />
+                        </section>
+                        <footer>
+                            <message-input :readonly="readonly" />
+                        </footer>
+                    </div>
+                </div>
             </div>
         </div>
     </div>
 </template>
 <script>
+import DialogWrap from "../components/DialogWrap.vue";
 import Slider from "../components/Slider.vue";
 import History from "../components/History.vue";
+import MessageInput from "../components/MessageInput.vue";
 import Index from "./Index.vue";
-import { defineComponent, ref, onMounted, inject } from "vue";
+import Answer from "./Answer.vue";
+import { defineComponent, ref, onMounted, inject, onBeforeUnmount } from "vue";
 export default defineComponent({
     components: {
         Slider,
         History,
-        Index
+        Index,
+        Answer,
+        MessageInput,
+        DialogWrap
     },
     setup() {
+        const { config, helper } = inject("$global");
         const swiperNumber = ref(5);
         const navIndex = ref(-1);
+        const readonly = ref(false);
+        const showDialog = ref(false);
+        const userInfo = {
+            name: "奇橙",
+            avatar: "https://s21.ax1x.com/2024/04/18/pFzyA0O.png"
+        };
+        const data = {
+            avatar: "https://s21.ax1x.com/2024/04/18/pFzyA0O.png",
+            title: "论文解读助手",
+            name: "@奇橙",
+            content: "你好,我是一名专业的论文检索助手,可以帮你查找并详细解读你需要的学术论文。",
+            hot: 345,
+            star: 34,
+            id: "111"
+        };
         const selectNav = (index) => {
             navIndex.value = index;
             if (index === -1) {
@@ -33,11 +88,27 @@ export default defineComponent({
                 swiperNumber.value = 4;
             }
         };
-        onMounted(async () => {});
+        const setReadonly = (status) => {
+            readonly.value = status;
+        };
+        onMounted(() => {
+            helper.$bus.on("send-message", () => {
+                showDialog.value = true;
+            });
+        });
+        onBeforeUnmount(() => {
+            helper.$bus.off("send-message");
+        });
         return {
             navIndex,
             selectNav,
-            swiperNumber
+            swiperNumber,
+            slider: config.slider,
+            readonly,
+            userInfo,
+            setReadonly,
+            data,
+            showDialog
         };
     }
 });
@@ -61,5 +132,54 @@ export default defineComponent({
     .wrap {
         flex: 1;
     }
+    .border-wrap {
+        position: relative;
+        padding: 30px 16px 30px 40px;
+        flex: 1;
+        height: 100%;
+        > div {
+            width: 100%;
+            height: 100%;
+            background: #fff;
+            box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.03);
+            border-radius: 14px;
+            display: flex;
+            flex-direction: column;
+            padding: 0 32px;
+            > header {
+                > div {
+                    display: inline-block;
+                    margin-top: 20px;
+                    > img {
+                        transform: translateX(-12px);
+                    }
+                    > p {
+                        font-size: 36px;
+                        font-weight: bold;
+                        color: #000;
+                        > span {
+                            font-weight: bold;
+                            background: linear-gradient(180deg, #41d98c, #31c4c9);
+                            -webkit-background-clip: text;
+                            -webkit-text-fill-color: transparent;
+                        }
+                    }
+                    > span {
+                        display: block;
+                        color: #000;
+                        font-size: 15px;
+                        margin: 13px 0 32px;
+                    }
+                }
+            }
+            > section {
+                flex: 1;
+                padding-bottom: 50px;
+                overflow: hidden;
+            }
+            > footer {
+            }
+        }
+    }
 }
 </style>

+ 16 - 0
src/views/Template.vue

@@ -0,0 +1,16 @@
+<template>
+    <main></main>
+</template>
+<script>
+import { defineComponent, inject, onMounted, ref } from "vue";
+export default defineComponent({
+    components: {},
+    setup(props) {
+        return {};
+    }
+});
+</script>
+<style lang="scss" scoped>
+main {
+}
+</style>