<!--
 * @Author: qinzhenpen qzp1807@126.com
 * @Date: 2025-03-03 14:13:35
 * @LastEditors: qinzhenpen qzp1807@126.com
 * @LastEditTime: 2025-05-07 10:05:57
 * @FilePath: \financial-big-data\src\components\Ai\index.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
    <div class="index">
      <div class="min-h-screen" ref="header">
        <div class="title">
          <h1>在这里，用 AI 智能学习，直通就业</h1>
        </div>
        <div class="tz-tobs mb10">
          <template v-for="(tbas, idx) in tabList" :key="tbas.name">
            <div :style="{ background: `url(${getAssetsFile(`${idx + 1}.webp`)}) no-repeat`, backgroundSize: '100% 100%' }" :class="[tbas == tabactive ? 'el-tobs-item active' : ' el-tobs-item', { 'el-tobs-item-active': claActive == tbas.name }]" @click="changeTab(tbas)">
              <span>{{ tbas.name }}</span>
            </div>
          </template>
        </div>
      </div>
      <div class="tz-centent" ref="container">
        <div class="tz-step" ref="step">
          <div class="tz-stop-info">
            <template v-for="item in tabactive.children" :key="item.title">
              <div :class="['info-title', { 'info-title-active': stopActive == item.title }]" @click="changeStop(item.title)">{{ item.title }}</div>
            </template>
          </div>
          <div class="tz-stop-centent">
            <div class="stop">
              <template v-for="(item, index) in tabactive.children" :key="index">
                <template v-if="item.title == stopActive">
                  <div :class="['stop-item', { 'info-title-completedSteps': childs.isTemplateBased }, { 'info-title-completedSteps-disdabled': !isTaskEnabled(item.child, idx) }]" v-for="(childs, idx) in item.child" :key="childs" @click="onClick(childs, idx)">{{ childs.name }}</div>
                </template>
              </template>
            </div>
            <div class="fbtn fr" style="display: flex; gap: 20px">
              <el-upload :auto-upload="false" class="avatar-uploader" :show-file-list="false" :data="domfile" :on-change="submitDocument" accept=".xls,.xlsx,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
                <template #trigger>
                  <el-button type="primary" :disabled="!allTrue()">提交</el-button>
                </template>
              </el-upload>
              <el-button type="success" @click="exportSession">导出会话</el-button>
            </div>
          </div>
        </div>
        <el-scrollbar :height="scrollbarHeight" ref="scrollArea">
          <ul class="QAlist">
            <li :class="[item.role == 'user' ? 'questionItem' : 'answerItem']" v-for="item in messages" :key="item" :data-name="item.name">
              <div :class="[item.role == 'user' ? 'qustionCon' : 'answerCon']">
                <div v-if="item.role !== 'user'" class="answerCon-img">
                  <img src="@/assets/images/ai.webp" alt="" />
                </div>
                <MdPreview :editorId="id" :modelValue="item.content" :codeFoldable="false" theme="dark" codeTheme="a11y" />
                <div class="copy">
                  <el-tooltip class="box-item" effect="dark" content="复制" placement="top">
                    <svg-icon icon-class="copy" v-copyText="item.content" v-copyText:callback="copyTextSuccess" />
                  </el-tooltip>
                </div>
              </div>
            </li>
            <li v-if="isRequestPending">
              <div style="display: flex; align-items: center">
                <img src="@/assets/images/ai.webp" alt="" />
                <div style="height: 68px" v-loading="isRequestPending" :element-loading-svg="svg"></div>
              </div>
            </li>
            <li>
              <div class="task-introduction" v-if="messages.length <= 0">
                <div class="title mb10">
                  <span>{{ tabactive.introduce }}</span>
                </div>
                <p style="font-size: 1rem !important">提示词小技巧:</p>
                <ul>
                  <li>提问时给出具体知识点、技能点等数量要求:</li>
                  <li>每个点或者整体加上字数限制:</li>
                  <li>要求AI输出表格，进行图表等可视化展示:</li>
                  <li>针对AI给出内容某一点继续详细追问，进行深入学习了解:</li>
                </ul>
              </div>
            </li>
          </ul>
        </el-scrollbar>
      </div>
      <div class="tz-footer-ipt" ref="footer">
        <el-input type="textarea" placeholder="可以问我任何问题" v-model.trim="inputMessage" @keyup.enter="sendMessage" :autosize="{ minRows: 4.5, maxRows: 3 }" />
        <svg-icon icon-class="发送" class="fr" v-if="isTypingComplete" @click="sendMessage" :class="{ disdabled: !inputMessage }" />
        <svg-icon icon-class="中断" class="fr" @click="gInterrupt" v-else />
      </div>
    </div>
  </template>
  <script setup>
  import { MdPreview, MdCatalog } from "md-editor-v3";
  import { getAssetsFile } from "@/utils/index.js";
  import * as AI from "@/api/AI.js";
  import * as indexApi from "@/api/index";
  import "md-editor-v3/lib/style.css";
  import useUserStore from "@/store/modules/user";
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
  const scrollbarHeight = ref("auto"); // 动态高度
  const container = ref(null); // 父容器
  const header = ref(null); // 头部
  const step = ref(null); // 头部
  const footer = ref(null); // 底部
  let resizeObserver = null; // ResizeObserver 实例
  const inputMessage = ref("");
  const isRequestPending = ref(false);
  const isTypingComplete = ref(true);
  const scrollArea = ref(null);
  const tabList = ref([
    {
      name: "知识画布",
      cor: "#55c5c3",
      introduce: "本模块分为理论知识、实务知识、未来展望三个模块，每个模块分为五个步骤，学生根据相应步骤提示词，了解实训系统相关理论知识重点难点及学习建议，学习实训系统相关业务实务知识提作流程，相关单据协议及业务风险等，最后了解业务现状及理论研究，新技术应用等情况,了解未来发展趋势",
      children: [
        {
          title: "理论知识",
          child: [
            { name: "课程知识点", com: "请梳理xxx课程相关知识点，每个知识点解释字数50字以内" },
            { name: "核心知识点", com: "请梳理xxx课程最核心的5个模块，每个知识点解释字数50字以内" },
            { name: "最难的部分", com: "请指出xxx课程最难的1个模块，并解释原因，字数300字以内" },
            { name: "难点详解", com: "请详细解释xxx，字数800字以内" },
            { name: "学习建议", com: "请给我一些xxx课程的学习建议，字数300字以内" },
          ],
        },
        {
          title: "实务知识",
          child: [
            {
              name: "实务知识概述",
              com: "xxx相关实务知识有哪些？",
            },
            {
              name: "业务操作流程",
              com: "xxx业务在实务中是怎么操作的？",
            },
            {
              name: "相关单据协议",
              com: "xxx业务在实操中会用到哪些合同协议文件？",
            },
            {
              name: "业务风险管理",
              com: "xxx业务有哪些风险？如何防范？",
            },
            {
              name: "流程优化建议",
              com: "xxx业务流程可以进行优化以提高效率吗？请给出优化建议？",
            },
          ],
        },
        {
          title: "未来展望",
          child: [
            {
              name: "业务现状",
              com: "xxx国际和国内发展现状",
            },
            {
              name: "理论研究",
              com: "xxx相关理论有哪些，研究现状如何，是否有理论研究突破",
            },
            {
              name: "新技术应用",
              com: "xxx业务应用了哪些新技术？比如人工智能、大数据、物联网等新技术",
            },
            {
              name: "技术应用详解",
              com: "xxx技术在xxx业务中是如何应用的？有哪些应用案例？",
            },
            {
              name: "发展趋势",
              com: "xxx发展趋势如何？",
            },
          ],
        },
      ],
    },
    {
      name: "行业企业",
      cor: "#538af7",
      introduce: "本模块分为就业行业分析、企业对比、企业调研三个模块，每个模块分为五个步骤。学生根据相应步骤提示词，了解目标行业的发展现状、就业趋势及核心技能要求，掌握不同企业的业务模式、文化特点及竞争优势，并通过调研工具和方法，深入分析企业的组织架构、岗位需求及职业发展路径。最后，学生将结合行业动态和企业实践，形成对就业市场的全面认知，为职业规划提供科学依据。",
      children: [
        {
          title: "就业行业",
          child: [
            {
              name: "行业类型",
              com: "xxx相关的就业行业类型主要有哪些？列出xx个行业",
            },
            {
              name: "行业发展状况",
              com: "xxx行业发展状况如何？",
            },
            {
              name: "行业对比分析",
              com: "请比较xx行业和xx行业，分析行业相关优劣及发展前景，输出表格",
            },
            {
              name: "最佳客户化",
              com: "请从行业特点及前景出发，客观推荐一个xx相关行业",
            },
            {
              name: "行业详细分析",
              com: "请详细分析xx行业，可以从多个方面进行分析",
            },
          ],
        },
        {
          title: "企业对比",
          child: [
            {
              name: "企业规模",
              com: "请对比前述相关xx家企业规模，并按企业规模大小排序",
            },
            {
              name: "企业所在地",
              com: "请根据前述xx家企业所在地，分析相关企业地域集聚现象",
            },
            {
              name: "企业盈利",
              com: "请根据企业规模，对比分析xx家企业盈利能力，并根据盈利能力进行排序",
            },
            {
              name: "企业福利",
              com: "请对比分析前述xx家企业福利状况，并根据福利状况进行排序",
            },
            {
              name: "招聘情况",
              com: "请对比分析前述xx家企业招聘情况，哪些企业招聘量较大？",
            },
          ],
        },
        {
          title: "企业调研",
          child: [
            {
              name: "行业相关企业",
              com: "xxx行业相关企业有哪些？请列出xx家",
            },
            {
              name: "企业发展状况",
              com: "xx企业发展历程及现状",
            },
            {
              name: "企业财务状况",
              com: "xx企业财务状况如何？盈利能力如何？财务风险如何？",
            },
            {
              name: "企业面临风险",
              com: "xx企业面临哪些风险？从政策、区域、行业、企业等多层面多角度分析",
            },
            {
              name: "就业企业建议",
              com: "上述相关企业推荐哪些企业作为就业企业？最佳荐哪家企业？",
            },
          ],
        },
      ],
    },
    {
      name: "岗位类型",
      cor: "#eabd42",
      introduce: "本模块分为岗位类型、岗位发展、推荐岗位三个模块，每个模块分为五个步骤。学生根据相应步骤提示词，了解不同岗位的职责要求、技能需求及工作特点，掌握岗位的职业发展路径及晋升机会，并通过分析行业趋势和个人兴趣，获得适合自己的岗位推荐及发展建议。最后，学生将结合岗位需求和自身优势，制定清晰的职业规划，为未来的就业选择提供科学指导。",
      children: [
        {
          title: "岗位类型",
          child: [
            {
              name: "岗位类型",
              com: "xx相关的岗位类型有哪些？请列举出xx个",
            },
            {
              name: "类型详解",
              com: "请详细解释xx类岗位",
            },
            {
              name: "工作内容",
              com: "xx类岗位工作内容有哪些？",
            },
            {
              name: "人员画像",
              com: "xx类岗位工作人员画像，哪些人适合从事xx类岗位",
            },
            {
              name: "推荐适合类型",
              com: "我是一个xxx样的人，请为我推荐合适的岗位类型",
            },
          ],
        },
        {
          title: "岗位发展",
          child: [
            {
              name: "基层岗位",
              com: "xxx相关基层岗位",
            },
            {
              name: "中层岗位",
              com: "xx相关中层岗位",
            },
            {
              name: "高层岗位",
              com: "xx相关高层岗位",
            },
            {
              name: "岗位现状",
              com: "xx岗位现状",
            },
            {
              name: "岗位前景",
              com: "xx岗位发展前景",
            },
          ],
        },
        {
          title: "推荐岗位",
          child: [
            {
              name: "具体岗位",
              com: "xxx相关岗位有哪些？请列出xx个岗位",
            },
            {
              name: "岗位对比",
              com: "请列出以上几个岗位，分析岗位优缺点，输出表格",
            },
            {
              name: "岗位晋升路径",
              com: "xx岗位晋升路径是怎样的？",
            },
            {
              name: "岗位可替代性",
              com: "随着社会发展，xx岗位是否容易被替代？",
            },
            {
              name: "推荐合适岗位",
              com: "简单描述个人信息及性格特点，让AI推荐合适岗位",
            },
          ],
        },
      ],
    },
    {
      name: "技能要求",
      cor: "#f1858b",
      children: [
        {
          title: "岗位技能",
          child: [
            {
              name: "岗位技能",
              com: "XX类岗位技能有哪些？请列出xx种技能",
            },
            {
              name: "理论支撑",
              com: "XX技能需要哪些理论知识支撑，需要学习哪些理论课程？",
            },
            {
              name: "实训实习",
              com: "可以通过哪些实训或者实习来学习相关技能？",
            },
            {
              name: "感兴趣技能",
              com: "我对XX技能感兴趣，请详细解释如何学习该技能",
            },
            {
              name: "实习推荐",
              com: "如果想加强学习XX技能，可以推荐相关企业实习吗，请列出XX家企业",
            },
          ],
        },
        {
          title: "技能发展",
          child: [
            {
              name: "技能发展路径",
              com: "XXX技能发展路径",
            },
            {
              name: "初级技能",
              com: "XX相关初级技能",
            },
            {
              name: "中级技能",
              com: "XXX相关中级技能",
            },
            {
              name: "高级技能",
              com: "XXX相关高级技能",
            },
            {
              name: "发展前景",
              com: "XXX相关技能发展前景",
            },
          ],
        },
        {
          title: "技能学习",
          child: [
            {
              name: "岗位核心技能",
              com: "XXX岗位核心技能有哪些？列出x种",
            },
            {
              name: "核心技能详解",
              com: "请详细解释上述xx技能",
            },
            {
              name: "学习渠道推荐",
              com: "请推荐学习XX技能的渠道，免费的和收费的各列",
            },
            {
              name: "学习方法",
              com: "请推荐XX技能的学习方法，帮助制定学习计划",
            },
            {
              name: "学习建议",
              com: "请给我一些XX技能学习建议",
            },
          ],
        },
      ],
    },
    {
      name: "薪资水平",
      cor: "#4da3f0",
      introduce: "本模块分为地域薪资、企业薪资、岗位薪资三个模块，每个模块分为五个步骤。学生根据相应步骤提示词，了解不同地区的薪资水平及生活成本，掌握不同类型企业的薪资结构及福利待遇，并通过分析岗位的薪资差异及增长潜力，获得科学的薪资预期及职业选择建议。最后，学生将结合地域、企业及岗位的薪资数据，制定合理的职业发展目标。",
      children: [
        {
          title: "地域新资",
          child: [
            {
              name: "各地新资概况",
              com: "我国各大主要城市xx行业新资水平",
            },
            {
              name: "各地新资对比",
              com: "请对比上述城市新资水平并进行排序，输出表格",
            },
            {
              name: "意向地新资",
              com: "我想在xx城市工作，请根据该城市xx相关行业新资水平",
            },
            {
              name: "意向地新资",
              com: "xx城市针对大学生就业有何帮扶政策及福利？",
            },
            {
              name: "地点推荐",
              com: "请根据当地新资水平和福利政策，客观推荐工作地点",
            },
          ],
        },
        {
          title: "企业新资",
          child: [
            {
              name: "各企业新资",
              com: "xx相关行业企业新资水平如何？请列出xx家企业新资水平",
            },
            {
              name: "新资对比分析",
              com: "请对比分析上述企业新资福利情况，并进行排序，输出表格",
            },
            {
              name: "意向企业新资",
              com: "xx企业新资水平如何，该企业各岗位新资水平如何？输出表格",
            },
            {
              name: "意向企业福利",
              com: "xx企业整体福利状况如何？各类岗位福利状况如何？输出表格",
            },
            {
              name: "企业推荐",
              com: "请根据企业新资福利状况，客观推荐xx家适合就业的企业",
            },
          ],
        },
        {
          title: "岗位新资",
          child: [
            {
              name: "岗位新资对比",
              com: "xxx相关岗位新资对比情况，输出表格",
            },
            {
              name: "初级岗位新资",
              com: "xxx相关初级岗位新资水平，列出x个岗位，输出表格",
            },
            {
              name: "中级岗位新资",
              com: "xxx相关中级岗位新资水平，列出x个岗位，输出表格",
            },
            {
              name: "高级岗位新资",
              com: "xxx相关高级岗位新资水平，列出x个岗位，输出表格",
            },
            {
              name: "适合岗位",
              com: "针对刚毕业的xx学校大学生，期望新资是xxx到xxx，喜欢xx，不喜欢xx，请推荐合适的岗位",
            },
          ],
        },
      ],
    },
    {
      name: "简历优化",
      cor: "#56c8c5",
      introduce: "本模块分为个人信息、简历撰写、个人技能三个模块，每个模块分为五个步骤。学生根据相应步骤提示词，了解如何系统化整理个人信息，掌握简历撰写的核心技巧与注意事项，并通过针对性的技能提升建议，增强个人竞争力。最后，学生将结合个人信息、简历优化及技能提升，打造一份高质量的求职材料，为成功就业奠定坚实基础。",
      children: [
        {
          title: "个人信息",
          child: [
            {
              name: "基础信息",
              com: "年龄、性别、籍贯、所在地等信息",
            },
            {
              name: "教育信息",
              com: "毕业院校、专业、所学课程等信息",
            },
            {
              name: "性格特点",
              com: "个人性格特点概述，例如外向、开朗、擅长交际、稳重、谨慎等外界评价",
            },
            {
              name: "自我评价",
              com: "个人自我评价，个人兴趣爱好等自我认知",
            },
            {
              name: "自我期待",
              com: "未来希望在哪个方向发展，期待自己成为一个什么样的人",
            },
          ],
        },
        {
          title: "简历撰写",
          child: [
            {
              name: "简历模板",
              com: "上传自己撰写的简历初稿",
            },
            {
              name: "针对企业",
              com: "想要给xx企业投简历，请给出修改建议",
            },
            {
              name: "针对岗位类型",
              com: "意向岗位类型是xx，想要针对xx岗位，请给出修改建议",
            },
            {
              name: "AI版本简历",
              com: "继续补充相关信息，个性化进行简历修改",
            },
            {
              name: "终稿导出",
              com: "导出修改后的简历终稿",
            },
          ],
        },
        {
          title: "个人技能",
          child: [
            {
              name: "知识技能",
              com: "掌握的实务性较强的知识技能，学会的一些工具，如Python等",
            },
            {
              name: "技能证书",
              com: "考取的相关职业资格证，各类技能证书等",
            },
            {
              name: "实验实训",
              com: "开展的哪些技能实训",
            },
            {
              name: "企业实习",
              com: "是否有企业实习经历",
            },
            {
              name: "技能发展",
              com: "未来想在哪方面继续学习，精进技能",
            },
          ],
        },
      ],
    },
  ]);
  const claActive = ref(tabList.value[0].name);
  const tabactive = ref(tabList.value[0]);
  const stopActive = ref("理论知识");
  const currentNode = ref(null);
  const taskinfo = ref({});
  let abortController = null;
  const messages = ref([]);
  const domfile = ref(null);
  // **计算每个任务是否可点击**
  const isTaskEnabled = (list, index) => {
    if (index === 0) return true; // 第一个任务默认可点击
    console.log(list[index - 1].isTemplateBased, "list[index - 1].isTemplateBased");
    return list[index - 1].isTemplateBased ?? false;
  };
  // 发送消息
  const sendMessage = async () => {
    if (!inputMessage.value.trim() || isRequestPending.value) return proxy.$message.warning("请输入内容");
    isRequestPending.value = true;
    isTypingComplete.value = true; // 重置为初始状态
    let isTemplateBased = false;
    if (currentNode.value) {
      isTemplateBased = isBasedOnCom(inputMessage.value, currentNode.value.com);
      if (!isTemplateBased) {
        isRequestPending.value = false;
        isTypingComplete.value = false; // 重置为初始状态
      } else {
        isRequestPending.value = true;
        isTypingComplete.value = true; // 重置为初始状态
      }
    }
    // 重置状态
    // 添加用户消息
    messages.value.push({
      id: Date.now(),
      role: "user",
      content: inputMessage.value,
      module: claActive.value,
      stop: stopActive.value,
      isTemplateBased: isTemplateBased,
      name: currentNode.value?.name ?? "",
    });
    // 用户提问后立即滚动到底部
    nextTick(() => {
      scrollToBottom();
      inputMessage.value = "";
    });
    abortController = new AbortController();
    const signal = abortController.signal;
    // 调用后端API
    let params = {
      systemOwner: "电商互联网产品",
      userId: userStore.userInfo.userId,
      type: "2",
    };
    isTypingComplete.value = false;
    // 将参数对象转换为查询字符串
    const queryString = new URLSearchParams(params);
    const aiBase = (import.meta.env.VITE_APP_AI_API || "/ai/").replace(/\/?$/, "/");
    const resp = await fetch(`${aiBase}api/kimi4?${queryString}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: inputMessage.value.trim(),
      signal,
    });
    // 添加AI消息占位
    let newMessage = reactive({ id: Date.now(), role: "assistant", content: "", module: claActive.value, stop: stopActive.value, name: currentNode.value?.name ?? "" });
    messages.value.push(newMessage); // 滚动到底部
    // 处理流式响应
    const reader = resp.body.getReader();
    const decoder = new TextDecoder("utf-8");
    while (true) {
      const { done, value } = await reader.read();
      isRequestPending.value = false;
      if (done) break;
      const text = decoder.decode(value, { stream: true });
      await typeText(newMessage, text);
      //滚动到最新消息
      nextTick(() => {
        const scrollEl = scrollArea.value.wrapRef;
        scrollEl.scrollTop = scrollEl.scrollHeight;
      });
    }
  };
  const isBasedOnCom = (input, com) => {
    // 1. 将 'xxx' 替换为匹配任意字符（包括换行）
    const comPattern = com.replace(/xxx/g, "([\\s\\S]+)");
    // 2. 构造正则，确保其严格匹配其他部分
    const regex = new RegExp(`^${comPattern}$`);
    // 3. 测试用户输入是否符合模式
    return regex.test(input);
  };
  const typeText = (message, text) => {
    return new Promise((resolve) => {
      isTypingComplete.value = false; // 重置状态
      let index = 0;
      message.content = ""; // 清空原内容
      const textLength = text.length;
      const chunkSize = textLength > 100 ? 3 : 1; // 根据文本长度动态调整批量字数
      const typingSpeed = textLength > 100 ? 30 : 60; // 适配长短文本
      const interval = setInterval(() => {
        if (index < text.length) {
          message.content += text.slice(index, index + chunkSize);
          index += chunkSize;
          if (index % 10 === 0) nextTick(() => scrollToBottom()); // 限制滚动更新频率
        } else {
          clearInterval(interval);
          isTypingComplete.value = true;
          AI.submitTaskStep({
            ...taskinfo.value,
            contentOne: JSON.stringify(messages.value),
          }).then((res) => {
            createPracticalTraining();
          });
          nextTick(() => scrollToBottom());
          resolve();
        }
      }, typingSpeed);
    });
  };
  // 滚动到底部的函数
  const scrollToBottom = () => {
    const scrollEl = scrollArea.value.wrapRef;
    if (scrollEl) {
      scrollEl.scrollTop = scrollEl.scrollHeight;
    }
  };
  const scrollToElement = async (dataName) => {
    await nextTick(); // 确保 DOM 更新完成
    const scrollContainer = scrollArea.value?.wrapRef; // 获取 el-scrollbar 内部滚动容器
    const targetElement = scrollContainer?.querySelector(`[data-name="${dataName}"]`);
    if (targetElement) {
      const top = targetElement.offsetTop; // 计算元素距离父容器顶部的偏移量
      scrollContainer.scrollTo({ top, behavior: "smooth" }); // 平滑滚动
    } else {
      console.error(`未找到 data-name="${dataName}" 的元素`);
    }
  };
  // 中断对话
  const gInterrupt = () => {
    if (abortController) {
      abortController.abort();
      abortController = null;
      isRequestPending.value = false;
      isTypingComplete.value = true;
    }
  };
  const onClick = (type, idx) => {
    const element = document.querySelectorAll(".stop-item"); // 选择你的元素
    if (element && element[idx].classList.contains("info-title-completedSteps-disdabled")) return (inputMessage.value = "");
    if (!type?.isTemplateBased) {
      let arr = ["课程知识点", "核心知识点", "最难的部分", "实务知识概述", "学习建议", "业务现状", "理论研究", "发展趋势"];
      if (arr.includes(type.name)) {
        inputMessage.value = type.com.replace("xxx", "电商互联网产品开发实训系统");
      } else {
        inputMessage.value = type.com;
      }
    }
    currentNode.value = type; //记录当前步骤节点
    scrollToElement(type.name);
  };
  const changeTab = (type) => {
    tabactive.value = type;
    claActive.value = type.name;
    stopActive.value = type.children[0].title;
    createPracticalTraining();
  };
  const copyTextSuccess = () => {
    proxy.$modal.msgSuccess("复制成功");
  };
  const getCurrentTeachingClassId = async () => {
    const cachedClassId = proxy.$cache.session.get("currentTeachingClassId");
    if (cachedClassId) return cachedClassId;
    const res = await indexApi.getCurrentTeachingClass({ userId: userStore.userInfo.userId });
    const classId = res?.data?.schoolClassId || "";
    if (classId) {
      proxy.$cache.session.set("currentTeachingClassId", classId);
    }
    return classId;
  };
  // 选择步骤
  const changeStop = (stop) => {
    stopActive.value = stop;
    setTimeout(() => {
      createPracticalTraining();
    }, 0);
  };
  // 创建任务
  const createPracticalTraining = async () => {
    await nextTick(); // 等待 Vue 状态更新完成
    const currentClassId = await getCurrentTeachingClassId();
    if (!currentClassId) return;
    const requestData = {
      systemOwner: "区块链金融",
      userId: userStore.userInfo.userId,
      classId: currentClassId,
      schoolId: userStore.userInfo.schoolId,
      module: claActive.value,
      step: stopActive.value, // 确保这里传递的是最新的值
    };
    AI.createTask(requestData).then((res) => {
      if (res) {
        taskinfo.value = res.data;
        messages.value = res.data.contentOne ? JSON.parse(res.data.contentOne) : [];
        setTimeout(() => {
          syncBackendStatus(messages.value);
        }, 0);
      }
    });
  };
  // 同步后端状态到前端 tabList
  const syncBackendStatus = (backendMessages) => {
    // 分离 user 和 assistant 消息
    const userMessages = backendMessages.filter((message) => message.role === "user" && message.isTemplateBased === true);
    const assistantMessages = backendMessages.filter((message) => message.role === "assistant");
    // 遍历 user 消息
    userMessages.forEach((userMessage) => {
      const { module, stop, name } = userMessage;
      // 查找对应的 assistant 消息，要求 name、module、stop 一致且 content 不为空
      const matchingAssistant = assistantMessages.find((assistant) => assistant.module === module && assistant.stop === stop && assistant.name === name && assistant.content && assistant.content.trim() !== "");
      // 如果没有匹配的 assistant 消息或 content 为空，直接返回
      if (!matchingAssistant) return;
      // 查找对应的选项卡
      const tab = tabList.value.find((tab) => tab.name === module);
      if (!tab) return;
  
      // 查找对应的任务
      const task = tab.children.find((child) => child.title === stop);
      if (!task) return;
      // 查找对应的步骤
      const step = task.child.find((child) => child.name === name);
      if (step) {
        // 只有当 assistant 消息匹配且 content 不为空时，设置为 true
        step.isTemplateBased = true;
        // console.log('同步后的步骤:', step);
      }
    });
  };
  // 会话记录导出
  const exportSession = (id) => {
    AI.exportSession({
      missionModuleId: taskinfo.value.missionModuleId,
    }).then((res) => {
      // 创建 Blob 对象
      const blob = new Blob([res], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
      // 创建下载链接
      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = `${stopActive.value}会话数据.xlsx`; // 这里可以自定义文件名
      document.body.appendChild(link);
      // 触发下载
      link.click();
      // 清理
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
    });
  };
  const submitDocument = (val) => {
    domfile.value = val.raw;
    const formData = new FormData();
    formData.append("file", domfile.value);
    formData.append("missionModuleId", taskinfo.value.missionModuleId);
    AI.uploadResourceFile(formData).then((res) => {
      AI.submitTask({
        ...taskinfo.value,
        uploadUrl: res.data,
      }).then((res) => {
        createPracticalTraining();
        proxy.$modal.msgSuccess("提交成功");
      });
    });
  };
  // 判断数组中所有元素是否都是 true，且数组不为空
  const allTrue = () => {
    // 根据stopActive和claActive的值筛选tabList中的数据
    const filteredTabList = tabList.value.filter((tab) => tab.name === claActive.value);
    const filteredTasks = filteredTabList[0].children.filter((task) => task.title === stopActive.value);
    const steps = filteredTasks[0].child;
    return steps.every((step) => step.isTemplateBased);
  };
  // 计算高度的函数
  const updateHeight = () => {
    const windowHeight = window.innerHeight; // 视口高度
    const headerHeight = header.value?.offsetHeight || 0; // 头部高度
    const footerHeight = footer.value?.offsetHeight || 0; // 底部高度
    const stepHeight = step.value?.offsetHeight || 0; // 底部高度
    const totalOffset = headerHeight + footerHeight + stepHeight; // 总偏移量
    scrollbarHeight.value = `${windowHeight - totalOffset - 150}px`;
  };
  onMounted(() => {
    createPracticalTraining();
    updateHeight();
    // 监听窗口大小变化
    window.addEventListener("resize", updateHeight);
    // 使用 ResizeObserver 监听头部和底部的尺寸变化
    resizeObserver = new ResizeObserver(() => {
      updateHeight(); // 元素尺寸变化时重新计算
    });
    if (header.value) resizeObserver.observe(header.value);
    if (footer.value) resizeObserver.observe(footer.value);
  });
  // 清理监听
  onUnmounted(() => {
    window.removeEventListener("resize", updateHeight);
    if (resizeObserver) {
      resizeObserver.disconnect();
    }
  });
  </script>
  <style lang="scss">
  .index {
    padding: 20px 40px;
    height: calc(100vh - 2.5rem);
    position: relative;
    background: url("@/assets/images/背景.webp") no-repeat;
    background-size: 100% 100%;
    .min-h-screen {
      text-align: center;
      .title {
        h1 {
          font-size: 2.25rem;
          line-height: 2.5rem;
          margin-top: 0px;
          color: #ffffff;
          font-weight: 600;
        }
      }
    }
    .tz-tobs {
      display: grid;
      grid-template-columns: repeat(6, 1fr);
      grid-gap: 1rem;
      margin-top: 1rem;
      width: calc(100vw - 50rem);
      margin: 0 auto;
      margin-bottom: 10px;
      .el-tobs-item {
        position: relative;
        height: 3rem;
        border-radius: 8px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        text-align: center;
        cursor: pointer;
        color: #ffffff;
        font-weight: normal;
        // 超出
        overflow: hidden;
        .svg-icon {
          width: 2rem;
          height: 2rem;
        }
        .el-tobs-item-active {
          .svg-icon {
            // 翻转180度
            transform: rotate(0deg);
            fill: #19578b;
          }
        }
      }
      .active {
        //放大
        transform: scale(1.12);
        font-size: var(--student-fs-h1);
        font-weight: 600;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
        color: #ffffff;
      }
      .el-tobs-item-active {
        .svg-icon {
          // 翻转180度
          transform: rotate(0deg);
        }
        .svg-icon {
          transform: rotate(180deg);
        }
        &:hover {
          transition: all 0.2s;
          background-color: #eff6ff;
        }
      }
      .el-tobs-item-active {
        .svg-icon {
          // 翻转180度
          transform: rotate(0deg);
          fill: #19578b;
        }
      }
      .active {
        background: #eff6ff;
        border-color: rgb(191 219 254);
      }
    }
    .tz-centent {
      width: calc(100vw - 50rem);
      height: calc(100vh - 25rem);
      margin: 0 auto;
      .QAlist {
        padding-left: 0px;
        li {
          display: flex !important;
          overflow: hidden;
          margin-bottom: 12px;
          :deep(.el-loading-mask) {
            background: transparent !important;
          }
        }
        .task-introduction {
          color: #ffffff;
          .title {
            font-size: 1.2rem;
          }
          // p除了第一个
          p:not(:first-child) {
            margin-top: 1rem;
            font-size: 0.8rem !important;
          }
          ul {
            margin: 0;
            padding: 0;
            width: 30rem;
            li {
              margin-top: 10px;
              background: #011031;
              border: 1px solid #003e7c;
              border-radius: 12px 12px 12px 0;
              padding: 10px 12px 10px 12px;
              font-size: var(--student-fs-body);
              color: #ffffff;
              // 鼠标小手
              cursor: pointer;
              line-height: 22px;
              &:hover {
                background: #002c5b;
              }
            }
          }
        }
        :deep(.md-editor-preview-wrapper) {
          padding: 0px 0px !important;
          .md-editor-preview {
            font-size: var(--student-fs-body) !important;
          }
        }
        .questionItem {
          // display: flex;
          justify-content: flex-end;
          .qustionCon {
            background: #ffffff;
            border-radius: 12px 12px 0 12px;
            // padding: 0px 12px 0px 16px;
            font-weight: 400;
            font-size: var(--student-fs-body);
            line-height: 20px;
            max-width: calc(100% - 51px);
            word-break: break-all;
          }
        }
        .answerItem {
          display: flex;
          margin-bottom: 12px;
          overflow: hidden;
          :deep(.answerCon) {
            display: flex;
            font-weight: 400;
            font-size: var(--student-fs-body);
            // color: rgb(68, 68, 68);
            line-height: 22px;
            // width: calc(100% - 51px);
            word-break: break-all;
            flex: 1 1 0%;
            padding: 0px 16px 0px 16px;
          }
        }
        margin-top: 10px;
      }
      .tz-step {
        margin-top: 2rem;
        width: 100%;
        height: 8rem;
        display: flex;
        gap: 2rem;
        .tz-stop-info {
          width: calc(100% - 90%);
          overflow: hidden;
          .info-title {
            font-size: 1rem;
            font-weight: 600;
            color: #ffffff;
            background: #19578b;
            text-emphasis: none;
            text-align: center;
            padding: 0.5rem 0px;
            border-radius: 0.5rem;
            margin-bottom: 0.5rem;
            width: 100%;
            // 鼠标小手
            cursor: pointer;
            &:hover {
              background: #2d89e3;
              transition: all 0.3s;
            }
          }
          .info-title-active {
            background: #2d89e3;
            transition: all 0.3s;
          }
        }
        .tz-stop-centent {
          flex: 1;
          background: #002c5b;
          border-radius: 0.5rem;
          min-width: calc(100% - 20%); /* 确保宽度自适应 */
          overflow: hidden; /* 防止内容溢出 */
          // padding-left: 5rem;
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 0px 4rem 0 4rem;
          .stop {
            flex: 1; /* 让 .stop 容器占据剩余空间 */
            height: 100%;
            display: flex;
            align-items: center;
            gap: 2rem;
            .stop-item {
              width: 6rem;
              height: 3rem;
              line-height: 3rem;
              text-align: center;
              border-radius: 0.4rem;
              background: #06233f;
              font-size: 0.9rem;
              color: #ffffff;
              position: relative;
              cursor: pointer;
            }
            .stop-item:not(:last-child) {
              &::after {
                content: "→";
                position: absolute;
                top: 0;
                right: -1.8rem;
                display: block;
                color: red;
                font-size: 23px;
              }
            }
            .info-title-completedSteps {
              background: #2378cb;
            }
            .info-title-completedSteps:not(:last-child) {
              &::after {
                content: "→";
                position: absolute;
                top: 0;
                right: -1.8rem;
                display: block;
                color: #2378cb !important;
                font-size: 23px;
              }
            }
            .info-title-completedSteps-disdabled {
              background: #d6dee8;
              cursor: not-allowed;
            }
          }
          .fbtn {
            .el-button {
              height: 3rem;
            }
          }
        }
      }
    }
    .tz-footer-ipt {
      position: absolute;
      left: 50%;
      bottom: 20px;
      transform: translateX(-50%);
      width: calc(100vw - 50rem);
      height: 11rem;
      background: #001d40;
      margin: 0 auto;
      border-radius: 35px;
      padding: 10px;
      border: 1px solid #003e7c;
      .svg-icon {
        width: 3rem;
        height: 2.7rem;
        // 鼠标小手
        cursor: pointer;
      }
      .disdabled {
        // 禁用
        cursor: not-allowed;
        .svg-icon {
          fill: #d6dee8;
        }
      }
      .el-textarea {
        .el-textarea__inner {
          background: transparent !important;
          box-shadow: none !important;
          // 光标颜色
          border-radius: 0px;
          color: #ffffff;
          &::placeholder {
            color: #ffffff;
          }
        }
      }
    }
  }
  .qustionCon {
    .md-editor {
      --md-color: #333333 !important;
      --md-bk-color: transparent !important;
    }
  }
  .answerCon {
    display: flex;
    gap: 1rem;
    position: relative;
    padding-right: 10px;
    .copy {
      position: absolute;
      top: 5px;
      right: 12px;
      display: none;
      .svg-icon {
        fill: #ffffff;
        width: 1.5rem;
        height: 1.5rem;
      }
    }
    &:hover {
      .copy {
        display: block;
        cursor: pointer;
      }
    }
    .md-editor {
      background: #011031;
      border-radius: 10px;
      border: 1px solid #003e7c;
      --md-color: #ffffff !important;
      --md-bk-color: #02132f !important;
    }
  }
  .el-loading-mask {
    background: transparent !important;
    .el-loading-spinner {
      text-align: left;
      padding-left: 2.5rem;
    }
  }
  .qustionCon {
    position: relative;
    padding-left: 10px;
    .copy {
      position: absolute;
      top: 5px;
      left: 8px;
      display: none;
      .svg-icon {
        fill: #333333;
        width: 1.5rem;
        height: 1.5rem;
      }
    }
    &:hover {
      .copy {
        display: block;
        cursor: pointer;
      }
    }
  }
  .el-scrollbar {
    --el-scrollbar-bg-color: transparent !important;
  }
  // 媒体查询监听视口小于1920
  @media screen and (max-width: 1700px) {
    .el-textarea {
      height: 2rem !important;
      .el-textarea__inner {
        font-size: 0.8rem;
      }
    }
    .tz-step {
      .tz-stop-info {
        display: none;
      }
      .tz-stop-centent {
        padding: 0px 1rem 0 1rem !important;
        .stop-item {
          width: 4rem !important;
          font-size: 0.7rem !important;
        }
      }
    }
    .tz-footer-ipt {
      height: 6rem !important;
    }
  }
  </style>
  
