yinbangzhong
2024-09-12 ff0ae974a1c561eaf2915988dac63cd066af189f
src/views/sessionManager/index.vue
@@ -78,61 +78,172 @@
                  />
                </template>
                <template #content>
                  <div :class="{ chartUserText: theme === 'light' }"
                    >{{ sessionDetail.content }}
                  <div :class="{ chartUserText: theme === 'light' }">
                    {{ sessionDetail.content }}
                    <!-- <a-input
                      :style="{ width: '100%' }"
                      v-model="sessionDetail.content"
                      v-if="isEdit"
                    />
                    <div v-else>
                      {{ sessionDetail.content }}
                    </div> -->
                  </div>
                  <!-- <div v-if="!isEdit">
                    <span
                      class="action"
                      v-if="index != 0"
                      @click="copy(sessionDetail.content)"
                    >
                      <icon-copy /> 复制
                    </span>
                    <span class="action" @click="edit()">
                      <icon-pen /> 编辑
                    </span>
                  </div>
                  <a-space v-else>
                    <a-button type="outline" size="mini" @click="cancelEdit(1)"
                      >取消</a-button
                    >
                    <a-button type="primary" size="mini" @click="cancelEdit(2)"
                      >确定</a-button
                    >
                  </a-space> -->
                </template>
              </a-comment>
              <a-comment v-else-if="sessionDetail.role === 'assistant'">
                <template #avatar>
                  <img
                    class="icon-user-jpg"
                    src="../../assets/images/icon-chart.png"
                    src="../../assets/images/icon-picture.png"
                    alt="本地图片"
                  />
                </template>
                <template #content>
                  <a-card v-if="isExistTip(sessionDetail.content)">
                    <div  :class="{ chatItemAnswer: theme === 'light' }"
                      :style="{backgroundColor: theme === 'light' ? '#ffffff' : '#000000'}" style="border: none;display: inline;"  v-for="(item,tipIndex) in tipMatch(sessionDetail)"
                  <a-card v-if="isExistTip(sessionDetail.content)" style="padding: 10px">
                    <div
                      :class="{ chatItemAnswer: theme === 'light' }"
                      :style="{
                        backgroundColor:
                          theme === 'light' ? '#ffffff' : '#000000',
                      }"
                      style="border: none"
                      v-for="(breakContent, breakIndex) in breakLine(
                        sessionDetail.content
                      )"
                    >
                      <span v-if="tipIndex == 0">
                        {{ sessionDetail.content.substring(0, item.index) }}
                        <a-tooltip background-color="#3491FA" :content="getTipContent(
                          messagenList.reference,
                          index
                        )">
                          <img style="width: 20px;height: 20px" :src="tipImage" />
                        </a-tooltip>
                      </span>
                      <span v-else>
                        {{ sessionDetail.content.substring(item.preIndex+item.item.length, item.index) }}
                        <a-tooltip background-color="#3491FA" :content="getTipContent(
                          messagenList.reference,
                          index
                        )">
                          <img style="width: 20px;height: 20px" :src="tipImage" />
                        </a-tooltip>
                      </span>
                      <div
                        :class="{ chatItemAnswer: theme === 'light' }"
                        :style="{
                          backgroundColor:
                            theme === 'light' ? '#ffffff' : '#000000',
                        }"
                        style="border: none"
                        v-if="isExistTip(breakContent)"
                      >
                        <div
                          :class="{ chatItemAnswer: theme === 'light' }"
                          :style="{
                            backgroundColor:
                              theme === 'light' ? '#ffffff' : '#000000',
                          }"
                          style="border: none"
                          v-for="(item, tipIndex) in tipMatch(breakContent)"
                        >
                          <span v-if="tipIndex == 0">
                            {{ breakContent.substring(0, item.index) }}
                            <a-tooltip
                              background-color="#3491FA"
                              :content="
                                getTipContent(messagenList.reference, index)
                              "
                            >
                              <img
                                style="width: 15px; height: 15px"
                                :src="tipImage"
                              />
                            </a-tooltip>
                            <span v-if="tipIndex == item.len - 1">
                              {{ breakContent.substring(item.index + 5) }}
                            </span>
                          </span>
                          <span v-else-if="tipIndex == item.len - 1">
                            {{
                              breakContent.substring(
                                item.preIndex + item.item.length,
                                item.index
                              )
                            }}
                            <a-tooltip
                              background-color="#3491FA"
                              :content="
                                getTipContent(messagenList.reference, index)
                              "
                            >
                              <img
                                style="width: 15px; height: 15px"
                                :src="tipImage"
                              />
                            </a-tooltip>
                            {{ breakContent.substring(item.index + 5) }}
                          </span>
                          <span v-else>
                            {{
                              breakContent.substring(
                                item.preIndex + item.item.length,
                                item.index
                              )
                            }}
                            <a-tooltip
                              background-color="#3491FA"
                              :content="
                                getTipContent(messagenList.reference, index)
                              "
                            >
                              <img
                                style="width: 15px; height: 15px"
                                :src="tipImage"
                              />
                            </a-tooltip>
                          </span>
                        </div>
                      </div>
                      <div v-else>
                        {{ breakContent }}
                      </div>
                    </div>
                  </a-card>
                  <a-card  v-else>
                    <div :class="{ chatItemAnswer: theme === 'light' }"
                         :style="{
                      backgroundColor:
                        theme === 'light' ? '#ffffff' : '#000000',
                    }" style="border: none">
                      {{ sessionDetail.content }}
                    </div>
                  <a-card v-else>
                    <a-textarea
                      readonly
                      auto-size
                      :default-value="chartText(sessionDetail.content)"
                      :class="{ chatItemAnswer: theme === 'light' }"
                      :style="{
                        backgroundColor:
                          theme === 'light' ? '#ffffff' : '#000000',
                      }"
                      style="border: none"
                    >
                    </a-textarea>
                  </a-card>
                  <!-- <div class="prompt">
                    <ul>
                      <li class="prompt-item" @click="copyText('43234')">
                        <span>
                          344343klsdjkjksdjkjksdjk就开始大家看数据库登记卡受打击凯撒登记卡受打击凯撒登记卡受打击凯撒的43</span
                        >
                        <span style="margin-left: 20px">
                          <icon-right />
                        </span>
                      </li>
                    </ul>
                  </div> -->
                </template>
                <!-- <div>{{ sessionDetail.role === 'assistant' }}</div> -->
                <template #actions>
                  <!-- <div
                    v-for="(item, leng) in messagenList.reference"
                    :key="leng"
                  > -->
                  <div>
                    <div>
                      <template
@@ -168,38 +279,36 @@
                  >
                    <icon-refresh /> 重新生成
                  </span>
                  <!-- <span class="action"><icon-to-bottom />下载 </span> -->
                </template>
              </a-comment>
              <a-comment v-else-if="sessionDetail.role === 'last'">
                <template #avatar>
                  <img
                    class="icon-user-jpg"
                    src="../../assets/images/icon-chart.png"
                    src="../../assets/images/icon-picture.png"
                    alt="本地图片"
                  />
                </template>
                <template #content>
                  <a-textarea
                    readonly
                    auto-size
                    v-model="displayedText"
                    :class="{ chatItemAnswer: theme === 'light' }"
                    :style="{
                      backgroundColor:
                        theme === 'light' ? '#ffffff' : '#000000',
                    }"
                    style="border: none"
                  >
                  </a-textarea>
                  <a-spin :loading="chartLoading" dot style="width: 100%">
                    <a-textarea
                      readonly
                      auto-size
                      v-model="displayedText"
                      :class="{ chatItemAnswer: theme === 'light' }"
                      :style="{
                        backgroundColor:
                          theme === 'light' ? '#ffffff' : '#000000',
                      }"
                      style="border: none"
                    >
                    </a-textarea>
                  </a-spin>
                </template>
                <template #actions>
                  <!-- <div class="icon-box">
                    <div>
                      <img :src="getIconByExtension('pdf')" alt="" />
                    </div>
                    <div class="icon-text"> 文件类型 </div>
                  </div> -->
                  <div
                    class="action"
                    @click="stopChat"
@@ -215,24 +324,12 @@
                </template>
              </a-comment>
            </div>
            <!-- <div class="chartStart" v-if="isStart" @click="startChat"
              >停止生成</div
            >
            <div class="chartStart v-else" @click="stopChat">重新生成</div> -->
          </a-scrollbar>
          <div class="chat_bottom">
            <div class="center-bottom">
              <!-- <a-textarea
              v-model="inputMsg"
              @keydown.shift.enter="sendMessage"
              style="height: 180px"
              placeholder="输入您想了解的内容,Shift+Enter发送"
              :max-length="500"
              allow-clear
              show-word-limit
            >
            </a-textarea> -->
              <a-textarea
                v-model="inputMsg"
                @keydown.shift.enter="handleShiftEnter"
@@ -261,12 +358,15 @@
                  display: flex;
                  justify-content: space-between;
                "
                class="uploadFileDis"
              >
                <updataFile
                  ref="fileInput"
                  :sessionId="activeSessionId"
                  :hide="hide_upload"
                  @selectFileCallback="selectFileCallback"
                ></updataFile>
                <a-button
                  :disabled="chatDis"
                  @click="sentClick"
@@ -354,12 +454,17 @@
          ></smartAi>
        </div>
      </a-col>
      <a-col :span="23" v-show="agentType == '5'">
        <div class="center">
          <seniorAgentSession :modalObj="agentObj"></seniorAgentSession>
        </div>
      </a-col>
    </a-row>
    <a-modal title=" " v-model:visible="fileVisible" :footer="false" fullscreen>
      <!--      <docx  previewSrc="http://192.168.20.116:1080/v1/document/get/405c3efa4d8c11ef97560242ac120006"></docx>-->
      <docx v-if="documenttype == 'docx'" :previewSrc="previewSrc"></docx>
      <excel v-if="documenttype == 'excel'" :previewSrc="previewSrc"></excel>
      <txtPdf v-if="documenttype == 'txtPdf'" :previewSrc="previewSrc"></txtPdf>
      <excel v-if="documenttype == ('xlsx' || 'xls')" :previewSrc="previewSrc"></excel>
      <Pdf v-if="documenttype == 'pdf'" :previewSrc="previewSrc"></Pdf>
      <txt  v-if="documenttype == 'txt'" :previewSrc="previewSrc"></txt>
    </a-modal>
    <a-modal
      v-model:visible="visible"
@@ -389,7 +494,7 @@
  </div>
</template>
<script setup lang="ts">
import { useAppStore, userModelState, useUserStore } from "@/store";
  import { useAppStore, userModelState, useUserStore } from '@/store';
  import {
    computed,
    nextTick,
@@ -407,9 +512,11 @@
  import chatMenu from '@/views/sessionManager/components/chatMenu.vue';
  import AddSession from '@/views/sessionManager/components/addSession.vue';
  import agentSession from '@/views/sessionManager/components/agentSession.vue';
  import seniorAgentSession from '@/views/sessionManager/components/seniorAgentSession.vue';
  import historySession from '@/views/sessionManager/components/historySession.vue';
  import smartAi from '@/views/sessionManager/components/smartAi.vue';
  import updataFile from '@/views/sessionManager/components/updataFile.vue';
  import EventBus from '@/utils/EventBus';
  import {
    addSessionApi,
@@ -418,21 +525,27 @@
    getSessionDetailsApi,
    sessionListApi,
    uploadWithoutKb,
    uploadAndParse,
    chatInfos,
    chatRm,
  } from '@/api/session';
import { getAuthorization } from "@/utils/auth";
import { queryCanvasList } from "@/api/Agent";
import { getAgentSessionDetailsApi } from "@/api/agentSession";
import useClipboard from "vue-clipboard3";
import pdfImg1 from "@/assets/session/PDF.png";
import pdfImg2 from "@/assets/session/wps-write.png";
import pdfImg3 from "@/assets/session/execl.png";
import pdfImg4 from "@/assets/session/icon-txt.png";
import pdfImg5 from "@/assets/session/txt.png";
import tipImage from "@/assets/session/tip.png";
  import { getAuthorization } from '@/utils/auth';
  import { queryCanvasList } from '@/api/Agent';
  import { getAgentSessionDetailsApi } from '@/api/agentSession';
  import useClipboard from 'vue-clipboard3';
  import pdfImg1 from '@/assets/session/PDF.png';
  import pdfImg2 from '@/assets/session/wps-write.png';
  import pdfImg3 from '@/assets/session/execl.png';
  import pdfImg4 from '@/assets/session/icon-txt.png';
  import pdfImg5 from '@/assets/session/txt.png';
  import tipImage from '@/assets/session/tip.png';
import docx from "@/views/dmx/knowledgeLib/components/docx.vue";
import excel from "@/views/dmx/knowledgeLib/components/excel.vue";
import txtPdf from "@/views/dmx/knowledgeLib/components/txtPdf.vue";
  import docx from '@/views/dmx/knowledgeLib/components/docx.vue';
  import excel from '@/views/dmx/knowledgeLib/components/excel.vue';
  import Pdf from '@/views/dmx/knowledgeLib/components/Pdf.vue';
  import txt from '@/views/dmx/knowledgeLib/components/txt.vue';
  import { is } from 'immutable';
  import uploadFile from './components/uploadFile.vue';
  // const url = ref('../../assets/session/PDF.png');
@@ -447,6 +560,7 @@
  const modalObj = reactive({ add: false });
  const dialogId = ref('');
  const chatDis = ref(false);
  const chartLoading = ref(false);
  const loading = ref(false);
  const agentType = ref('1');
  const agentTitle = ref('未命名会话');
@@ -454,13 +568,14 @@
  let from = reactive({
    name: '未命名会话',
  });
  const isStopChat = ref(false);
  const currIndex = ref(0);
  const displayedText = ref(''); // 正在显示的文字
  let timer: number | null = null;
  const streamStr = ref('');
  const inputMsg = ref('');
  const activeSessionId = ref('');
  const hide_upload=ref(2);
  const fieldNames = { value: 'id', label: 'name' };
  const dialogs = ref([]);
  const dialogObj = reactive({});
@@ -473,7 +588,7 @@
  const uploadRef = ref();
  const files = ref([]);
  const file = ref('');
  const fileInput = ref(null);
  const fileInput = ref();
  const chatDataMeg = reactive({});
  const visible = ref(false);
  const fileVisible = ref(false);
@@ -481,8 +596,7 @@
  let documenttype = ref('docx');
  let previewSrc = ref('');
const modelStore = userModelState();
  const modelStore = userModelState();
  const httpUrl = modelStore.hrefUrl;
  const rules = {
@@ -498,41 +612,69 @@
    if (errors) return;
    let chatData = {
      id: chatObj.id,
      conversation_id: chatObj.id,
      name: from.name,
      dialog_id: chatObj.dialog_id,
      conversation_desc: from.name,
    };
    const { code, data } = await addSessionApi(chatData);
    if (data) {
      Message.success('修改成功');
      handleCancel();
      if (data?.id) {
        activeSessionId.value = data.id;
      }
      queryNewSessionDetail(activeSessionId.value);
    }
  };
  let dataItem = [];
  const isExistTip=(message:string):boolean=>{
  const isExistTip = (message: string): boolean => {
    if (/##[0-9]\$\$/.test(message)) {
      return true;
    } else {
      return false;
    }
  }
  };
  const tipMatch = (session: any): any[] => {
  const breakLine = (message: string): string[] => {
    //按\n分割字符串
    let arr = message.split('\n');
    return arr;
  };
  const chartText = (message: string): string => {
    //去除所有的#和**
    let arr = message.replace(/\*\*|\#\#\#/g, '');
    return arr;
  };
  const tipMatch = (msg: string): any[] => {
    //match session.content中的##[0-9]$$的索引
    let indexs: any[] = [];
    let preTip=0;
    session.content.match(/##([0-9]+)\$\$/g)?.map((item) => {
      let i = session.content.indexOf(item);
      indexs.push({ index: i, item: item, preIndex: preTip });
    let preTip = 0;
    let matches = msg.match(/##([0-9]+)\$\$/g);
    matches?.map((item) => {
      let i = msg.indexOf(item);
      indexs.push({
        index: i,
        item: item,
        preIndex: preTip,
        len: matches.length,
      });
      preTip = i;
    });
    return indexs;
  }
  };
  const getTxt = (data, role, message, index) => {
    if (isExistTip(message)) {
      if (role == 'assistant' && index) {
        let i = index / 2 - 1 > 0 ? index / 2 - 1 : 0;
        if (data.length == 1) {
          i = 0;
        }
        if (data.length == i) {
          i = data.length-1;
        }
        dataItem = data[i].doc_aggs;
      }
    } else {
@@ -543,22 +685,27 @@
  };
  const getTipContent = (data, index): string => {
    let maxSimilarityContent = "";
    let maxSimilarityContent = '';
    let i = index / 2 - 1 > 0 ? index / 2 - 1 : 0;
    if (data.length == 1) {
      i = 0;
    }
    if (data.length == i) {
      i = data.length-1;
    }
    let maxSimilarity = 0;
    data[i].chunks.forEach((chunk) => {
        if (chunk.similarity > maxSimilarity) {
          maxSimilarity = chunk.similarity;
          maxSimilarityContent = chunk.content_with_weight;
        }
      if (chunk.similarity > maxSimilarity) {
        maxSimilarity = chunk.similarity;
        maxSimilarityContent = chunk.content_with_weight;
      }
    );
    });
    return maxSimilarityContent;
  };
  const clickHref = async (item) => {
    documenttype.value = item.doc_name.split('.').pop();
    previewSrc.value = httpUrl + `/api/v1/document/get/${item.doc_id}`;
    previewSrc.value = httpUrl + `/api/v1/document/get/${item.doc_id}`+"?t="+new Date().getTime();
    fileVisible.value = true;
  };
@@ -621,12 +768,29 @@
    // formRef.value.resetFields();
  };
  const selectFileCallback = (data) => {
    console.log(data, 'selectFileCallback');
    uploaditemList.value = [
      ...uploaditemList.value,
      ...data
    ];
  const selectFileCallback = (data,conversation_id) => {
    uploaditemList.value = [...uploaditemList.value, ...data];
    activeSessionId.value = conversation_id;
  };
  //上传成功解析getinfo
  const getInfo = async (id) => {
    const { code, data } = await chatInfos({
      doc_ids: activeSessionId.value,
    });
    if (code === 200) {
      uploaditemList.value = data;
    }
  };
  //删除上传文档
  const deleteUpload = async (id) => {
    const { code, data } = await chatRm({
      doc_id: id,
    });
    if (code === 200) {
      getInfo();
    }
  };
  let onFileSelectedLoading = ref(false);
@@ -634,11 +798,25 @@
  const deleteFile = (item) => {
    console.log(uploaditemList.value);
    uploaditemList.value.splice(item.index, 1);
    // EventBus.emit('queryAgent', item);
  };
  const { toClipboard } = useClipboard();
  const copy = async (text) => {
    await toClipboard(text); //参数为要复制的文本
  };
  const isEdit = ref(false);
  const edit = () => {
    isEdit.value = !isEdit.value;
  };
  const cancelEdit = (val) => {
    if (val == 1) {
      isEdit.value = !isEdit.value;
    } else {
      //编辑保存
      isEdit.value = !isEdit.value;
    }
  };
  const DialogList = async () => {
@@ -710,7 +888,9 @@
  // 发送
  const sentClick = () => {
    displayedText.value="";
    sendMessage('click');
    uploaditemList.value = [];
  };
  // 重新生成
  const reGenerate = () => {
@@ -744,7 +924,9 @@
            if (res.code == 200) {
              // console.log(res.data.conversation_id);
              activeSessionId.value = res.data?.conversation_id;
              const { code, data } = await getSessionDetailsApi(res.data?.conversation_id);
              const { code, data } = await getSessionDetailsApi(
                res.data?.conversation_id
              );
              if (code === 200) {
                console.log(data, '新建会话详情');
                Object.assign(chatObj, data);
@@ -765,10 +947,14 @@
    }
  };
  let message_stop = [];
  let message_id = "";
  const startChat = async (valMsg) => {
    chatDis.value = true;
    loading.value = true;
    toStop = false;
    displayedText.value = '';
    chartLoading.value = true;
    sessionDetailList.value.push({
      content: valMsg,
      role: 'user',
@@ -797,6 +983,7 @@
    currIndex.value = 0;
    while (true) {
      if (toStop) {
        message_stop.push(message_id);
        displayedText.value = '';
        setChatDataMeg(chatDataMeg);
        break;
@@ -808,9 +995,20 @@
        try {
          const val = JSON.parse(value?.data || '');
          const d = val?.data;
          if (message_id != d.message_id) {
            message_id = d.message_id;
            message_stop = [];
          } else {
            //message_stop中查找message_id
            const index = message_stop.findIndex(item => item === message_id);
            if (index > -1) {
              break;
            }
          }
          if (typeof d !== 'boolean') {
            // console.info("data:", d);
            streamStr.value = d.content;
            chartLoading.value = false;
            startDisplayStr();
          }
        } catch (e) {
@@ -819,12 +1017,9 @@
        if (done) {
          console.info('done');
          displayedText.value = '';
          if (isStopChat.value) {
            setChatDataMeg(chatDataMeg);
          } else {
            queryNewSessionDetail(activeSessionId.value);
            EventBus.emit('queryAppUsageList');
          }
          chartLoading.value = false;
          queryNewSessionDetail(activeSessionId.value);
          EventBus.emit('queryAppUsageList');
          break;
        }
      }
@@ -835,14 +1030,8 @@
  };
  const stopChat = async () => {
    // const { code, data } = await stopChatApi(activeSessionId.value);
    // if (code === 200) {
    //   Message.success("已停止");
    //   queryNewSessionDetail(activeSessionId.value);
    // }
    loading.value = false;
    chatDis.value = false;
    isStopChat.value = true;
    toStop = true;
    console.log('stopChat');
    console.log(displayedText.value, 'displayedText');
@@ -865,7 +1054,9 @@
      .concat(lastArr);
    console.log(sessionDetailList.value, 'sessionDetailList2');
    console.log(chatObj, 'chatObj对象');
    chatObj.message = chatObj.message.concat(lastArr);
    if (chatObj?.message) {
      chatObj.message = chatObj.message.concat(lastArr);
    }
    Object.assign(chatDataMeg, {
      id: chatObj.id,
      conversation_id: chatObj.id,
@@ -895,11 +1086,12 @@
      agentTitle.value = data.name;
      from.name = data.name;
      refreshScroll(); //刷新滚动条位置
      isStopChat.value = false;
    }
  };
  const changeAgentType = (val, session) => {
    hide_upload.value = hide_upload.value+1;
    uploaditemList.value=[];
    agentType.value = val;
    console.log(val, 'val');
  };
@@ -910,6 +1102,7 @@
    from.name = session.name;
    const { code, data } = await getSessionDetailsApi(session.id);
    if (code === 200) {
      Object.assign(chatObj, data);
      sessionDetailList.value = data.message;
      messagenList.value = data;
      refreshScroll(); //刷新滚动条位置
@@ -961,12 +1154,16 @@
    activeSessionId.value = '';
  });
  onMounted(() => {
    document.getElementsByTagName;
    let container = document.getElementById('container');
    container.addEventListener('click', () => {
      fileInput.value.cancel();
      // fileInput.value.cancel();
    });
    EventBus.on('newChat', () => {
      uploaditemList.value = [];
      agentType.value = '1';
      chatObj = {};
      hide_upload.value = hide_upload.value+1;
      // createSession('');
      activeSessionId.value = '';
      sessionDetailList.value = [
@@ -1066,6 +1263,33 @@
            font-size: 12px;
            padding-left: 10px;
          }
        }
      }
    }
    // .uploadFileDis {
    //   :deep(.arco-upload-list-type-text) {
    //     display: none;
    //   }
    // }
    .prompt {
      ul {
        margin: 0;
        padding: 0;
        display: flex;
      }
      ul > li {
        list-style-type: none;
      }
      .prompt-item {
        display: flex;
        justify-content: space-between;
        background-color: #fff;
        cursor: pointer;
        border-radius: 8px;
        padding: 10px;
        border: #e5e5e5;
        &:hover {
          background-color: #eee;
        }
      }
    }
@@ -1202,6 +1426,11 @@
        }
      }
    }
    .uploadFileDis {
      :deep(.arco-upload-list-type-text) {
        display: none;
      }
    }
    .chat-item {
      padding: 10px 0;
@@ -1211,6 +1440,19 @@
        font-size: 14px;
        color: #333;
        margin-top: 4px;
      }
      :deep(.arco-card-body) {
        padding: 0;
        border-radius: 10px;
      }
      :deep(.arco-textarea) {
        padding-top: 10px !important;
      }
      :deep(.arco-comment-inner-content) {
        border-radius: 10px;
      }
      :deep(.arco-card-bordered) {
        border-radius: 10px;
      }
      .icon-user-jpg {
@@ -1230,6 +1472,9 @@
          margin-left: 10px;
        }
      }
      :deep(.arco-spin-loading .arco-spin-mask-icon) {
        left: 10%;
      }
    }
    .right {