<template>
  <div ref="aiChatRef" class="ai-chat">
    <el-scrollbar ref="scrollDiv" @scroll="handleScrollTop">
      <div ref="dialogScrollbar" class="ai-chat__content p-24 chat-width" style="padding: 24px;">
        <div class="item-content mb-16" style="margin-bottom: 16px">
<!--          <div class="avatar">-->
<!--            <img src="@/assets/logo/logo2.png" height="30px" />-->
<!--          </div>-->
          <div class="content">
            <span style="font-size: 14px">您好，我是 果知小助手，您可以向我提出关于 果知的相关问题。</span>
          </div>
        </div>

        <template v-for="(item, index) in chatList" :key="index">
          <!-- 问题 -->
          <div v-if="item.type === 'question'"  class="item-content mb-16 lighter" style="font-weight: 400;display: flex;align-items: flex-start;max-width: 100%;flex-direction: row-reverse;">
<!--            <div class="avatar" style="margin-left: 10px">-->
<!--              <el-avatar style="width:30px;height: 30px;background: #3370FF">-->
<!--                <img src="@/assets/aichat/user-icon.svg" style="width: 30px" alt="" />-->
<!--              </el-avatar>-->
<!--            </div>-->
            <div class="content" style="max-width: 100%;word-break: break-all;white-space: pre-wrap;">
              <el-card class="chat_question_card" shadow="never" style="border-radius: 26px;background-color: #f5f5f5">
                <div class="text break-all pre-wrap" style="word-break: break-all;white-space: pre-wrap;font-size: 14px">
                  {{ item.content }}
                </div>
              </el-card>
              <el-tag type="success" style="margin-bottom: 5px;cursor: pointer;display: inline-block" v-for="(fileName,index) in item.file" :disable-transitions="false" @click="downloadFile(fileName.file,fileName.bucket,item.sessionId)">{{fileName.file}}</el-tag>
            </div>
          </div>
          <!-- 回答 -->
          <div v-if="item.type === 'answer'" class="item-content mb-16 lighter" style="font-weight: 400">
            <el-popconfirm v-if="chatList[chatList.length-1].isEnd || chatList[chatList.length-1].isEnd" popper-style="z-index:3000" title="确定要回到这一步吗?" @confirm="confirmReturn(item,index)">
              <template #reference>
                <div class="returnToHere" style="margin-top: 5px;margin-bottom: 5px">
                  <i class="ri-bookmark-2-fill returnToHereIcon"></i>
                  <el-divider  class="returnToHereDivider">
                    <span class="divider-text">回到这里</span>
                  </el-divider>
                  <i class="ri-arrow-go-back-line returnToHereIcon"></i>
                </div>
              </template>
            </el-popconfirm>
            <div v-else class="returnToHere" style="margin-top: 5px;margin-bottom: 5px">
              <i class="ri-bookmark-2-fill returnToHereIcon"></i>
              <el-divider  class="returnToHereDivider">
<!--                <span class="divider-text">回到这里</span>-->
              </el-divider>
            </div>
<!--            <div class="avatar">-->
<!--              <img src="@/assets/logo/logo2.png" height="30px" />-->
<!--            </div>-->
            <div class="content">
              <MdRenderer :is_large="is_large" :chatIndex="index" :source="item.content" @fullscreenG6="fullscreen"></MdRenderer>
              <Interrupt :isLastChat="index === (chatList.length - 1)" :chatId="item.chatId" :action="item.action" :source="item.interrupt" :checkpointer="item.checkpointer" @processAuth="processAuth"></Interrupt>
              <div v-if="item.isEnd" class="flex-between" style="display: flex; justify-content: space-between; align-items: center;">
                <OperationButton :data="item" :index="index"  @changeThumb="changeThumb" />
<!--                <OperationButton :data="item" :index="index" @regeneration="regenerationChart(index)" @changeThumb="changeThumb" />-->
              </div>
            </div>
          </div>
        </template>
        <div class="flex-between mt-8" style="display: flex; justify-content: space-between; align-items: center; margin-top: 8px">
          <div>
            <div v-if="currentJobId !== ''">
              <i class="ri-loader-2-line loading-icon"></i>
              <span style="font-size: 14px;color: #409EFF">回复中...</span>
            </div>
<!--            <el-button-->
<!--                type="primary"-->
<!--                v-if="chatList.length>0 && chatList[chatList.length-1].isStop && !chatList[chatList.length-1].isEnd"-->
<!--                @click="startChat(chatList.length-1)"-->
<!--                link-->
<!--            >重新生成-->
<!--            </el-button>-->
<!--            <el-button type="primary" v-else-if="chatList.length>0 && !chatList[chatList.length-1].isEnd" @click="stopChat(chatList.length-1)" link-->
<!--            >停止回答-->
<!--            </el-button>-->
            <el-button type="primary" v-if="currentJobId !== ''" link @click="stopChat(chatList.length-1)"
            >停止回答
            </el-button>
          </div>
        </div>
        <div v-if="currentError !== ''">
          <div class="returnToHere" style="margin-top: 5px;margin-bottom: 5px">
            <i class="ri-bookmark-2-fill returnToHereIcon"></i>
            <el-divider  class="returnToHereDivider">
            </el-divider>
          </div>
          <span style="font-size: 14px">{{currentError}}</span>
          <el-link :underline="false" icon="Refresh" type="primary" @click="refreshModal">重试</el-link>
        </div>
      </div>
    </el-scrollbar>
    <div class="ai-chat__operate p-24" style="padding: 24px">
<!--      <slot name="operateBefore" />-->
      <div class="chat-width">
        <el-button type="primary" link class="new-chat-button mb-8" style="margin-bottom: 8px" @click="openUpload">
          <el-icon><Upload /></el-icon><span class="ml-4">导入文件</span>
        </el-button>
      </div>
      <div>
        <el-tag type="success" style="margin-bottom: 5px" v-for="(tag,index) in currentFiles" closable :disable-transitions="false" @close="removeFile(index)">{{tag.file}}</el-tag>
      </div>
      <div>
        <span v-if="currentMachine.length > 0" style="font-size: 14px">与  </span>
        <el-tag style="margin-bottom: 5px" size="large" v-for="tag in currentMachine" closable :disable-transitions="false" @close="removeMachine">
          <span style="font-size: 14px;margin-left: 10px">{{tag}}</span>
        </el-tag>
        <span v-if="currentMachine.length > 0" style="font-size: 14px">  对话</span>
      </div>
      <div class="operate-textarea flex chat-width">
        <el-popover
            :visible="popoverVisible"
            placement="top"
            :width="is_large?'400px':'860px'"
            popper-style="max-width:860px;z-index:3000;margin-left:47px"
            :show-arrow=false
            :offset=1
        >
          <template #reference>
            <el-input
                ref="quickInputRef"
                v-model="inputValue"
                :placeholder="'@选择机器人，Ctrl+Enter 换行，Enter发送'"
                :autosize="{ minRows: 1, maxRows: 4 }"
                type="textarea"
                :maxlength="100000"
                @input="handleInput"
                @keydown.enter="sendChatHandle($event)"
            />
          </template>
          <template #default>
            <div style="width: 100%"  v-click-outside="clickoutside">
              <div style="align-items: center;display: flex">
                <span style="font-size: 25px;font-weight: bold">选择智能体</span>
                <el-link :underline="false" style="font-size: 20px;margin-left: auto" @click="closeMachinePop"><i class="ri-close-large-line"></i></el-link>
              </div>
              <el-scrollbar max-height="200px">
                <div class="machineDiv" @click="chooseMachine('元数据专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">元数据专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('数据标准专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">数据标准专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('数据质量专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">数据质量专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('数据模型专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">数据模型专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('数据安全专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">数据安全专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('数据分析专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">数据分析专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('数据治理管理专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">数据治理管理专家</span>
                </div>
                <div class="machineDiv" @click="chooseMachine('智能导航专家','@/assets/aichat/智能体logo.jpg')" style="align-items: center;width:100%;display: flex">
                  <el-avatar style="width: 30px;height: 30px"><img src="@/assets/aichat/智能体logo.jpg"></el-avatar>
                  <span style="font-size: 16px;margin-left: 10px">智能导航专家</span>
                </div>
              </el-scrollbar>
            </div>
          </template>
        </el-popover>
        <div class="operate flex align-center">
          <el-button
              text
              class="sent-button"
              :disabled="isDisabledChart || loading"
              @click="sendChatHandle"
          >
            <img v-show="isDisabledChart || loading" src="@/assets/aichat/icon_send.svg" alt="" />
            <img v-show="!isDisabledChart && !loading" src="@/assets/aichat/icon_send_colorful.svg" alt="" />
          </el-button>
          <el-popover
              popper-style="z-index:3000;width:300px"
              title="自动审批配置"
              placement="left-end"
              trigger="click"
          >
            <template #reference>
              <el-button
                  text
                  style="padding: 0;margin: 0"
              >
                <el-icon><Setting /></el-icon>
              </el-button>
            </template>
            <template #default>
              <div  class="autoProcessClass"  @click.prevent="handleCheckAllRobot" style="cursor: pointer">
                <el-checkbox
                    v-model="autoProcess.checkAll"
                    :indeterminate="autoProcess.isIndeterminate"
                >
                  全部自动审批
                </el-checkbox>
              </div>
              <el-divider style="margin-top: 5px;margin-bottom: 5px" />
              <el-checkbox-group
                  v-model="autoProcess.autoArray"
                  style="display: flex; flex-direction: column"
              >
                <div v-for="robot in autoProcess.robots" @click.prevent="toggleRobot(robot)" class="autoProcessClass" style="cursor: pointer">
                  <el-checkbox :key="robot" :label="robot" :value="robot"></el-checkbox>
                </div>
              </el-checkbox-group>
            </template>
          </el-popover>

        </div>
      </div>
    </div>
    <!-- 文件导入对话框 -->
    <el-dialog :z-index="4001" title="文件导入" v-model="upload.open" width="400px" append-to-body>
      <el-upload v-if="upload.open"
          ref="uploadRef"
          :headers="upload.headers"
          :action="upload.url"
          :data = "upload.data"
          :disabled="upload.isUploading"
          :on-progress="handleFileUploadProgress"
          :on-success="handleFileSuccess"
          :auto-upload="false"
          drag
      >
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitFileForm">确 定</el-button>
          <el-button @click="upload.open = false">取 消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog z-index="99999999999999999999" title="ER图全览" v-if="showFullscreenG6Data" :fullscreen="true" v-model="showFullscreenG6Data" append-to-body>
      <fullscreenG6 :g6Data="fullscreenG6Data"></fullscreenG6>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import OperationButton from './OperationButton.vue'
import MdRenderer from '@/views/aichat/MdRenderer.vue'
import fullscreenG6 from '@/views/aichat/fullscreenG6.vue'
import {getToken} from "@/utils/auth.js";
import {addChat, cancelJob, DeleteChatList, postChatMessage, updateChatProcessData} from "@/api/aichat/aichat.js"
import cache from "@/plugins/cache.js";
import Cookies from "js-cookie";
import Interrupt from "@/views/aichat/Interrupt.vue";
import { v4 as uuidv4 } from 'uuid';

defineOptions({ name: 'AiChat' })
const route = useRoute()
const fullscreenG6Data = ref(null)
const showFullscreenG6Data = ref(false)
const {
  params: { accessToken, id },
  query: { mode }
} = route
const props = defineProps({
  is_large: Boolean,
  cookieSessionId: String,
  data: {
    type: Object,
    default: () => {}
  },
  record: {
    type: Array,
    default: () => []
  },
  chatId: {
    type: String,
    default: ''
  }, // 历史记录Id
  debug: {
    type: Boolean,
    default: false
  }
})

const { proxy } = getCurrentInstance();

const aiChatRef = ref()
const quickInputRef = ref()
const scrollDiv = ref()
const dialogScrollbar = ref()
const loading = ref(false)
const inputValue = ref('')
const currentQuestion = ref({})
const chartOpenId = ref('')
const chatList = ref([])
const controller = ref(null)
const currentChatData = ref({})
const autoProcess = ref({
  checkAll: false,
  isIndeterminate: false,
  autoArray:[],
  robots:['数据治理管理专家自动批准','元数据专家自动批准','数据标准专家自动批准','数据安全专家自动批准','数据分析专家自动批准','数据模型专家自动批准','数据质量专家自动批准','智能导航专家自动审批'],
})
const popoverVisible = ref(false)
const currentMachine = ref([])
const currentFiles = ref([])
const currentError = ref('')
const currentJobId = ref('')
const lastQuestion = ref({})
const upload = reactive({
  // 是否显示弹出层（用户导入）
  open: false,
  // 弹出层标题（用户导入）
  title: "",
  // 是否禁用上传
  isUploading: false,
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/aichat/upload",
  data: {"sessionId":Cookies.get("chatSessionId")}
});
const isDisabledChart = computed(
    () => !(inputValue.value.trim())
)

const emit = defineEmits(['scroll'])

function setScrollBottom() {
  // 将滚动条滚动到最下面
  scrollDiv.value.setScrollTop(getMaxHeight())
}

function confirmReturn(item,index){
  DeleteChatList(item.chatId).then(res=>{
    chatList.value.splice(index, chatList.value.length - index);
    let reqData = {
      "user_id": cache.local.get("username"),
      "session_id": Cookies.get("chatSessionId"),
      "checkpointer": item.checkpointer
    }
    sendChatMessage(reqData)
  })

}

function handleCheckAllRobot(){
  autoProcess.value.checkAll = !autoProcess.value.checkAll
  autoProcess.value.autoArray = autoProcess.value.checkAll ? autoProcess.value.robots : []
  autoProcess.value.isIndeterminate = false
}

function toggleRobot(robot){
  const index = autoProcess.value.autoArray.indexOf(robot)
  if (index !== -1) {
    // 已选中则取消
    autoProcess.value.autoArray = autoProcess.value.autoArray.filter(item => item !== robot)
  } else {
    // 未选中则添加
    autoProcess.value.autoArray = [...autoProcess.value.autoArray, robot]
  }
  const checkedCount = autoProcess.value.autoArray.length
  autoProcess.value.checkAll = checkedCount === autoProcess.value.robots.length
  autoProcess.value.isIndeterminate = checkedCount > 0 && checkedCount < autoProcess.value.robots.length
}

/**
 * 滚动条距离最上面的高度
 */
const scrollTop = ref(0)

const getMaxHeight = () => {
  return dialogScrollbar.value?.scrollHeight ?? 0;
}

const handleScrollTop = ($event) => {
  scrollTop.value = $event.scrollTop
  emit('scroll', { ...$event, dialogScrollbar: dialogScrollbar.value, scrollDiv: scrollDiv.value })
}

const handleScroll = () => {
  if (scrollDiv.value) {
    // 内部高度小于外部高度 就需要出滚动条
    if (scrollDiv.value.wrapRef.offsetHeight < dialogScrollbar.value.scrollHeight) {
      // 如果当前滚动条距离最下面的距离在 规定距离 滚动条就跟随
      // if (scorll.value) {
        scrollDiv.value.setScrollTop(getMaxHeight())
      // }
    }
  }
}

/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
  upload.isUploading = true;
};

const handleFileSuccess = (response, file, fileList) => {
  currentFiles.value.push({file:response.data.file,bucket:response.data.bucket})
  upload.open = false;
  upload.isUploading = false;
  proxy.$modal.msgSuccess(response.msg);
  // proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
};

/** 提交上传文件 */
function submitFileForm() {
  proxy.$refs["uploadRef"].submit();
}

watch(
    () => props.chatId,
    (val) => {
      if (val && val !== 'new') {
        chartOpenId.value = val
      } else {
        chartOpenId.value = ''
      }
    },
    { deep: true }
)

watch(
    () => props.record,
    (value) => {
      chatList.value = value
    },
    {
      immediate: true
    }
)

watch(() => props.cookieSessionId, value => upload.data = {sessionId:value})

// function regenerationChart(index){
//
//   if (currentQuestion.value.query){
//     sendChatMessage(currentQuestion.value)
//   }else{
//     // let question = chatList.value[index - 1]
//     // let chat = {
//     //   "chatId":uuidv4(),
//     //   "type":"question",
//     //   "content":question.content,
//     //   "time": formatDate(new Date()),
//     //   "file": question.file}
//     // chatList.value.push(chat)
//     // let data = {
//     //   "query": chat.content,
//     //   "user_id": cache.local.get("username"),
//     //   "robot": currentMachine.value.length>0?currentMachine.value[0]:"",
//     //   "session_id": Cookies.get("chatSessionId"),
//     //   "doc": chat.file,
//     // }
//     sendChatMessage(data)
//   }
//
// }

function changeThumb(index,chat){
  chatList.value[index].operate = chat.operate
  chatList.value[index].thumbDownReason = chat.thumbDownReason
}

function openUpload(){
  upload.open = true
}

function chooseMachine(val){
  inputValue.value = inputValue.value.slice(0,-1)
  currentMachine.value = [val]
  popoverVisible.value = false
}

function closeMachinePop(){
  if (inputValue.value.endsWith('@')){
    inputValue.value = inputValue.value.slice(0,-1)
  }
  popoverVisible.value = false
}

function removeMachine(){
  currentMachine.value = []
}

function fullscreen(data){
  fullscreenG6Data.value = data
  showFullscreenG6Data.value = true
}

function clickoutside() {
  if (inputValue.value.endsWith('@')){
    inputValue.value = inputValue.value.slice(0,-1)
  }
  popoverVisible.value = false
}

function removeFile(index){
  currentFiles.value = currentFiles.value.slice(index,1)
}

function handleInput() {
  popoverVisible.value = inputValue.value.endsWith("@");
}

function padZero(num) {
  return num < 10 ? '0' + num : num;
}

function formatDate(date) {
  const year = date.getFullYear();
  const month = padZero(date.getMonth() + 1); // 月份从0开始，所以需要+1
  const day = padZero(date.getDate());
  const hours = padZero(date.getHours());
  const minutes = padZero(date.getMinutes());
  const seconds = padZero(date.getSeconds());

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

function downloadFile(file,bucket,sessionId){
  let data = {file,bucket,sessionId}
  proxy.download("/default-api/aichat/file/download", {
    ...data,
  }, file);
}

function processAuth(data){
  for (let i = 0; i < data.interrupt.block.length; i++) {
    for (let key in data.formData){
      if (key && key === data.interrupt.block[i].name){
        data.interrupt.block[i].default_value = data.formData[key]
      }
    }
  }
  let updateData = {
    chatId: data.chatId,
    action: data.action,
    interrupt: JSON.stringify(data.interrupt)
  }
  updateChatProcessData(updateData).then(res=>{
    let reqData = {
      "user_id": cache.local.get("username"),
      "session_id": Cookies.get("chatSessionId"),
      "checkpointer": data.checkpointer,
      "action": data.action,
      "resume": true,
      "block": data.interrupt.block
    }
    currentChatData.value = reqData
    sendChatMessage(reqData)
  })
}

function refreshModal(){
  sendChatMessage(currentChatData.value)
}

async function sendChatHandle(event) {
  if (!event.ctrlKey) {
    // 如果没有按下组合键ctrl，则会阻止默认事件
    event.preventDefault()
    if (inputValue.value.trim() && (chatList.value.length === 0||
                                    (chatList.value[chatList.value.length - 1].isStop ||
                                    chatList.value[chatList.value.length - 1].isEnd))) {
      chatList.value.push({
        "chatId": uuidv4(),
        "type": "question",
        "content": inputValue.value.trim(),
        "time": formatDate(new Date()),
        "sessionId": Cookies.get("chatSessionId"),
        "sessionName": chatList.value.length > 0 ? chatList.value[0].content.substring(0, 20) : inputValue.value.trim().substring(0, 20),
        "file": currentFiles.value
      })
      let question = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1]))
      question.file = JSON.stringify(question.file)
      await addChat(question)
      nextTick(() => {
        // 将滚动条滚动到最下面
        scrollDiv.value.setScrollTop(getMaxHeight())
      })
      let data = {
        "query": inputValue.value.trim(),
        "user_id": cache.local.get("username"),
        "robot": currentMachine.value.length > 0 ? currentMachine.value[0] : "",
        "session_id": Cookies.get("chatSessionId"),
        "doc": currentFiles.value
      }
      if (chatList.value.length > 1){
         // 判断为1 因为上方已将问题加入 chatList了 , >1 则视为已存在提问交互
        //取最新的checkpointer值
        data.checkpointer = chatList.value[chatList.value.length - 2].content.checkpointer
      }
      currentQuestion.value = data
      inputValue.value = ''
      sendChatMessage(data)
    }
  } else {
    // 如果同时按下ctrl+回车键，则会换行
    inputValue.value += '\n'
  }
}

function sendChatMessage(data){
  controller.value = new AbortController()
  postChatMessage(data,{signal:controller.value.signal}).then(res=>{
    if (res.status !== 200){
      // chatList.value.push({"chatId":uuidv4(),"type":"answer","content":[{"type":"text","content":"服务异常，错误码:"+res.status}],"isEnd":true,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName,"operate":'',"thumbDownReason":''})
      currentError.value = "服务异常，错误码:"+res.status +",请联系管理员！"
    }else {
      currentError.value = ''
      currentChatData.value = {}
      currentFiles.value = []
      chatList.value.push({"chatId":uuidv4(),"type":"answer","content":[],"isEnd":false,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName, "operate":'',"thumbDownReason":''})
      const reader = res.body.getReader()
      const write = getWrite(reader)
      reader.read().then(write).then(()=> {
        let answer = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1]))
        answer.content = JSON.stringify(answer.content)
        answer.interrupt = answer.interrupt ? JSON.stringify(answer.interrupt): null
        answer.checkpointer = JSON.stringify(answer.checkpointer)
        addChat(answer)
      }).then(()=>{
        let answer = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1]))
        if(answer.interrupt){
          let robot = answer.interrupt.robot
          let block = answer.interrupt.block
          let action = answer.interrupt.action
          let autoRequest = false
          if (autoProcess.value.autoArray.length > 0 && autoProcess.value.autoArray.indexOf(robot) !== -1){
            //自动审批信号有效，需判断 block内容的必填项，有无默认值，有的话，可以自动发送，没有需填写
            autoRequest = true
            if (block && block.length>0){
              for (let i = 0; i < block.length; i++) {
                if (block[i].required){
                  if (block[i].ct_type === 'dateRangePicker' || block[i].ct_type === 'checkboxGroup' || block[i].ct_type === 'multiselect'){
                    //default_value 是数组
                    if (!block[i].default_value || block[i].default_value === []){
                      autoRequest = false
                    }
                  }
                  if (block[i].ct_type === 'datePicker' || block[i].ct_type === 'input' || block[i].ct_type === 'radioGroup' ||block[i].ct_type === 'select'){
                    //default_value 是文字
                    if (!block[i].default_value || block[i].default_value.trim() === ''){
                      autoRequest = false
                    }
                  }
                }
              }
            }
            if (autoRequest){
              let reqData = {
                "user_id": cache.local.get("username"),
                "session_id": Cookies.get("chatSessionId"),
                "checkpointer": answer.checkpointer,
                "action":"",
                "resume": true,
                "block": block
              }
              for (let i = 0; i < action.length; i++) {
                if (action[i].style === 'primary'){
                  reqData.action = action[i].action
                }
              }
              sendChatMessage(reqData)
            }
          }
        }
      })
    }
  }).catch((e) => {
    currentError.value = "服务异常,请联系系统管理员！"
  })
}



watch(
    chatList,
    () => {
      nextTick(() => {
        // 将滚动条滚动到最下面
        // scrollDiv.value.setScrollTop(getMaxHeight())
        handleScroll()
      })
    },{
      deep:true, immediate:true
    }
)

const getWrite = (reader) => {
  // 修复点1：将tempResult改为局部变量，避免递归状态污染
  let tempResult = '';
  const write_stream = ({ done, value }) => {
    if (done) return;
    const decoder = new TextDecoder('utf-8');
    let str = decoder.decode(value, { stream: true });
    tempResult += str;
    // 修复点2：使用非贪婪匹配确保精确分块
    const split = tempResult.match(/data:.*?}\r\n/g);

    if (split) {
      // 修复点3：按顺序处理每个匹配块，避免遗漏
      for (let i = 0; i < split.length; i++) {
        const chunkStr = split[i];
        tempResult = tempResult.replace(chunkStr, '', 1); // 修复点4：单次替换避免残留
        try {
          const chunk = JSON.parse(chunkStr.replace('data:', '').trim());
          processChunk(chunk);
        } catch (e) {
          console.error('解析错误:', e, chunkStr);
        }
      }
      // 递归处理剩余数据
      return reader.read().then(write_stream);
    } else {
      // 无匹配块时继续累积数据
      return reader.read().then(write_stream);
    }
  };

  const processChunk = (chunk) => {
    currentJobId.value = chunk.job_id;
    const lastMsg = chatList.value[chatList.value.length - 1];
    // 修复点5：统一处理所有类型的数据块
    if (chunk.docs?.length) {
      lastMsg.content.push({ content: chunk.docs, type: "docs" });
    } else if (chunk.G6_ER?.length) {
      lastMsg.content.push({ content: chunk.G6_ER, type: "G6_ER" });
    } else if (chunk.html_image?.length) {
      lastMsg.content.push({ content: chunk.html_image, type: "html_image" });
    } else if (chunk.table?.length) {
      lastMsg.content.push({ content: chunk.table, type: "table" });
    } else if (chunk.router) {
      lastMsg.content.push({ content: chunk.router, type: "router" });
    } else if (chunk.choices?.length) {
      // 修复点6：纯文本处理增加防重复校验
      // const text = chunk.choices[0].delta.content.replace(/\n/g, "\n\n");
      // 智能换行处理
      const text = chunk.choices[0].delta.content;
      // const isNewParagraph = content.startsWith('\n\n');
      // 仅当需要时添加换行
      // const text = content;
      // const text = isNewParagraph
      //     ? content.replace(/\n{2,}/g, '\n\n')
      //     : content.replace(/\n/g, '\n\n');
      const lastContent = lastMsg.content[lastMsg.content.length - 1];
      if (lastContent?.type === "text") {
        lastContent.content += text;
      } else {
        lastMsg.content.push({ content: text, type: "text" });
      }
    }
    if (chunk.interrupt){
      lastMsg.interrupt = chunk.interrupt
    }
    // 修复点7：统一处理结束标志
    if (chunk.checkpointer) {
      lastMsg.checkpointer = chunk.checkpointer
      currentJobId.value = ''
      lastMsg.isEnd = true;
      lastMsg.time = formatDate(new Date());
    }
    nextTick(() => scrollDiv.value.setScrollTop(getMaxHeight()));
  };

  return write_stream;
};

const stopChat = (index) => {
  if (currentJobId.value !== ''){
    cancelJob({job_id:currentJobId.value})
  }
  chatList.value[index].isStop = true
  if (controller.value !== null){
    controller.value.abort()
  }
  let answer = JSON.parse(JSON.stringify(chatList.value[index]))
  answer.content = JSON.stringify(answer.content)
  addChat(answer)
  currentJobId.value = ''
}

// const startChat = (index) => {
//   regenerationChart(index)
// }

defineExpose({
  setScrollBottom
})

</script>
<style lang="scss" scoped>
.ai-chat {
  --padding-left: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  position: relative;
  color: #1f2329;

  &__content {
    height: auto;
    padding-top: 0;
    box-sizing: border-box;

    .avatar {
      float: left;
    }

    .content {
      padding-left: var(--padding-left);

      :deep(ol) {
        margin-left: 16px !important;
      }
    }

    .text {
      padding: 6px 0;
    }

    .problem-button {
      width: 100%;
      border: none;
      border-radius: 8px;
      background: #f5f6f7;
      height: 46px;
      padding: 0 12px;
      line-height: 46px;
      box-sizing: border-box;
      color: #1f2329;
      -webkit-line-clamp: 1;
      word-break: break-all;

      &:hover {
        background: var(--el-color-primary-light-9);
      }

      &.disabled {
        &:hover {
          background: #f5f6f7;
        }
      }

      :deep(.el-icon) {
        color: var(--el-color-primary);
      }
    }
  }

  &__operate {
    background: #f3f7f9;
    position: relative;
    width: 100%;
    box-sizing: border-box;
    z-index: 10;

    &:before {
      background: linear-gradient(0deg, #f3f7f9 0%, rgba(243, 247, 249, 0) 100%);
      content: '';
      position: absolute;
      width: 100%;
      top: -16px;
      left: 0;
      height: 16px;
    }

    .operate-textarea {
      box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
      background-color: #ffffff;
      border-radius: 8px;
      border: 1px solid #ffffff;
      box-sizing: border-box;

      &:has(.el-textarea__inner:focus) {
        border: 1px solid var(--el-color-primary);
        scrollbar-width: none;
      }

      :deep(.el-textarea__inner) {
        border-radius: 8px !important;
        box-shadow: none;
        resize: none;
        padding: 12px 16px;
        box-sizing: border-box;
        scrollbar-width: none;
      }

      .operate {
        padding: 6px 10px;
        .el-icon {
          font-size: 20px;
        }

        .sent-button {
          max-height: none;
          .el-icon {
            font-size: 24px;
          }
        }

        :deep(.el-loading-spinner) {
          margin-top: -15px;

          .circular {
            width: 31px;
            height: 31px;
          }
        }
      }
    }
  }

  .dialog-card {
    border: none;
    border-radius: 8px;
    box-sizing: border-box;
  }
}

.chat-width {
  max-width: 80%;
  margin: 0 auto;
}
.machineDiv {
  cursor: pointer;
  padding-top: 3px;
  padding-bottom: 3px;
}
.machineDiv:hover {
  background-color: #ebf1ff;
  font-weight: bold;
}
@media only screen and (max-width: 1000px) {
  .chat-width {
    max-width: 100% !important;
    margin: 0 auto;
  }
}
.autoProcessClass:hover {
  background-color: var(--el-color-primary-light-9);
}

:deep(.chat_question_card .el-card__body) {
  padding: 10px 20px !important;
}

.returnToHere {
  display: flex;
  align-items: center;
  cursor: pointer;
  /* 文字默认隐藏 */
  --text-color: transparent;
  --divider-border: rgba(0,0,0,0);
}

.returnToHereIcon {
  color: var(--icon-color, #000);
  transition: color 0.3s;
}

.returnToHereDivider {
  flex: 1;
  margin: 0 10px;
  --divider-border-color: var(--divider-border);
}

/* 文字默认透明 */
.divider-text {
  color: var(--text-color);
  transition: color 0.3s;
}

/* 鼠标移入时整体变化 */
.returnToHere:hover {
  --icon-color: #409EFF;       /* 蓝色 */
  --text-color: #409EFF;       /* 文字变蓝 */
  --divider-border: #409EFF;   /* 分隔线变蓝 */
}

/* 处理Element Plus分隔线边框颜色 */
:deep(.el-divider__text) {
  background: transparent !important;
  padding: 0 !important;
}
:deep(.el-divider__line) {
  border-top-color: var(--divider-border-color) !important;
}
/* 定义旋转动画 */
@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
/* 应用动画到图标 */
//.ri-loader-2-line {
//  display: inline-block; /* 确保 transform 生效 */
//  animation: rotate 1s linear infinite; /* 1秒一圈，匀速旋转，无限循环 */
//  transform-origin: 50% 50%;
//}
/* 可选：添加渐隐效果 */
//.ri-loader-2-line.fade {
//  opacity: 0.7;
//  transition: opacity 0.3s;
//}
.loading-icon {
  display: inline-block; /* 确保 transform 生效 */
  animation: rotate 1s linear infinite; /* 1秒一圈，匀速旋转，无限循环 */
  transform-origin: 50% 50%;
  color: #409EFF; /* 修改颜色 */
  //animation: rotate 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
  /* 使用非匀速旋转增加动感 */
}

</style>
