<template>
  <div class="reading-layout">
    <!-- 左栏：文件管理树 -->
    <div class="left-panel" :style="{ width: leftCollapsed ? '0px' : leftWidth + 'px' }" v-show="!leftCollapsed">
      <div class="panel-card">
        <div class="left-header">
          <div class="left-title">文件管理</div>
          <div class="left-actions">
            <img src="@/assets/images/reading/create.png" alt="新建" title="新建文件夹" class="action-icon" @click="createFolder()">
            <span class="collapse-btn" title="收起" @click="leftCollapsed = true">«</span>
          </div>
        </div>

        <div class="left-search">
          <el-input v-model="searchWord" placeholder="搜索文件..." size="small" clearable>
            <template #prefix>
              <img src="@/assets/images/search.png" style="width:14px;height:14px;">
            </template>
          </el-input>
        </div>

        <div class="left-tree">
          <el-tree
            ref="treeRef"
            :data="filteredTreeData"
            :props="treeProps"
            node-key="treeId"
            :expand-on-click-node="false"
            :default-expand-all="false"
            highlight-current
            show-checkbox
            @node-click="handleNodeClick"
            @check-change="handleCheckChange"
          >
            <template #default="{ node, data }">
              <div class="tree-node" :class="{ 'is-file': !data.isFolder, 'is-selected-file': selectedFile && selectedFile.fileId === data.id && !data.isFolder }">
                <div class="tree-node-content">
                  <img v-if="data.isFolder" src="@/assets/images/reading/folder.png" class="tree-icon folder-icon">
                  <span v-else class="tree-file-icon">{{ getFileIcon(data.label) }}</span>
                  <span class="tree-label" :title="data.label">{{ data.label }}</span>
                </div>
                <div class="tree-node-actions" @click.stop>
                  <el-dropdown trigger="click" size="small">
                    <img src="@/assets/images/reading/operates.png" class="tree-operate-icon">
                    <template #dropdown>
                      <el-dropdown-menu>
                        <template v-if="data.isFolder">
                          <el-dropdown-item @click="uploadToFolder(data)">上传文件</el-dropdown-item>
                          <el-dropdown-item @click="createFolder(data)">重命名</el-dropdown-item>
                          <el-dropdown-item @click="delFolder(data)" style="color:#BE0000">删除</el-dropdown-item>
                        </template>
                        <template v-else>
                          <el-dropdown-item @click="renameFile(data)">重命名</el-dropdown-item>
                          <el-dropdown-item @click="downLoadDoc(data)">下载</el-dropdown-item>
                          <el-dropdown-item @click="delFileItem(data)" style="color:#BE0000">删除</el-dropdown-item>
                        </template>
                      </el-dropdown-menu>
                    </template>
                  </el-dropdown>
                </div>
              </div>
            </template>
          </el-tree>
        </div>

        <div class="left-footer">
          <div class="batch-del-btn" @click="delBatch">
            <img src="@/assets/images/reading/del.png" alt="">
            <span>批量删除</span>
          </div>
        </div>
      </div>
    </div>

    <!-- 左栏展开按钮（折叠时显示） -->
    <div class="expand-btn" v-if="leftCollapsed" @click="leftCollapsed = false" title="展开文件管理">»</div>

    <!-- 左分隔条（可拖拽） -->
    <div class="splitter" v-show="!leftCollapsed" @mousedown="startResizeLeft"></div>

    <!-- 中间栏：文件预览 -->
    <div class="center-panel">
      <div class="panel-card">
        <div v-if="!selectedFile" class="center-placeholder">
          <div class="placeholder-content">
            <img src="@/assets/images/reading/empty.png" alt="" style="width:80px;height:80px;opacity:0.5;">
            <p>点击左侧文件查看内容</p>
          </div>
        </div>
        <div v-else class="center-content">
          <div class="center-header">
            <span class="center-title" :title="selectedFile.fileName">{{ selectedFile.fileName }}</span>
          </div>
          <div class="file-content" ref="fileContent" id="file-content">
            <div class="view-md" id="file-html-content" v-html="docHtml"></div>
            <div id="note-content" :title="noteContent" class="file-note"></div>
          </div>
        </div>
      </div>
    </div>

    <!-- 右分隔条（可拖拽） -->
    <div class="splitter" @mousedown="startResizeRight"></div>

    <!-- 右栏：ReadingBox -->
    <div class="right-panel" :style="{ width: rightWidth + 'px' }">
      <div class="panel-card right-card">
        <div v-if="!selectedFile" class="right-placeholder">
          <div class="placeholder-content">
            <img src="@/assets/images/reading/empty.png" alt="" style="width:60px;height:60px;opacity:0.4;">
            <p class="placeholder-title">知识库问答</p>
            <p class="placeholder-sub">选择文件后开始提问</p>
          </div>
        </div>
        <ReadingBox v-else ref="readingBox" />
      </div>
    </div>

    <!-- 右键菜单 -->
    <div class="click-bar-button" id="shortMenuDom" ref="shortMenuDom" v-show="shortMenuShow">
      <template v-for="(item, index) in contentMenuList" :key="index">
        <div class="menu-name" @click="fileContentMenu(index)">{{ item }}</div>
        <div class="vertical-line" v-if="index < contentMenuList.length - 1"></div>
      </template>
    </div>
  </div>

  <!-- 创建/重命名文件夹弹框 -->
  <el-dialog v-model="folderDialogVisible" :title="folderDialogTitle" width="500" :close-on-click-modal="false">
    <el-input v-model="submitFolderParam.name" placeholder="请输入文件夹名称" @keyup.enter="submitFolderName"></el-input>
    <template #footer>
      <el-button @click="folderDialogVisible = false">取消</el-button>
      <el-button type="primary" @click="submitFolderName">确定</el-button>
    </template>
  </el-dialog>

  <!-- 重命名文件弹框 -->
  <el-dialog v-model="showRenameDialog" title="重命名" width="500" :close-on-click-modal="false">
    <el-input v-model="renameDocName" placeholder="请输入文件名称" />
    <template #footer>
      <el-button @click="showRenameDialog = false">取消</el-button>
      <el-button type="primary" @click="saveDocName">保存</el-button>
    </template>
  </el-dialog>

  <!-- 上传文件弹框 -->
  <el-dialog v-model="showUploadDialog" title="上传文件" width="715" @close="closeUpload()" :close-on-click-modal="false">
    <el-upload class="upload-demo" action="#" :auto-upload="false" accept=".pdf,.docx,.txt,.xlsx,.xls"
        name="files" multiple :show-file-list="false" :on-change="handleFileChange">
      <el-icon :size="100" class="el-icon--upload" style="display: flex;flex-wrap: wrap">
        <upload-filled style="color: #004EA0"/>
        <span style="margin-top:-50px;font-size:13px; font-style: normal;color:#004EA0">选择文件</span>
      </el-icon>
      <template #tip>
        <div class="el-upload__tip">支持TXT/DOCX/PDF/XLSX/XLS格式文件，文件大小不超过10M</div>
      </template>
    </el-upload>
    <div class="files-upload-result">
      <div class="result" v-for="item in uploadingFileList" :key="item.uid">
        <div class="file-name" :title="item.name">{{ item.name }}</div>
        <div class="result-info">
          <div class="fail-msg" v-if="item.status === 'fail'" :title="item.message">{{ item.message }}</div>
          <img class="upload-remove" v-if="item.status === 'ready'" src="@/assets/images/reading/upload_remove.png" @click="handleRemove(item)">
          <div class="loading" v-if="item.status === 'uploading'"><Loading /></div>
          <img v-if="item.status === 'success'" src="@/assets/images/reading/upload_success.png">
          <img v-if="item.status === 'fail'" src="@/assets/images/reading/upload_fail.png">
        </div>
      </div>
    </div>
    <template #footer>
      <div style="text-align: center" v-if="uploadingFileList.length > 0">
        <el-button @click="closeUpload()">关闭</el-button>
        <div class="dialog-tips"><img src="@/assets/images/reading/tips.png"><span>关闭弹窗后文件将继续上传</span></div>
      </div>
    </template>
  </el-dialog>

  <!-- 添加笔记弹框 -->
  <el-dialog v-model="showAddNoteDialog" title="写下你的想法吧" width="715" :close-on-click-modal="false" @open="openAddNoteDialog">
    <el-input ref="noteMsgRef" v-model="noteMsg" type="textarea" :rows="10" placeholder="请输入笔记内容"/>
    <template #footer>
      <div style="text-align: center;"><el-button type="primary" @click="submitNote()">发表</el-button></div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import {onMounted, onUnmounted, ref, reactive, provide, nextTick, computed, watch} from "vue";
import {
  getKnowledgeBaseList, addKnowledgeBase, editKnowledgeBase, delKnowledgeBase,
  getKnowledgeBaseContent, uploadFile, editFile, delFile, delFiles,
  downloadFile, getFileContent, addFileNote, getSize
} from "@/api";
import {withLoading} from "@/utils/loading";
import {copyToClip, getGlobalSelectionPosition} from "@/utils";
import {transforMd} from "@/utils/markdown";
import ReadingBox from "@/components/ReadingBox.vue";
import Loading from "@/components/Loading.vue";
import {UploadFilled} from '@element-plus/icons-vue';
import {ElMessage, ElMessageBox, type UploadFile, type UploadFiles} from "element-plus";
import Mark from "mark.js";

// ===================== 面板尺寸与折叠 =====================
const leftWidth = ref(280);
const rightWidth = ref(420);
const leftCollapsed = ref(false);
let resizingPanel = '' as '' | 'left' | 'right';
let startX = 0;
let startWidth = 0;

const startResizeLeft = (e: MouseEvent) => {
  resizingPanel = 'left';
  startX = e.clientX;
  startWidth = leftWidth.value;
  document.addEventListener('mousemove', onResize);
  document.addEventListener('mouseup', stopResize);
  document.body.style.cursor = 'col-resize';
  document.body.style.userSelect = 'none';
};
const startResizeRight = (e: MouseEvent) => {
  resizingPanel = 'right';
  startX = e.clientX;
  startWidth = rightWidth.value;
  document.addEventListener('mousemove', onResize);
  document.addEventListener('mouseup', stopResize);
  document.body.style.cursor = 'col-resize';
  document.body.style.userSelect = 'none';
};
const onResize = (e: MouseEvent) => {
  if (resizingPanel === 'left') {
    const newWidth = startWidth + (e.clientX - startX);
    leftWidth.value = Math.max(200, Math.min(500, newWidth));
  } else if (resizingPanel === 'right') {
    const newWidth = startWidth - (e.clientX - startX);
    rightWidth.value = Math.max(300, Math.min(600, newWidth));
  }
};
const stopResize = () => {
  resizingPanel = '';
  document.removeEventListener('mousemove', onResize);
  document.removeEventListener('mouseup', stopResize);
  document.body.style.cursor = '';
  document.body.style.userSelect = '';
};

// ===================== 文件树数据 =====================
const treeRef = ref(null);
const searchWord = ref('');
const treeData = ref<any[]>([]);
const treeProps = { children: 'children', label: 'label' };
const checkedNodes = ref<any[]>([]);

// ===================== 选中文件 =====================
const selectedFile = ref<any>(null);
provide('selectedFile', selectedFile);

// ===================== 文件预览 =====================
const docHtml = ref('');
const fileContent = ref(null);
const readingBox = ref(null);

// ===================== 笔记 =====================
const fileNote = reactive({ notes: [] as any[] });
const noteContent = ref('');
provide('fileNote', fileNote);

// ===================== 右键菜单 =====================
const shortMenuDom = ref(null);
const shortMenuShow = ref(false);
const selectText = ref('');
const contentMenuList = ['引用', '复制', '名词解释', '添加笔记', '翻译'];

// ===================== 引用 (provide to ReadingBox) =====================
const quoteMsg = ref<string>('');
provide('quoteMsg', quoteMsg);
provide('updateQuoteMsg', (newValue: string) => { quoteMsg.value = newValue; });

// ===================== 笔记弹框 =====================
const showAddNoteDialog = ref(false);
const noteMsg = ref('');
const noteMsgRef = ref(null);
provide('noteMsg', noteMsg);
provide('showAddNoteDialog', showAddNoteDialog);

interface NoteParam {
  id: string; fileId: number; pstartId: string; pendId: string;
  wordContent: string; startIndex: number; endIndex: number; noteContent: string;
}
interface PositionInfo {
  start: number; end: number; parentStart: string | null; parentEnd: string | null;
}
const noteParam = ref<NoteParam>({
  id: '', fileId: 0, pstartId: '', pendId: '',
  wordContent: '', startIndex: 0, endIndex: 0, noteContent: ''
});
provide('noteParam', noteParam);
const positionInfo = ref<PositionInfo>({ start: -1, end: -1, parentStart: '', parentEnd: '' });
const activeNoteIndex = ref(-1);
provide('activeNoteIndex', activeNoteIndex);
provide('highlightContentFlag', (note: any, newIndex: number) => {
  activeNoteIndex.value = newIndex;
  showFileNote(note, true);
});
const showFileNoteEvent = ref('');

// ===================== 文件夹弹框 =====================
const folderDialogVisible = ref(false);
const folderDialogTitle = ref("新建文件夹");
const submitFolderParam = reactive({ id: 0, name: "" });

// ===================== 文件重命名弹框 =====================
const showRenameDialog = ref(false);
const renameDocName = ref('');
const renameDocType = ref('');
const renameDocId = ref(0);

// ===================== 上传文件弹框 =====================
const showUploadDialog = ref(false);
const uploadFolderId = ref(0);
interface CusUploadFile extends UploadFile { message?: string | null }
const uploadingFileList = ref([] as CusUploadFile[]);
const isUploading = ref(false);
const uploadFinishFlag = ref(true);
const currentIndex = ref(0);
const totalFileSize = ref<number>(0);
const allowFileTypes = ["TXT", "DOCX", "PDF", "XLSX", "XLS"];
const FILE_MAX_SIZE = 10 * 1024 * 1024;
const TOTAL_FILE_MAX_SIZE = 50 * 1024 * 1024;

// ========================================
// 文件树：加载、过滤、checkbox
// ========================================
let treeIdCounter = 0;
const loadTreeData = async () => {
  try {
    let res = await getKnowledgeBaseList<any>({ folderType: "0" });
    if (res && res.data) {
      const folders = res.data.map((f: any) => ({
        treeId: 'folder-' + f.id,
        id: f.id, label: f.name, isFolder: true, children: [], raw: f, loaded: false
      }));
      treeData.value = folders;
    }
  } catch (error: any) {
    ElMessage.error(error?.message || '加载文件夹失败');
  }
};

const loadFolderFiles = async (folder: any) => {
  if (folder.loaded) return;
  try {
    let res = await getKnowledgeBaseContent<any>({
      folderType: "1", knowledgeBaseId: folder.id + "", patentId: "0"
    });
    if (res && res.data) {
      folder.children = res.data.map((f: any) => ({
        treeId: 'file-' + f.id,
        id: f.id, label: f.filename, isFolder: false, isLeaf: true, raw: f,
        folderId: folder.id, folderName: folder.label
      }));
    }
    folder.loaded = true;
  } catch (error: any) {
    ElMessage.error(error?.message || '加载文件列表失败');
  }
};

const filteredTreeData = computed(() => {
  if (!searchWord.value.trim()) return treeData.value;
  const keyword = searchWord.value.toLowerCase();
  return treeData.value.filter((folder: any) => {
    const folderMatch = folder.label.toLowerCase().includes(keyword);
    const childMatch = folder.children?.some((f: any) => f.label.toLowerCase().includes(keyword));
    return folderMatch || childMatch;
  }).map((folder: any) => ({
    ...folder,
    children: folder.children?.filter((f: any) =>
      f.label.toLowerCase().includes(keyword) || folder.label.toLowerCase().includes(keyword)
    )
  }));
});

const handleNodeClick = async (data: any) => {
  if (data.isFolder) {
    await loadFolderFiles(data);
    return;
  }
  const doc = data.raw;
  selectedFile.value = {
    fileId: doc.id, fileName: doc.filename, embeddingId: doc.embeddingId,
    folderId: data.folderId, folderName: data.folderName,
    articleAbstract: doc.articleAbstract || '暂无内容，请重试',
    articleKeywords: doc.articleKeywords || '暂无内容，请重试',
    articleParagraph: doc.articleParagraph || '暂无内容，请重试',
    fullContent: doc.context
  };
  await loadFileContent();
};

const handleCheckChange = () => {
  if (treeRef.value) {
    checkedNodes.value = (treeRef.value as any).getCheckedNodes();
  }
};

const getFileIcon = (filename: string) => {
  const ext = filename?.split('.').pop()?.toLowerCase() || '';
  const icons: Record<string, string> = {
    'pdf': '📄', 'docx': '📝', 'doc': '📝', 'txt': '📃', 'xlsx': '📊', 'xls': '📊', 'md': '📋'
  };
  return icons[ext] || '📄';
};

// ========================================
// 文件预览：加载内容
// ========================================
const pattern = /(\$\$.*?\$\$|\$.*?\$)/g;

const loadFileContent = async () => {
  if (!selectedFile.value) return;
  let offsets: number[] = [];
  try {
    let res = await withLoading(getFileContent)({
      fileId: selectedFile.value.fileId,
      embeddingId: selectedFile.value.embeddingId,
      knowledgeBaseId: selectedFile.value.folderId
    });
    if (res && res.code === 200 && res.data && res.data.content) {
      let content = res.data.content;
      content = content.replace(pattern, (match: string, _cg: string, offset: number) => {
        offsets.push(offset); return transforMd(match);
      });
      docHtml.value = content.replace(/<p>(.*?<span class="katex">.*?<\/span>.*?)<\/p>/g, '$1');
      fileNote.notes = res.data.notes || [];
    } else {
      docHtml.value = '<p style="color:#999;text-align:center;margin-top:40px;">文件内容获取失败</p>';
    }
  } catch {
    docHtml.value = '<p style="color:#999;text-align:center;margin-top:40px;">文件内容获取失败</p>';
  }
  await nextTick();
  try { handelLaTeXElements(offsets); handelCommentElements(); bindFileContentEvents(); handelNoteFlagMouseEvent(); } catch {}
};

provide('updateFileContent', async () => { await loadFileContent(); });

const handelLaTeXElements = (offsets: number[]) => {
  let els = document.querySelectorAll('span[class="katex"]');
  for (let i = 0; i < els.length; i++) els[i].setAttribute('id', 'katex-' + offsets[i]);
};
const handelCommentElements = () => {
  document.querySelectorAll('a[href^="#comment-"]').forEach(el => {
    let targetId = el.getAttribute('href');
    (el as HTMLElement).onclick = (e) => { e.preventDefault(); document.querySelector(targetId!)?.scrollIntoView({ behavior: 'smooth' }); };
  });
};

// ========================================
// 右键菜单
// ========================================
const fileContentMenu = (index: number) => {
  disShowShortMenu();
  if (index === 0) { quoteMsg.value = selectText.value?.slice(0, 1000) || ''; readingBox.value?.changeHead(2); }
  if (index === 1) { copyToClip(selectText.value); ElMessage.success("复制成功"); }
  if (index === 2) { readingBox.value?.changeHead(2); readingBox.value?.readingBoxSend("我想请你解释一下这个词语的含义：" + selectText.value, '1'); }
  if (index === 3) { readingBox.value?.changeHead(3); showAddNoteDialog.value = true; }
  if (index === 4) { readingBox.value?.changeHead(4); readingBox.value?.translateQuery(selectText.value); }
};
const disShowShortMenu = () => { shortMenuShow.value = false; };

const bindFileContentEvents = () => {
  if (!fileContent.value) return;
  fileContent.value.addEventListener('mouseup', (event: MouseEvent) => {
    activeNoteIndex.value = -1; displayFileNote(); removeHighlightSelectedWord();
    setTimeout(() => {
      const sel = window.getSelection(); if (!sel) return;
      selectText.value = sel.toString();
      positionInfo.value = getGlobalSelectionPosition(sel);
      if (selectText.value && shortMenuDom.value && fileContent.value?.contains(sel.anchorNode)) {
        shortMenuShow.value = true;
        (shortMenuDom.value as HTMLElement).style.left = event.clientX + 'px';
        (shortMenuDom.value as HTMLElement).style.top = event.clientY + 'px';
      }
    });
  });
  fileContent.value.addEventListener('scroll', () => {
    let noteEl = document.getElementById('note-' + (activeNoteIndex.value + 1));
    let ncEl = document.getElementById('note-content');
    if (noteEl && ncEl) {
      let top = noteEl.getBoundingClientRect().top - noteEl.getBoundingClientRect().height - 8;
      ncEl.style.top = top + 'px';
      ncEl.style.display = top < 75 ? 'none' : (activeNoteIndex.value != -1 ? 'block' : 'none');
    }
  });
};

// ========================================
// 笔记功能
// ========================================
const openAddNoteDialog = async () => { await nextTick(); noteMsgRef.value?.focus(); };

const submitNote = async () => {
  if (noteParam.value.id) {
    noteParam.value.noteContent = noteMsg.value;
    try { let res = await withLoading(addFileNote)(noteParam.value); if (res.code == 200) { ElMessage.success('笔记修改成功'); showAddNoteDialog.value = false; noteMsg.value = ''; await loadFileContent(); } else ElMessage.error('笔记修改失败'); } catch (e: any) { ElMessage.error(e?.message || '未知错误'); }
  } else {
    Object.assign(noteParam.value, { fileId: selectedFile.value?.fileId, pstartId: positionInfo.value.parentStart || '', pendId: positionInfo.value.parentEnd || '', wordContent: selectText.value, startIndex: positionInfo.value.start, endIndex: positionInfo.value.end, noteContent: noteMsg.value });
    try { let res = await withLoading(addFileNote)(noteParam.value); if (res.code == 200) { ElMessage.success('笔记添加成功'); showAddNoteDialog.value = false; noteMsg.value = ''; await loadFileContent(); } else ElMessage.error('笔记添加失败'); } catch (e: any) { ElMessage.error(e?.message || '未知错误'); }
  }
};

const displayFileNote = () => {
  document.querySelectorAll('span[id^="note-"]').forEach(el => el.classList.remove('note-flag-active'));
  let ncEl = document.getElementById('note-content'); if (ncEl) ncEl.style.display = 'none';
  document.querySelectorAll('.highlight').forEach(hl => { const p = hl.parentNode!; p.replaceChild(document.createTextNode(hl.textContent || ''), hl); p.normalize(); });
};

const showFileNote = (note: any, isPosition = false) => {
  displayFileNote(); if (!note) return;
  let noteEl = document.getElementById('note-' + (activeNoteIndex.value + 1));
  let ncEl = document.getElementById('note-content');
  let fcEl = document.getElementById("file-content"); if (!fcEl) return;
  const rcFC = fcEl.getBoundingClientRect();
  if (noteEl) {
    noteEl.classList.add('note-flag-active');
    new Mark('#file-html-content').markRanges([{ start: note.startIndex, length: note.endIndex - note.startIndex }], { className: 'highlight', exclude: ['.note-flag'] });
    if (isPosition) { let sels = document.querySelectorAll('mark[class^="highlight"]'); if (sels.length > 0) sels[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); }
    noteContent.value = note.noteContent;
    const rn = noteEl.getBoundingClientRect();
    if (ncEl) { ncEl.innerHTML = noteContent.value; ncEl.style.display = 'block'; let rnc = ncEl.getBoundingClientRect(); let left = rn.left - 10; if (left + rnc.width > rcFC.width) left = rcFC.width - rnc.width; ncEl.style.left = left + 'px'; ncEl.style.top = rn.top - rn.height - 8 + 'px'; }
  }
};

const removeHighlightSelectedWord = () => { document.querySelectorAll('.highlight').forEach(hl => { const p = hl.parentNode!; p.replaceChild(document.createTextNode(hl.textContent || ''), hl); }); };

const handelNoteFlagMouseEvent = () => {
  document.querySelectorAll('span[id^="note-"]').forEach(el => {
    el.addEventListener('mouseover', () => { showFileNoteEvent.value = 'mouseover'; let eid = Number(el.id.replace('note-', '')); for (let i = 0; i < fileNote.notes.length; i++) if (i + 1 == eid) { activeNoteIndex.value = i; showFileNote(fileNote.notes[i]); break; } });
    el.addEventListener('mouseleave', () => { if (showFileNoteEvent.value === 'mouseover') { activeNoteIndex.value = -1; displayFileNote(); } });
    el.addEventListener('click', async (e) => { showFileNoteEvent.value = 'click'; let eid = Number(el.id.replace('note-', '')); for (let i = 0; i < fileNote.notes.length; i++) if (i + 1 == eid) { activeNoteIndex.value = i; break; } readingBox.value?.changeHead(3); await nextTick(); document.getElementById('right-note-' + eid)?.scrollIntoView({ behavior: 'smooth' }); showFileNote(fileNote.notes[activeNoteIndex.value]); e.stopPropagation(); });
  });
};

// ========================================
// 文件夹操作
// ========================================
const createFolder = (item: any = null) => {
  if (item) { submitFolderParam.id = item.id; submitFolderParam.name = item.label || item.name; folderDialogTitle.value = "重命名文件夹"; }
  else { submitFolderParam.id = 0; submitFolderParam.name = ""; folderDialogTitle.value = "新建文件夹"; }
  folderDialogVisible.value = true;
};

const submitFolderName = async () => {
  if (!submitFolderParam.name.trim()) { ElMessage.warning("请输入文件夹名称"); return; }
  try {
    if (submitFolderParam.id !== 0) { let res = await withLoading(editKnowledgeBase<any>)({ id: submitFolderParam.id + "", name: submitFolderParam.name.trim(), folderType: "0" }); if (res?.code === 200) ElMessage.success("重命名成功"); else ElMessage.error(res.msg); }
    else { let res = await withLoading(addKnowledgeBase<any>)({ name: submitFolderParam.name.trim() }); if (res?.code === 200) ElMessage.success("文件夹创建成功"); else ElMessage.error(res.msg); }
    folderDialogVisible.value = false; await loadTreeData(); for (let f of treeData.value) await loadFolderFiles(f);
  } catch (e: any) { ElMessage.error(e?.message || '未知错误'); }
};

const delFolder = async (item: any) => {
  ElMessageBox.confirm("确定删除该文件夹？", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(async () => {
    try { let res = await withLoading(delKnowledgeBase<any>)({ knowledgeBaseId: item.id + "", folderType: "0" }); if (res?.code === 200) { ElMessage.success("删除成功"); await loadTreeData(); for (let f of treeData.value) await loadFolderFiles(f); } else ElMessage.error(res.msg); } catch (e: any) { ElMessage.error(e?.message || '未知错误'); }
  }).catch(() => {});
};

const delBatch = async () => {
  if (!treeRef.value) return;
  const checked = (treeRef.value as any).getCheckedNodes();
  const fileIds = checked.filter((n: any) => !n.isFolder).map((n: any) => n.id);
  const folderIds = checked.filter((n: any) => n.isFolder).map((n: any) => n.id);
  if (fileIds.length === 0 && folderIds.length === 0) { ElMessage.warning("请先勾选要删除的文件或文件夹"); return; }
  ElMessageBox.confirm(`确定删除选中的 ${folderIds.length} 个文件夹和 ${fileIds.length} 个文件？`, "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(async () => {
    try {
      if (fileIds.length > 0) await delFiles({ fileList: fileIds, folderList: [] });
      for (const fid of folderIds) await delKnowledgeBase<any>({ knowledgeBaseId: fid + "", folderType: "0" });
      ElMessage.success("删除成功");
      if (selectedFile.value && fileIds.includes(selectedFile.value.fileId)) { selectedFile.value = null; docHtml.value = ''; }
      await loadTreeData(); for (let f of treeData.value) await loadFolderFiles(f);
    } catch { ElMessage.error("删除失败"); }
  }).catch(() => {});
};

// ========================================
// 文件操作
// ========================================
const renameFile = (data: any) => {
  const fn = data.label || data.raw?.filename;
  if (fn) { let ns = fn.split('.'); renameDocName.value = ns[0]; renameDocType.value = ns.length >= 2 ? ns[ns.length - 1] : ''; }
  renameDocId.value = data.id; showRenameDialog.value = true;
};

const saveDocName = async () => {
  if (!renameDocName.value.trim()) { ElMessage.error("请输入文件名"); return; }
  let p = new FormData(); p.append('fileId', renameDocId.value + ''); p.append('fileName', renameDocName.value + (renameDocType.value ? '.' + renameDocType.value : '')); p.append('folderId', '0');
  try { await withLoading(editFile)(p); ElMessage.success("修改成功"); showRenameDialog.value = false; await refreshFolders(); } catch { ElMessage.error("修改失败"); }
};

const downLoadDoc = async (data: any) => {
  try { const resp = await downloadFile({ fileId: data.id }); const url = URL.createObjectURL(new Blob([resp])); const a = document.createElement('a'); a.href = url; a.setAttribute('download', data.label || data.raw?.filename); document.body.appendChild(a); a.click(); document.body.removeChild(a); } catch (e: any) { ElMessage.error(e?.message || '下载失败'); }
};

const delFileItem = (data: any) => {
  ElMessageBox.confirm("确认删除文档吗？", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(async () => {
    try { let res = await withLoading(delFiles)({ fileList: [data.id], folderList: [] }); if (res?.code === 200) { ElMessage.success("删除成功"); if (selectedFile.value?.fileId === data.id) { selectedFile.value = null; docHtml.value = ''; } await refreshFolders(); } else ElMessage.error("删除失败"); } catch { ElMessage.error("删除失败"); }
  }).catch(() => {});
};

const refreshFolders = async () => {
  for (let f of treeData.value) if (f.loaded) { f.loaded = false; await loadFolderFiles(f); }
};

// ========================================
// 上传文件
// ========================================
const uploadToFolder = async (data: any) => { uploadFolderId.value = data.id; try { let r = await getSize(); if (r.code === 200) totalFileSize.value = r.data; } catch {} showUploadDialog.value = true; if (uploadFinishFlag.value) uploadingFileList.value = []; };

const handleFileChange = async (file: UploadFile) => {
  const ext = file.name.split('.').pop()?.toUpperCase();
  if (!allowFileTypes.includes(ext as string)) { ElMessage.warning(file.name + '文件格式不匹配'); return; }
  if (file.size && file.size > FILE_MAX_SIZE) { ElMessage.warning(file.name + '文件大小已超过10M'); return; }
  totalFileSize.value += file.size || 0;
  if (totalFileSize.value > TOTAL_FILE_MAX_SIZE) { totalFileSize.value -= file.size || 0; ElMessage.warning('个人素材总量不能超过50M'); return; }
  uploadingFileList.value.push(file as CusUploadFile);
};

watch(uploadingFileList, nv => {
  if (!isUploading.value) for (let i = 0; i < nv.length; i++) if (nv[i].status === 'ready') { isUploading.value = true; nv[i].status = 'uploading'; uploadNext(); break; }
}, { immediate: true, deep: true });

const handleRemove = (file: UploadFile) => { uploadingFileList.value = uploadingFileList.value.filter(i => i.uid !== file.uid); };

const uploadNext = async () => {
  uploadFinishFlag.value = uploadingFileList.value.every(f => f.status === 'success' || f.status === 'fail');
  if (uploadFinishFlag.value) { isUploading.value = false; return; }
  const file = uploadingFileList.value[currentIndex.value]; file.status = 'uploading';
  const fd = new FormData(); fd.append('files', file.raw as Blob); fd.append('folderId', '0'); fd.append('knowledgeBaseId', uploadFolderId.value + '');
  try { let r = await uploadFile(fd); if (r.code && r.code != 200) { file.status = 'fail'; (file as CusUploadFile).message = r.msg; } else file.status = 'success'; }
  catch (e: any) { file.status = 'fail'; (file as CusUploadFile).message = e?.message || '未知错误'; }
  finally { currentIndex.value++; await uploadNext(); }
};

const closeUpload = () => { showUploadDialog.value = false; currentIndex.value = 0; refreshFolders(); };

// ========================================
// 生命周期
// ========================================
onMounted(async () => {
  await loadTreeData();
  for (let f of treeData.value) await loadFolderFiles(f);
  document.addEventListener('mousedown', (e: MouseEvent) => {
    let el = document.getElementById('shortMenuDom');
    if (!el?.contains(e.target as Node)) disShowShortMenu();
  });
});
</script>

<style lang="scss" scoped>
.reading-layout {
  display: flex;
  height: 100%;
  width: 100%;
  overflow: hidden;
  padding: 10px;
  gap: 0;
  box-sizing: border-box;
}

.panel-card {
  height: 100%;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 0 8px 1px rgba(180, 189, 221, 0.3);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

// ========== 左栏折叠/展开按钮 ==========
.expand-btn {
  width: 20px;
  height: 60px;
  background: #fff;
  border-radius: 0 8px 8px 0;
  box-shadow: 2px 0 6px rgba(180, 189, 221, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #004EA0;
  font-size: 14px;
  font-weight: bold;
  align-self: center;
  &:hover { background: #F0F4FF; }
}

// ========== 分隔条 ==========
.splitter {
  width: 6px;
  cursor: col-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  &:hover { background: rgba(0, 78, 160, 0.1); border-radius: 3px; }
  &::after { content: ''; width: 2px; height: 30px; background: #D5DDFF; border-radius: 1px; }
}

// ========== 左栏 ==========
.left-panel {
  min-width: 200px;
  height: 100%;
  flex-shrink: 0;
  transition: width 0.2s;

  .left-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 12px 6px 16px;
    .left-title { font-weight: bold; font-size: 15px; color: #000; }
    .left-actions {
      display: flex; align-items: center; gap: 8px;
      .action-icon { width: 18px; height: 18px; cursor: pointer; opacity: 0.7; &:hover { opacity: 1; } }
      .collapse-btn { font-size: 14px; cursor: pointer; color: #999; padding: 2px 4px; border-radius: 4px; &:hover { background: #F0F4FF; color: #004EA0; } }
    }
  }

  .left-search { padding: 4px 12px 8px 12px; }

  .left-tree {
    flex: 1;
    overflow-y: auto;
    padding: 0 4px;
    &::-webkit-scrollbar { width: 4px; }
    &::-webkit-scrollbar-thumb { background: #D5DDFF; border-radius: 2px; }

    :deep(.el-tree-node__content) { height: auto; padding: 1px 0; }
    :deep(.el-checkbox) { margin-right: 4px; }

    .tree-node {
      display: flex; align-items: center; justify-content: space-between; width: 100%;
      padding: 4px 6px; border-radius: 6px;
      &:hover { background: #F0F4FF; .tree-node-actions { opacity: 1; } }
      &.is-selected-file { background: #E0EAFF; }
      .tree-node-content {
        display: flex; align-items: center; flex: 1; min-width: 0;
        .folder-icon { width: 18px; height: 16px; margin-right: 5px; }
        .tree-file-icon { font-size: 13px; margin-right: 5px; }
        .tree-label { font-size: 13px; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
      }
      .tree-node-actions { opacity: 0; margin-left: 4px; .tree-operate-icon { width: 10px; cursor: pointer; padding: 4px; } }
    }
  }

  .left-footer {
    padding: 8px 12px; border-top: 1px solid #E6EDFF;
    .batch-del-btn {
      display: flex; align-items: center; justify-content: center; height: 30px;
      border: 1px solid #004EA0; border-radius: 4px; cursor: pointer; color: #004EA0; font-size: 13px;
      img { width: 14px; margin-right: 4px; }
      &:hover { background: #F0F4FF; }
    }
  }
}

// ========== 中间栏 ==========
.center-panel {
  flex: 1;
  height: 100%;
  min-width: 300px;
  overflow: hidden;

  .center-placeholder {
    flex: 1; display: flex; align-items: center; justify-content: center; height: 100%;
    .placeholder-content { text-align: center; color: #999; p { margin-top: 12px; font-size: 14px; } }
  }

  .center-content {
    flex: 1; display: flex; flex-direction: column; height: 100%; overflow: hidden;
    .center-header {
      padding: 12px 20px; border-bottom: 1px solid #E6EDFF;
      .center-title { font-weight: bold; font-size: 15px; color: #000; }
    }
    .file-content {
      flex: 1; overflow: auto; position: relative; padding: 0;
      .view-md {
        padding: 20px;
        :deep(p) { font-size: 15px; line-height: 1.8rem; margin-block-start: 0; }
        :deep(.highlight) { background: #D0EAC8; }
        :deep(.note-flag) { width: 23px; height: 28px; line-height: 28px; display: inline-block; text-align: center; font-weight: bold; font-size: 10px; margin-left: 8px; cursor: pointer; background: url("@/assets/images/reading/note.png"); color: #004EA0; background-size: contain !important; background-repeat: no-repeat !important; background-position: center bottom !important; }
        :deep(.note-flag:hover) { color: #FAFBFF; background: url("@/assets/images/reading/note_active.png"); }
        :deep(.note-flag-active) { color: #FAFBFF; background: url("@/assets/images/reading/note_active.png"); }
      }
      .file-note { max-width: 240px; height: 36px; line-height: 36px; padding: 0 12px; position: absolute; display: none; background: #004EA0; box-shadow: 0 0 8px 1px rgba(180,189,221,0.56); border-radius: 8px; border: 1px solid #D5DDFF; font-size: 14px; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    }
  }
}

// ========== 右栏 ==========
.right-panel {
  min-width: 300px;
  height: 100%;
  flex-shrink: 0;

  .right-card {
    background: linear-gradient(180deg, rgba(178, 226, 255, 0.4) 0%, rgba(106, 148, 255, 0.4) 100%);
  }

  .right-placeholder {
    height: 100%; display: flex; align-items: center; justify-content: center;
    .placeholder-content { text-align: center; color: #999;
      .placeholder-title { margin-top: 12px; font-size: 16px; font-weight: bold; color: #666; }
      .placeholder-sub { margin-top: 4px; font-size: 13px; }
    }
  }
}

// ========== 右键菜单 ==========
.click-bar-button {
  height: 36px; padding: 0 4px; background: #fff;
  box-shadow: 0 0 8px 1px rgba(180,189,221,0.56); border-radius: 8px; border: 1px solid #D5DDFF;
  display: flex; justify-content: space-evenly; align-items: center; position: fixed; z-index: 9999; font-size: 14px;
  .menu-name { line-height: 36px; cursor: pointer; padding: 0 12px; &:hover { color: #004EA0; } }
  .vertical-line { height: 16px; width: 2px; background-color: #D5DDFF; }
}

// ========== 弹框 ==========
.upload-demo { width: 100%; text-align: center; .el-upload__tip { text-align: center; margin-top: 12px; } }
.files-upload-result {
  max-height: 210px; overflow-y: auto; margin-top: 16px;
  .result { height: 30px; display: flex; justify-content: space-between; align-items: center;
    .file-name { width: 434px; font-size: 14px; color: #606771; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-left: 10px; }
    .result-info { display: flex; align-items: center;
      .fail-msg { width: 192px; text-align: right; font-size: 12px; color: #BD0000; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 8px; }
      .loading { width: 38px; height: 38px; } img { width: 14px; margin-right: 10px; } .upload-remove { cursor: pointer; }
    }
  }
}
.dialog-tips { font-size: 12px; color: #858A94; margin-top: 8px; display: flex; align-items: center; justify-content: center; img { width: 12px; height: 12px; margin-right: 3px; } }
</style>
