<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ project.name }} · 标伙伴</title>
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<link rel="stylesheet" href="/static/style.css">
<style>
  [x-cloak]{display:none!important}
  body{font-family:'PingFang SC','Microsoft YaHei',sans-serif;background:#f0f4f8}
  .tab-btn{@apply px-4 py-2 text-sm font-medium rounded-lg transition}
  .prose-content{white-space:pre-wrap;line-height:1.9;font-size:14px}
  .section-item:hover .section-actions{opacity:1}
  .section-actions{opacity:0;transition:opacity .15s}
  .sidebar-fixed{height:calc(100vh - 4rem);position:sticky;top:4rem;overflow-y:auto}
</style>
</head>
<body class="min-h-screen" x-data="projectApp({{ project.id }})" x-init="init()">

<!-- ── 顶栏 ── -->
<header class="bg-white border-b border-gray-200 sticky top-0 z-50 shadow-sm h-16 flex items-center px-6">
  <div class="flex items-center gap-3 flex-1 min-w-0">
    <a href="/" class="p-1.5 hover:bg-gray-100 rounded-lg transition text-gray-500">
      <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
      </svg>
    </a>
    <div class="w-px h-5 bg-gray-200"></div>
    <div class="w-7 h-7 rounded-lg bg-gradient-to-br from-blue-600 to-indigo-600 flex items-center justify-center flex-shrink-0">
      <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
          d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
      </svg>
    </div>
    <h1 class="text-base font-bold text-gray-900 truncate">{{ project.name }}</h1>
  </div>
  <!-- 步骤指示 -->
  <div class="hidden md:flex items-center gap-1 mx-4">
    <template x-for="(step, i) in steps" :key="i">
      <div class="flex items-center">
        <button @click="activeTab=step.key"
          class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition"
          :class="activeTab===step.key ? 'bg-blue-600 text-white shadow-sm' : 'text-gray-500 hover:bg-gray-100'">
          <span x-text="i+1" class="w-4 h-4 rounded-full flex items-center justify-center text-xs"
            :class="activeTab===step.key ? 'bg-white/20' : 'bg-gray-200'"></span>
          <span x-text="step.label"></span>
        </button>
        <div x-show="i < steps.length-1" class="w-4 h-px bg-gray-200 mx-1"></div>
      </div>
    </template>
  </div>
  <!-- 导出按钮 -->
  <button @click="exportDoc()" :disabled="exporting"
    class="flex items-center gap-2 px-4 py-2 bg-green-600 hover:bg-green-700 text-white text-sm font-medium rounded-lg shadow-sm transition disabled:opacity-60 flex-shrink-0">
    <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
        d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
    </svg>
    <span x-show="!exporting">导出 Word</span>
    <span x-show="exporting">导出中...</span>
  </button>
</header>

<!-- ── 主体布局 ── -->
<div class="flex max-w-full">

  <!-- 左侧：章节树（仅在生成大纲后显示） -->
  <aside x-show="sections.length > 0" class="sidebar-fixed w-72 flex-shrink-0 bg-white border-r border-gray-200 px-3 py-4 hidden lg:block">
    <div class="flex items-center justify-between mb-2 px-1">
      <span class="text-xs font-semibold text-gray-500 uppercase tracking-wider">标书章节</span>
      <div class="flex items-center gap-1.5">
        <span x-show="progress.running > 0"
          class="flex items-center gap-1 text-xs text-purple-600 font-medium">
          <span class="relative flex h-2 w-2">
            <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-400 opacity-75"></span>
            <span class="relative inline-flex rounded-full h-2 w-2 bg-purple-600"></span>
          </span>
          <span x-text="progress.running + '路'"></span>
        </span>
        <span class="text-xs text-gray-400" x-text="`${progress.done}/${progress.total}`"></span>
      </div>
    </div>
    <div x-show="progress.total > 0" class="mb-3 px-1">
      <div class="h-1.5 bg-gray-100 rounded-full overflow-hidden">
        <div class="h-full rounded-full transition-all duration-500"
          :class="progress.running > 0 ? 'bg-purple-500' : progress.percent===100 ? 'bg-green-500' : 'bg-blue-500'"
          :style="'width:' + progress.percent + '%'"></div>
      </div>
    </div>

    <div class="space-y-0.5">
      <template x-for="s in sections" :key="s.id">
        <div class="section-item flex items-start gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer group"
          :class="selectedSection && selectedSection.id===s.id ? 'bg-blue-50' : ''"
          @click="selectSection(s)">
          <!-- 缩进 -->
          <div class="flex-shrink-0 mt-0.5" :style="'width:' + (s.level-1)*12 + 'px'"></div>
          <!-- 状态图标 -->
          <div class="flex-shrink-0 w-4 h-4 mt-0.5">
            <svg x-show="s.status==='done'" class="w-4 h-4 text-green-500" fill="currentColor" viewBox="0 0 20 20">
              <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
            </svg>
            <div x-show="s.status==='generating'" class="w-3.5 h-3.5 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin m-0.5"></div>
            <svg x-show="s.status==='error'" class="w-4 h-4 text-red-400" fill="currentColor" viewBox="0 0 20 20">
              <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"/>
            </svg>
            <div x-show="!['done','generating','error'].includes(s.status)"
              class="w-3 h-3 rounded-full border border-gray-300 m-0.5"></div>
          </div>
                <span class="text-xs leading-5 flex-1 min-w-0 break-words"
            :class="[s.level===1 ? 'font-semibold text-gray-800' : s.level===2 ? 'font-medium text-gray-700' : 'text-gray-600',
                     selectedSection && selectedSection.id===s.id ? 'text-blue-700' : '']"
                  x-text="s.title"></span>
          <!-- 生成按钮 -->
          <button x-show="s.is_leaf" class="section-actions flex-shrink-0 text-blue-500 hover:text-blue-700"
            @click.stop="generateSection(s.id)" :disabled="s.status==='generating'" title="生成此章节">
            <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
            </svg>
          </button>
        </div>
      </template>
    </div>
  </aside>

  <!-- 右侧：内容区 -->
  <main class="flex-1 min-w-0 p-6">

    <!-- ═══ TAB 1：招标分析 ═══ -->
    <div x-show="activeTab==='parse'" class="space-y-5">

      <!-- 上传区 -->
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
        <h2 class="text-base font-bold text-gray-800 mb-4 flex items-center gap-2">
          <span class="w-6 h-6 bg-blue-100 text-blue-600 rounded-md flex items-center justify-center text-xs font-bold">1</span>
          上传招标文件
        </h2>
        <div class="border-2 border-dashed rounded-xl p-8 text-center transition"
          :class="dragover ? 'border-blue-400 bg-blue-50' : 'border-gray-200 hover:border-gray-300'"
          @dragover.prevent="dragover=true" @dragleave="dragover=false"
          @drop.prevent="onDrop($event)">
          <input type="file" id="fileInput" class="hidden" accept=".pdf,.doc,.docx" @change="onFileSelect($event)">
          <svg class="w-10 h-10 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
          </svg>
          <p class="text-sm text-gray-500 mb-1">拖拽文件到此处，或</p>
          <button class="text-blue-600 text-sm font-medium hover:underline" @click="$el.closest('.border-dashed').querySelector('#fileInput').click()">
            点击选择文件
          </button>
          <p class="text-xs text-gray-400 mt-2">支持 PDF、DOC、DOCX，最大 50MB</p>
        </div>

        <!-- 已上传文件 -->
        <div x-show="project.file_name" class="mt-4 flex items-center justify-between p-3 bg-gray-50 rounded-xl">
          <div class="flex items-center gap-3">
            <div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">
              <svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
              </svg>
            </div>
            <div>
              <p class="text-sm font-medium text-gray-800" x-text="project.file_name"></p>
              <p class="text-xs text-gray-400">已上传</p>
            </div>
          </div>
          <button @click="parseTender()" :disabled="parsing || project.parse_status==='parsing'"
            class="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded-lg transition disabled:opacity-60">
            <span x-show="!parsing">AI 解析</span>
            <span x-show="parsing" class="flex items-center gap-1.5">
              <span class="w-3.5 h-3.5 border-2 border-white/40 border-t-white rounded-full animate-spin"></span>
              解析中...
            </span>
          </button>
        </div>

        <!-- 上传进度 -->
        <div x-show="uploading" class="mt-3">
          <div class="flex items-center gap-2 text-sm text-blue-600">
            <div class="w-4 h-4 border-2 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
            上传中...
          </div>
        </div>
      </div>

      <!-- ── 工程量清单导入（可选）── -->
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
        <div class="flex items-center gap-2 mb-4">
          <svg class="w-4 h-4 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
          </svg>
          <h3 class="font-semibold text-gray-800 text-sm">工程量清单导入 <span class="text-xs font-normal text-gray-400 ml-1">（可选）解析后可联动招标内容，让生成内容包含准确工程量</span></h3>
        </div>

        <!-- 上传区域 -->
        <div x-show="!project.boq_file_name"
          class="border-2 border-dashed border-orange-200 rounded-xl p-6 text-center hover:border-orange-400 transition cursor-pointer bg-orange-50"
          @dragover.prevent="true"
          @drop.prevent="uploadBoqFile($event.dataTransfer.files[0])"
          @click="$refs.boqFileInput.click()">
          <input type="file" x-ref="boqFileInput" class="hidden"
            accept=".xlsx,.xls,.csv,.pdf,.docx,.doc"
            @change="uploadBoqFile($event.target.files[0])">
          <svg class="w-8 h-8 mx-auto mb-2 text-orange-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
          </svg>
          <p class="text-sm text-orange-600">拖拽清单文件到此处，或点击选择</p>
          <p class="text-xs text-gray-400 mt-1">支持 Excel（xlsx/xls）、CSV、PDF、Word，最大 50MB</p>
        </div>

        <!-- 已上传 -->
        <div x-show="project.boq_file_name" class="flex items-center justify-between p-3 bg-orange-50 rounded-xl">
          <div class="flex items-center gap-3">
            <div class="w-8 h-8 bg-orange-100 rounded-lg flex items-center justify-center flex-shrink-0">
              <svg class="w-4 h-4 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
              </svg>
            </div>
            <div>
              <p class="text-sm font-medium text-gray-800" x-text="project.boq_file_name"></p>
              <p class="text-xs text-gray-400" x-text="project.boq_status==='done' ? '已解析完成' : project.boq_status==='parsing' ? '解析中...' : '已上传'"></p>
            </div>
          </div>
          <div class="flex items-center gap-2">
            <!-- 重新上传 -->
            <button @click="project.boq_file_name=''; project.boq_status='none'"
              class="text-xs text-gray-400 hover:text-gray-600 transition">重新上传</button>
            <!-- 解析按钮 -->
            <button @click="parseBoq()"
              :disabled="parsingBoq || project.boq_status==='parsing'"
              class="flex items-center gap-1.5 px-4 py-2 bg-orange-500 hover:bg-orange-600 text-white text-sm rounded-lg transition disabled:opacity-60">
              <span x-show="!parsingBoq && project.boq_status!=='parsing'">AI 解析清单</span>
              <span x-show="parsingBoq || project.boq_status==='parsing'" class="flex items-center gap-1.5">
                <span class="w-3.5 h-3.5 border-2 border-white/40 border-t-white rounded-full animate-spin"></span>
                解析中...
              </span>
            </button>
          </div>
        </div>

        <!-- 上传进度 -->
        <div x-show="uploadingBoq" class="mt-3 flex items-center gap-2 text-sm text-orange-600">
          <div class="w-4 h-4 border-2 border-orange-200 border-t-orange-500 rounded-full animate-spin"></div>
          上传中...
        </div>

        <!-- 解析错误 -->
        <div x-show="project.boq_status==='error'" class="mt-3 flex items-center gap-2 text-sm text-red-600">
          <svg class="w-4 h-4 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
            <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"/>
          </svg>
          <span x-text="'解析失败: ' + (project.boq_error || '未知错误')"></span>
        </div>

        <!-- 本地清单结构化分析（bill-worker 规则 Python 版） -->
        <div x-show="project.boq_status==='done' && project.boq_analysis" class="mt-4 space-y-2">
          <template x-if="project.boq_analysis && (project.boq_analysis.scanned || project.boq_analysis.no_bill_pages)">
            <p class="text-xs text-amber-700 bg-amber-50 border border-amber-100 rounded-lg px-3 py-2">
              <span x-show="project.boq_analysis.scanned">本地分析：文本过少，可能为扫描件或未提取到文字层；已仅依赖全文生成摘要。</span>
              <span x-show="!project.boq_analysis.scanned && project.boq_analysis.no_bill_pages">本地分析：未识别到清单表页，摘要仍可能基于全文。</span>
            </p>
          </template>
          <template x-if="project.boq_analysis && project.boq_analysis.categories && project.boq_analysis.categories.length && !project.boq_analysis.scanned && !project.boq_analysis.no_bill_pages">
            <div class="rounded-xl border border-orange-100 bg-white p-3 text-xs">
              <div class="flex items-center justify-between gap-2 mb-2">
                <p class="font-semibold text-orange-800 flex items-center gap-1">
                  <span class="w-1.5 h-1.5 bg-orange-500 rounded-full"></span>
                  本地清单分析
                </p>
                <button type="button" @click="showBoqAnalysisDetail = !showBoqAnalysisDetail"
                  class="text-orange-600 hover:text-orange-800 shrink-0">
                  <span x-text="showBoqAnalysisDetail ? '收起明细' : '展开明细'"></span>
                </button>
              </div>
              <p class="text-gray-600 mb-1" x-text="project.boq_analysis.project_summary && project.boq_analysis.project_summary.remark"></p>
              <p class="text-gray-500">
                分部 <span class="font-medium text-gray-700" x-text="boqStructuredCatCount()"></span> 个，
                清单项 <span class="font-medium text-gray-700" x-text="boqStructuredItemCount()"></span> 条
                <template x-if="project.boq_analysis._meta">
                  <span>（识别清单相关页 <span x-text="project.boq_analysis._meta.bill_pages || 0"></span> / 总 <span x-text="project.boq_analysis._meta.total_pages || 0"></span> 页）</span>
                </template>
              </p>
              <div x-show="showBoqAnalysisDetail" class="mt-2 max-h-64 overflow-y-auto border border-gray-100 rounded-lg">
                <template x-for="cat in (project.boq_analysis.categories || [])" :key="cat.name">
                  <div class="border-b border-gray-50 last:border-0 px-2 py-2">
                    <p class="font-medium text-gray-800 mb-1" x-text="cat.name"></p>
                    <table class="w-full text-left text-[11px] text-gray-600">
                      <thead>
                        <tr class="text-gray-400 border-b border-gray-100">
                          <th class="py-0.5 pr-2 font-normal">编码</th>
                          <th class="py-0.5 pr-2 font-normal">名称</th>
                          <th class="py-0.5 pr-1 font-normal w-10">单位</th>
                          <th class="py-0.5 font-normal w-14">工程量</th>
                        </tr>
                      </thead>
                      <tbody>
                        <template x-for="(it, idx) in (cat.items || [])" :key="idx">
                          <tr class="border-b border-gray-50 align-top">
                            <td class="py-0.5 pr-2 font-mono text-[10px]" x-text="it.code || '—'"></td>
                            <td class="py-0.5 pr-2" x-text="it.name"></td>
                            <td class="py-0.5 pr-1" x-text="it.unit || ''"></td>
                            <td class="py-0.5" x-text="it.quantity || ''"></td>
                          </tr>
                        </template>
                      </tbody>
                    </table>
                  </div>
                </template>
              </div>
            </div>
          </template>
        </div>

        <!-- BOQ 摘要显示/编辑（解析完成后） -->
        <div x-show="project.boq_status==='done' && project.boq_summary" class="mt-4">
          <div class="flex items-center justify-between mb-2">
            <p class="text-xs font-semibold text-orange-700 flex items-center gap-1">
              <span class="w-1.5 h-1.5 bg-orange-500 rounded-full"></span>
              工程量清单摘要
            </p>
            <div class="flex items-center gap-2">
              <template x-if="editBoq">
                <div class="flex gap-2">
                  <button @click="saveBoqSummary()" :disabled="savingBoq"
                    class="flex items-center gap-1 px-3 py-1 bg-orange-500 hover:bg-orange-600 text-white text-xs rounded-lg transition disabled:opacity-60">
                    <span x-show="!savingBoq">保存</span>
                    <span x-show="savingBoq" class="flex items-center gap-1">
                      <span class="w-3 h-3 border border-white/40 border-t-white rounded-full animate-spin"></span>
                      保存中
                    </span>
                  </button>
                  <button @click="editBoq=false; editBoqText=project.boq_summary"
                    class="px-3 py-1 text-xs border border-gray-200 text-gray-500 hover:bg-gray-50 rounded-lg transition">
                    取消
                  </button>
                </div>
              </template>
              <template x-if="!editBoq">
                <button @click="editBoq=true; editBoqText=project.boq_summary"
                  class="flex items-center gap-1 px-3 py-1 text-xs border border-gray-200 text-gray-600 hover:bg-gray-50 rounded-lg transition">
                  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
                  </svg>
                  编辑
                </button>
              </template>
            </div>
          </div>
          <!-- 预览 -->
          <div x-show="!editBoq"
            class="text-xs text-gray-700 bg-orange-50 rounded-xl p-4 max-h-48 overflow-y-auto whitespace-pre-wrap leading-6 border border-orange-100"
            x-text="project.boq_summary"></div>
          <!-- 编辑 -->
          <div x-show="editBoq">
            <textarea x-model="editBoqText" rows="10"
              class="w-full px-4 py-3 border border-orange-300 rounded-xl text-xs leading-6 focus:outline-none focus:ring-2 focus:ring-orange-400 resize-y font-mono"
              placeholder="工程量清单摘要内容..."></textarea>
            <p class="text-xs text-gray-400 mt-1">修改后点击"保存"，将在生成章节内容时作为工程量参考</p>
          </div>
        </div>
      </div>

      <!-- 解析状态 -->
      <div x-show="project.parse_status && project.parse_status !== 'none' && project.parse_status !== 'uploaded'"
        class="bg-white rounded-2xl shadow-sm border border-gray-100 p-5">
        <div x-show="project.parse_status==='parsing'" class="flex items-center gap-3 text-blue-600">
          <div class="w-5 h-5 border-2 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
          <span class="text-sm font-medium" x-text="project.parse_error || '正在解析，请稍候...'"></span>
        </div>
        <div x-show="project.parse_status==='error'" class="flex items-center gap-3 text-red-600">
          <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
            <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"/>
          </svg>
          <span class="text-sm" x-text="'解析失败: ' + (project.parse_error || '未知错误')"></span>
        </div>
      </div>

      <!-- 解析结果 -->
      <div x-show="project.parse_status==='done'" class="space-y-4">

        <!-- 标书类型（解析自动识别，影响步骤 3 正文模板） -->
        <div class="bg-gradient-to-r from-indigo-50 to-blue-50 rounded-2xl border border-indigo-100 p-5">
          <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
            <div>
              <h3 class="text-sm font-semibold text-indigo-900">标书类型</h3>
              <p class="text-xs text-indigo-700/80 mt-0.5">解析完成后自动识别为工程类 / 服务类 / 货物类；步骤 3 生成章节将套用对应写作模板（施工组织 / 服务方案 / 供货方案）。识别有误可在此修正。</p>
            </div>
            <div class="flex items-center gap-2 flex-shrink-0">
              <select x-model="tenderKind"
                class="text-sm border border-indigo-200 rounded-lg px-3 py-2 bg-white text-gray-800 focus:ring-2 focus:ring-indigo-400 focus:outline-none min-w-[11rem]">
                <option value="engineering">工程类（施工组织设计）</option>
                <option value="service">服务类（服务实施方案）</option>
                <option value="goods">货物类（供货技术方案）</option>
              </select>
              <button type="button" @click="saveTenderData('kind')"
                :disabled="savingTenderData"
                class="px-3 py-2 text-xs font-medium rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-50">
                保存类型
              </button>
            </div>
          </div>
        </div>

        <!-- 招标文件摘要 -->
        <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
          <div class="flex items-center justify-between mb-3">
            <h3 class="font-semibold text-gray-800 flex items-center gap-2">
              <span class="w-2 h-2 bg-green-500 rounded-full"></span>
              招标文件摘要
            </h3>
            <div class="flex items-center gap-2">
              <template x-if="editSummary">
                <div class="flex items-center gap-2">
                  <button @click="saveTenderData('summary')"
                    :disabled="savingTenderData"
                    class="flex items-center gap-1 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white text-xs font-medium rounded-lg transition disabled:opacity-60">
                    <span x-show="!savingTenderData">保存</span>
                    <span x-show="savingTenderData" class="flex items-center gap-1">
                      <span class="w-3 h-3 border border-white/40 border-t-white rounded-full animate-spin"></span>
                      保存中
                    </span>
                  </button>
                  <button @click="editSummary=false; editSummaryText=project.summary"
                    class="px-3 py-1.5 text-xs border border-gray-200 text-gray-500 hover:bg-gray-50 rounded-lg transition">
                    取消
                  </button>
                </div>
              </template>
              <template x-if="!editSummary">
                <div class="flex items-center gap-2">
                  <button @click="editSummary=true; editSummaryText=project.summary"
                    class="flex items-center gap-1 px-3 py-1.5 text-xs border border-gray-200 text-gray-600 hover:bg-gray-50 rounded-lg transition">
                    <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
                    </svg>
                    编辑
                  </button>
                  <button @click="activeTab='outline'" class="text-xs text-blue-600 hover:underline">
                    下一步：生成大纲 →
                  </button>
                </div>
              </template>
            </div>
          </div>
          <!-- 预览模式 -->
          <div x-show="!editSummary"
            class="prose-content text-gray-700 bg-gray-50 rounded-xl p-4 max-h-64 overflow-y-auto text-sm"
            x-text="project.summary || '摘要生成中...'"></div>
          <!-- 编辑模式 -->
          <div x-show="editSummary">
            <textarea x-model="editSummaryText" rows="12"
              class="w-full px-4 py-3 border border-blue-300 rounded-xl text-sm leading-7 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y font-mono"
              placeholder="招标文件摘要内容..."></textarea>
            <p class="text-xs text-gray-400 mt-1">修改后点击"保存"，将作为生成大纲的依据</p>
          </div>
        </div>

        <!-- 技术评分要求 -->
        <div x-show="project.rating_requirements" class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
          <div class="flex items-center justify-between mb-3">
            <h3 class="font-semibold text-gray-800 flex items-center gap-2">
              <span class="w-2 h-2 bg-orange-500 rounded-full"></span>
              技术评分要求
            </h3>
            <div class="flex items-center gap-2">
              <template x-if="editRating">
                <div class="flex items-center gap-2">
                  <button @click="saveTenderData('rating')"
                    :disabled="savingTenderData"
                    class="flex items-center gap-1 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white text-xs font-medium rounded-lg transition disabled:opacity-60">
                    <span x-show="!savingTenderData">保存</span>
                    <span x-show="savingTenderData" class="flex items-center gap-1">
                      <span class="w-3 h-3 border border-white/40 border-t-white rounded-full animate-spin"></span>
                      保存中
                    </span>
                  </button>
                  <button @click="editRating=false; editRatingText=project.rating_requirements"
                    class="px-3 py-1.5 text-xs border border-gray-200 text-gray-500 hover:bg-gray-50 rounded-lg transition">
                    取消
                  </button>
                </div>
              </template>
              <template x-if="!editRating">
                <button @click="editRating=true; editRatingText=project.rating_requirements"
                  class="flex items-center gap-1 px-3 py-1.5 text-xs border border-gray-200 text-gray-600 hover:bg-gray-50 rounded-lg transition">
                  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
                  </svg>
                  编辑
                </button>
              </template>
            </div>
          </div>
          <!-- 预览模式 -->
          <div x-show="!editRating"
            class="prose-content text-gray-700 bg-orange-50 rounded-xl p-4 max-h-64 overflow-y-auto text-sm"
            x-text="project.rating_requirements"></div>
          <!-- 编辑模式 -->
          <div x-show="editRating">
            <textarea x-model="editRatingText" rows="14"
              class="w-full px-4 py-3 border border-orange-300 rounded-xl text-sm leading-7 focus:outline-none focus:ring-2 focus:ring-orange-400 resize-y font-mono"
              placeholder="技术评分要求内容..."></textarea>
            <p class="text-xs text-gray-400 mt-1">修改后点击"保存"，将作为生成大纲的依据（只保留技术评分，删除商务/价格评分内容）</p>
          </div>
        </div>

      </div>
    </div>

    <!-- ═══ TAB 2：大纲生成 ═══ -->
    <div x-show="activeTab==='outline'" class="space-y-5">
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
        <h2 class="text-base font-bold text-gray-800 mb-4 flex items-center gap-2">
          <span class="w-6 h-6 bg-indigo-100 text-indigo-600 rounded-md flex items-center justify-center text-xs font-bold">2</span>
          生成标书大纲
        </h2>

        <div x-show="project.parse_status !== 'done'" class="py-8 text-center">
          <p class="text-gray-400 text-sm">请先完成招标文件解析</p>
          <button @click="activeTab='parse'" class="mt-3 text-blue-600 text-sm hover:underline">返回解析步骤</button>
        </div>

        <div x-show="project.parse_status === 'done'">
          <p class="text-sm text-gray-500 mb-4">AI 将根据招标文件摘要和技术评分标准，生成结构化的四级标书目录。</p>
          <button @click="generateOutline()"
            :disabled="project.outline_status==='outline_generating'"
            class="flex items-center gap-2 px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium rounded-xl shadow-sm transition disabled:opacity-60">
            <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/>
            </svg>
            <span x-show="project.outline_status !== 'outline_generating'">
              <span x-text="sections.length > 0 ? '重新生成大纲' : '生成标书大纲'"></span>
            </span>
            <span x-show="project.outline_status === 'outline_generating'" class="flex items-center gap-1.5">
              <span class="w-3.5 h-3.5 border-2 border-white/40 border-t-white rounded-full animate-spin"></span>
              正在生成...
            </span>
          </button>
          <p class="text-xs text-gray-400 mt-2" x-show="project.outline && project.outline.length > 0">
            当前用于生成的大纲：约 <span x-text="project.outline.length"></span> 字，<span x-text="sections.length"></span> 个章节
          </p>
        </div>

        <!-- 大纲进度 -->
        <div x-show="project.outline_status==='outline_generating'" class="mt-4 flex items-center gap-2 text-indigo-600">
          <div class="w-4 h-4 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin"></div>
          <span class="text-sm">AI 正在生成标书大纲，通常需要 30-60 秒...</span>
        </div>
        <div x-show="project.outline_status==='outline_error'" class="mt-4 text-red-500 text-sm"
          x-text="'生成失败: ' + (project.outline_error || '')"></div>

        <!-- 大纲预览 / 编辑 -->
        <div x-show="sections.length > 0" class="mt-5">
          <div class="flex items-center justify-between mb-3">
            <h3 class="font-medium text-gray-700">
              大纲
              <span x-show="!editOutline">预览</span>
              <span x-show="editOutline">编辑</span>
              （<span x-text="sections.length"></span> 个章节）
            </h3>
            <div class="flex items-center gap-2">
              <!-- 编辑模式按钮组 -->
              <template x-if="editOutline">
                <div class="flex items-center gap-2">
                  <button @click="saveOutline()"
                    :disabled="savingOutline"
                    class="flex items-center gap-1 px-3 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white text-xs font-medium rounded-lg transition disabled:opacity-60">
                    <span x-show="!savingOutline">保存并更新章节</span>
                    <span x-show="savingOutline" class="flex items-center gap-1">
                      <span class="w-3 h-3 border border-white/40 border-t-white rounded-full animate-spin"></span>
                      保存中
                    </span>
                  </button>
                  <button @click="cancelEditOutline()"
                    class="px-3 py-1.5 text-xs border border-gray-200 text-gray-500 hover:bg-gray-50 rounded-lg transition">
                    取消
                  </button>
                  <button @click="expandOutline()"
                    :disabled="expandingOutline || !editOutlineText.trim()"
                    class="flex items-center gap-1 px-3 py-1.5 bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-medium rounded-lg transition disabled:opacity-60">
                    <span x-show="!expandingOutline">AI自动填充小章节</span>
                    <span x-show="expandingOutline" class="flex items-center gap-1">
                      <span class="w-3 h-3 border border-white/40 border-t-white rounded-full animate-spin"></span>
                      填充中
                    </span>
                  </button>
                </div>
              </template>
              <!-- 预览模式按钮组 -->
              <template x-if="!editOutline">
                <div class="flex items-center gap-2">
                  <button @click="enterEditOutline()"
                    class="flex items-center gap-1 px-3 py-1.5 text-xs border border-gray-200 text-gray-600 hover:bg-gray-50 rounded-lg transition">
                    <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
                    </svg>
                    编辑大纲
                  </button>
                  <button @click="activeTab='content'"
                    class="flex items-center gap-1 text-xs text-indigo-600 hover:underline font-medium">
                    下一步：生成内容 →
                  </button>
                </div>
              </template>
            </div>
          </div>

          <!-- 预览模式：章节树 -->
          <div x-show="!editOutline" class="bg-gray-50 rounded-xl p-4 max-h-96 overflow-y-auto">
            <template x-for="s in sections" :key="s.id">
              <div class="flex items-center gap-2 py-0.5" :style="'padding-left:' + (s.level-1)*20 + 'px'">
                <span class="text-gray-400 text-xs" x-text="s.level <= 2 ? '▸' : '·'"></span>
                <span class="text-sm" :class="s.level===1 ? 'font-semibold text-gray-800' : s.level===2 ? 'font-medium text-gray-700' : 'text-gray-600'"
                  x-text="s.title"></span>
              </div>
            </template>
          </div>

          <!-- 编辑模式：原始大纲文本 -->
          <div x-show="editOutline">
            <div class="mb-2 p-3 bg-amber-50 border border-amber-200 rounded-xl text-xs text-amber-700 flex items-start gap-2">
              <svg class="w-4 h-4 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
              </svg>
              <span>保存后将按新大纲重新划分章节，<b>已生成的章节正文内容将被清除</b>，需重新生成。编辑时请保持层级编号格式（一、1.1、1.1.1…）不变。</span>
            </div>
            <textarea x-model="editOutlineText" @input="outlineDirty=true" rows="20"
              class="w-full px-4 py-3 border border-indigo-300 rounded-xl text-sm leading-6 focus:outline-none focus:ring-2 focus:ring-indigo-500 resize-y font-mono"
              placeholder="在此编辑大纲文本，每行一个章节项..."></textarea>
            <p class="text-xs text-gray-400 mt-1">格式示例：第一行为标书名称，章节用"一、""1.1""1.1.1""1.1.1.1"等格式</p>
          </div>
        </div>
      </div>
    </div>

    <!-- ═══ TAB 3：内容生成 ═══ -->
    <div x-show="activeTab==='content'" class="space-y-5">
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
        <div class="flex items-start justify-between mb-4">
          <h2 class="text-base font-bold text-gray-800 flex items-center gap-2">
            <span class="w-6 h-6 bg-purple-100 text-purple-600 rounded-md flex items-center justify-center text-xs font-bold">3</span>
            章节内容生成
          </h2>
          <div class="flex items-center gap-2">
            <!-- 图表设置按钮 -->
            <button @click="showDiagramPanel = !showDiagramPanel"
              :class="(enableFigure||enableTable) ? 'bg-teal-500 hover:bg-teal-600 text-white border-teal-500' : 'bg-white hover:bg-teal-50 text-gray-600 border-gray-200'"
              class="flex items-center gap-1.5 px-3 py-2 border text-sm font-medium rounded-lg transition">
              <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
              </svg>
              图表设置
              <span x-show="enableFigure||enableTable" class="w-1.5 h-1.5 rounded-full bg-white"></span>
            </button>
            <!-- 暗标设置按钮 -->
            <button @click="showAnonPanel = !showAnonPanel"
              :class="anonEnabled ? 'bg-amber-500 hover:bg-amber-600 text-white border-amber-500' : 'bg-white hover:bg-amber-50 text-gray-600 border-gray-200'"
              class="flex items-center gap-1.5 px-3 py-2 border text-sm font-medium rounded-lg transition">
              <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
              </svg>
              暗标设置
              <span x-show="anonEnabled" class="w-1.5 h-1.5 rounded-full bg-white"></span>
            </button>
            <!-- 一键并发生成 -->
            <button @click="generateAllSections()"
              :disabled="progress.running > 0"
              class="flex items-center gap-2 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white text-sm font-medium rounded-lg transition disabled:opacity-60">
              <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
              </svg>
              <span x-show="progress.running === 0">一键并发生成</span>
              <span x-show="progress.running > 0" class="flex items-center gap-1.5">
                <span class="w-3 h-3 border-2 border-white/40 border-t-white rounded-full animate-spin"></span>
                并发生成中...
              </span>
            </button>
          </div>
        </div>

        <!-- 暗标设置面板 -->
        <div x-show="showAnonPanel" x-transition
          class="mb-5 p-5 bg-amber-50 border border-amber-200 rounded-xl">
          <div class="flex items-center justify-between mb-3">
            <div class="flex items-center gap-3">
              <svg class="w-4 h-4 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
              </svg>
              <span class="text-sm font-semibold text-amber-800">暗标模式</span>
              <!-- 启用开关 -->
              <label class="flex items-center gap-2 cursor-pointer select-none">
                <div class="relative">
                  <input type="checkbox" x-model="anonEnabled" class="sr-only">
                  <div :class="anonEnabled ? 'bg-amber-500' : 'bg-gray-300'"
                    class="w-9 h-5 rounded-full transition"></div>
                  <div :class="anonEnabled ? 'translate-x-4' : 'translate-x-0'"
                    class="absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow transition-transform"></div>
                </div>
                <span class="text-xs text-amber-700" x-text="anonEnabled ? '已启用' : '未启用'"></span>
              </label>
            </div>
            <button @click="saveAnon()" :disabled="savingAnon"
              class="flex items-center gap-1.5 px-3 py-1.5 bg-amber-500 hover:bg-amber-600 text-white text-xs font-medium rounded-lg transition disabled:opacity-60">
              <svg x-show="!savingAnon" class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
              </svg>
              <span class="w-3.5 h-3.5 border-2 border-white/40 border-t-white rounded-full animate-spin" x-show="savingAnon"></span>
              <span x-text="savingAnon ? '保存中...' : '保存暗标要求'"></span>
            </button>
          </div>

          <div x-show="anonEnabled" x-transition>
            <p class="text-xs text-amber-700 mb-3">
              暗标要求将附加到每个章节的 AI 生成规范中，AI 必须严格遵守。适用于评标文件不得暴露投标人身份的项目。
            </p>
            <!-- 常用预设 -->
            <div class="flex flex-wrap gap-2 mb-3">
              <span class="text-xs text-amber-600 font-medium self-center mr-1">常用预设：</span>
              <button @click="addAnonPreset('不得出现投标人公司名称、注册地址等任何可识别单位信息')"
                class="px-2.5 py-1 text-xs bg-white border border-amber-200 text-amber-700 rounded-md hover:bg-amber-100 transition">
                + 公司名称
              </button>
              <button @click="addAnonPreset('不得出现项目经理、技术负责人等人员的真实姓名')"
                class="px-2.5 py-1 text-xs bg-white border border-amber-200 text-amber-700 rounded-md hover:bg-amber-100 transition">
                + 人员姓名
              </button>
              <button @click="addAnonPreset('不得出现联系方式（电话、邮箱、网址等）')"
                class="px-2.5 py-1 text-xs bg-white border border-amber-200 text-amber-700 rounded-md hover:bg-amber-100 transition">
                + 联系方式
              </button>
              <button @click="addAnonPreset('不得出现营业执照号、税务登记号、社会信用代码等证件编号')"
                class="px-2.5 py-1 text-xs bg-white border border-amber-200 text-amber-700 rounded-md hover:bg-amber-100 transition">
                + 证件编号
              </button>
              <button @click="addAnonPreset('不得出现公司Logo、印章等视觉标识的描述')"
                class="px-2.5 py-1 text-xs bg-white border border-amber-200 text-amber-700 rounded-md hover:bg-amber-100 transition">
                + 公司标识
              </button>
              <button @click="addAnonPreset('涉及以往业绩时，仅描述项目规模/类型，不得提及甲方名称和项目地点')"
                class="px-2.5 py-1 text-xs bg-white border border-amber-200 text-amber-700 rounded-md hover:bg-amber-100 transition">
                + 业绩脱敏
              </button>
            </div>
            <textarea x-model="anonText" rows="5"
              placeholder="在此输入暗标要求，每条要求单独一行，例如：&#10;- 不得出现公司名称、地址等可识别信息&#10;- 人员描述只写职称和年限，不写姓名&#10;- 业绩案例只描述规模和类型，不提及甲方名称"
              class="w-full px-4 py-3 border border-amber-200 rounded-xl text-sm leading-6 focus:outline-none focus:ring-2 focus:ring-amber-400 bg-white resize-y font-mono"></textarea>
            <p class="text-xs text-amber-600 mt-1.5">
              填写后点击「保存暗标要求」，再点击「一键并发生成」或单章节「AI 生成」，暗标规则将自动注入 AI 提示词。
            </p>
          </div>
          <div x-show="!anonEnabled" class="text-xs text-amber-600">
            启用暗标模式后，可设置禁止 AI 在生成内容中暴露投标人身份的具体规则。
          </div>
        </div>

        <!-- 图表设置面板 -->
        <div x-show="showDiagramPanel" x-transition
          class="mb-5 p-5 bg-teal-50 border border-teal-200 rounded-xl">
          <div class="flex items-center justify-between mb-4">
            <div class="flex items-center gap-2">
              <svg class="w-4 h-4 text-teal-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
              </svg>
              <span class="text-sm font-semibold text-teal-800">图表生成模式</span>
            </div>
            <button @click="saveDiagram()" :disabled="savingDiagram"
              class="flex items-center gap-1.5 px-3 py-1.5 bg-teal-500 hover:bg-teal-600 text-white text-xs font-medium rounded-lg transition disabled:opacity-60">
              <svg x-show="!savingDiagram" class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
              </svg>
              <span class="w-3.5 h-3.5 border-2 border-white/40 border-t-white rounded-full animate-spin" x-show="savingDiagram"></span>
              <span x-text="savingDiagram ? '保存中...' : '保存设置'"></span>
            </button>
          </div>

          <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
            <!-- 图开关 -->
            <label class="flex items-start gap-3 p-4 bg-white border border-teal-200 rounded-xl cursor-pointer hover:border-teal-400 transition"
              :class="enableFigure ? 'border-teal-400 ring-1 ring-teal-300' : ''">
              <div class="relative mt-0.5 flex-shrink-0">
                <input type="checkbox" x-model="enableFigure" class="sr-only">
                <div :class="enableFigure ? 'bg-teal-500' : 'bg-gray-300'"
                  class="w-9 h-5 rounded-full transition"></div>
                <div :class="enableFigure ? 'translate-x-4' : 'translate-x-0'"
                  class="absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow transition-transform"></div>
              </div>
              <div>
                <p class="text-sm font-semibold text-gray-800">启用"图"生成</p>
                <p class="text-xs text-gray-500 mt-1 leading-5">AI 生成章节时自动插入组织架构图、流程图、施工进度图、施工平面示意图等，以 <code class="bg-gray-100 px-1 rounded text-teal-700">[FIGURE:标题]...[/FIGURE]</code> 格式嵌入正文，导出时渲染为 Word 图示块。</p>
              </div>
            </label>
            <!-- 表开关 -->
            <label class="flex items-start gap-3 p-4 bg-white border border-teal-200 rounded-xl cursor-pointer hover:border-teal-400 transition"
              :class="enableTable ? 'border-teal-400 ring-1 ring-teal-300' : ''">
              <div class="relative mt-0.5 flex-shrink-0">
                <input type="checkbox" x-model="enableTable" class="sr-only">
                <div :class="enableTable ? 'bg-teal-500' : 'bg-gray-300'"
                  class="w-9 h-5 rounded-full transition"></div>
                <div :class="enableTable ? 'translate-x-4' : 'translate-x-0'"
                  class="absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow transition-transform"></div>
              </div>
              <div>
                <p class="text-sm font-semibold text-gray-800">启用"表"生成</p>
                <p class="text-xs text-gray-500 mt-1 leading-5">AI 生成章节时自动插入人员配置表、设备设施表、劳动力配置表、临时用地表等，以 <code class="bg-gray-100 px-1 rounded text-teal-700">[TABLE:标题]...[/TABLE]</code> 格式嵌入正文，导出时渲染为 Word 表格。</p>
              </div>
            </label>
          </div>

          <p class="text-xs text-teal-600 mt-3">
            设置后点击「保存设置」，再点击「一键并发生成」或单章节「AI 生成」，图表将自动生成并嵌入正文；导出 Word 文档时自动渲染为带标题的图示块和正式表格。
          </p>
        </div>

        <!-- 并发进度面板 -->
        <div x-show="progress.total > 0" class="mb-5 p-4 rounded-xl border"
          :class="progress.running > 0 ? 'bg-purple-50 border-purple-200' : 'bg-gray-50 border-gray-200'">
          <div class="flex items-center justify-between mb-2">
            <div class="flex items-center gap-3 text-sm">
              <span class="font-medium text-gray-700">生成进度</span>
              <span x-show="progress.running > 0"
                class="flex items-center gap-1.5 text-purple-600 font-medium">
                <span class="relative flex h-2.5 w-2.5">
                  <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-400 opacity-75"></span>
                  <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-purple-600"></span>
                </span>
                <span x-text="progress.running"></span> 路并发
              </span>
            </div>
            <span class="text-sm font-bold" :class="progress.percent===100 ? 'text-green-600' : 'text-gray-700'"
              x-text="progress.percent + '%'"></span>
          </div>
          <div class="h-2.5 bg-white rounded-full overflow-hidden shadow-inner">
            <div class="h-full rounded-full transition-all duration-500"
              :class="progress.running > 0 ? 'bg-gradient-to-r from-purple-500 to-indigo-500' : progress.percent===100 ? 'bg-green-500' : 'bg-purple-500'"
              :style="'width:'+progress.percent+'%'"></div>
          </div>
          <div class="flex justify-between mt-2 text-xs text-gray-500">
            <div class="flex gap-4">
              <span class="flex items-center gap-1">
                <span class="w-2 h-2 rounded-full bg-green-500"></span>
                已完成 <b x-text="progress.done" class="text-gray-700"></b>
              </span>
              <span x-show="progress.running > 0" class="flex items-center gap-1">
                <span class="w-2 h-2 rounded-full bg-purple-500 animate-pulse"></span>
                生成中 <b x-text="progress.running" class="text-gray-700"></b>
              </span>
              <span x-show="progress.errors > 0" class="flex items-center gap-1">
                <span class="w-2 h-2 rounded-full bg-red-400"></span>
                失败 <b x-text="progress.errors" class="text-red-600"></b>
              </span>
            </div>
            <span>共 <b x-text="progress.total" class="text-gray-700"></b> 个章节</span>
          </div>
        </div>

        <div x-show="sections.length === 0" class="py-8 text-center">
          <p class="text-gray-400 text-sm">请先生成标书大纲</p>
          <button @click="activeTab='outline'" class="mt-3 text-indigo-600 text-sm hover:underline">返回生成大纲</button>
        </div>

        <!-- 章节列表 -->
        <div x-show="sections.length > 0" class="space-y-1">
          <template x-for="s in sections" :key="s.id">
            <div class="section-item flex items-center gap-3 px-3 py-2 rounded-xl hover:bg-gray-50 cursor-pointer transition"
              :class="selectedSection && selectedSection.id===s.id ? 'bg-blue-50 ring-1 ring-blue-200' : ''"
              @click="selectSection(s); activeTab='editor'"
              :style="s.level > 1 ? 'margin-left:' + (s.level-1)*20 + 'px' : ''">

              <!-- 状态 -->
              <div class="w-5 h-5 flex-shrink-0 flex items-center justify-center">
                <svg x-show="s.status==='done'" class="w-4.5 h-4.5 text-green-500" fill="currentColor" viewBox="0 0 20 20">
                  <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
                </svg>
                <div x-show="s.status==='generating'" class="w-4 h-4 border-2 border-purple-300 border-t-purple-600 rounded-full animate-spin"></div>
                <svg x-show="s.status==='error'" class="w-4 h-4 text-red-400" fill="currentColor" viewBox="0 0 20 20">
                  <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V10a1 1 0 00-1-1zm1 8a1 1 0 100-2 1 1 0 000 2z"/>
                </svg>
                <div x-show="!['done','generating','error'].includes(s.status)"
                  class="w-3.5 h-3.5 rounded-full border-2 border-gray-300"></div>
              </div>

              <span class="flex-1 text-sm min-w-0 truncate"
                :class="s.level===1 ? 'font-semibold text-gray-800' : s.level===2 ? 'font-medium text-gray-700' : 'text-gray-600'"
                x-text="s.title"></span>

              <span x-show="s.has_content && s.status==='done'" class="text-xs text-green-600 bg-green-50 px-2 py-0.5 rounded-full flex-shrink-0">已生成</span>
              <span x-show="!s.is_leaf" class="text-xs text-gray-400 flex-shrink-0">章节</span>

              <div class="section-actions flex gap-1 flex-shrink-0">
                <button x-show="s.is_leaf" @click.stop="generateSection(s.id)"
                  :disabled="s.status==='generating'"
                  class="px-2.5 py-1 text-xs bg-purple-100 text-purple-700 hover:bg-purple-200 rounded-lg transition disabled:opacity-50">
                  生成
                </button>
                <button @click.stop="selectSection(s); activeTab='editor'"
                  class="px-2.5 py-1 text-xs bg-gray-100 text-gray-600 hover:bg-gray-200 rounded-lg transition">
                  查看
                </button>
              </div>
            </div>
          </template>
        </div>
      </div>
    </div>

    <!-- ═══ TAB 4：编辑器 ═══ -->
    <div x-show="activeTab==='editor'" class="space-y-4">
      <div x-show="!selectedSection" class="bg-white rounded-2xl shadow-sm border border-gray-100 p-12 text-center">
        <p class="text-gray-400">从左侧章节列表或内容列表中选择一个章节</p>
        <button @click="activeTab='content'" class="mt-3 text-purple-600 text-sm hover:underline">查看章节列表</button>
      </div>

      <div x-show="selectedSection" class="bg-white rounded-2xl shadow-sm border border-gray-100">
        <!-- 编辑器头部 -->
        <div class="flex items-start justify-between px-6 py-4 border-b border-gray-100 gap-4">
          <div class="min-w-0">
            <h3 class="font-bold text-gray-900 text-base" x-text="selectedSection && selectedSection.title"></h3>
          </div>
          <div class="flex flex-col items-end gap-2 flex-shrink-0">
            <!-- 生成模式切换（仅叶节点） -->
            <div x-show="selectedSection && selectedSection.is_leaf"
              class="flex rounded-lg border border-gray-200 overflow-hidden text-xs">
              <button @click="switchGenMode('auto')"
                :class="genMode==='auto' ? 'bg-purple-600 text-white' : 'bg-white text-gray-500 hover:bg-gray-50'"
                class="px-3 py-1.5 flex items-center gap-1 transition">
                <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
                </svg>
                自动生成
              </button>
              <button @click="switchGenMode('chat')"
                :class="genMode==='chat' ? 'bg-indigo-600 text-white' : 'bg-white text-gray-500 hover:bg-gray-50'"
                class="px-3 py-1.5 flex items-center gap-1 transition border-l border-gray-200">
                <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-3 3v-3z"/>
                </svg>
                对话生成
              </button>
            </div>
            <!-- 自动模式操作按钮 -->
            <div x-show="genMode==='auto'" class="flex items-center gap-2">
              <button x-show="selectedSection && selectedSection.is_leaf"
                @click="generateSection(selectedSection.id)"
                :disabled="selectedSection && selectedSection.status==='generating'"
                class="flex items-center gap-1.5 px-3 py-1.5 text-sm bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition disabled:opacity-60">
                <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
                </svg>
                <span x-show="selectedSection && selectedSection.status !== 'generating'">AI 生成</span>
                <span x-show="selectedSection && selectedSection.status === 'generating'">生成中...</span>
              </button>
              <button @click="saveContent()" x-show="editMode"
                class="px-3 py-1.5 text-sm bg-green-600 hover:bg-green-700 text-white rounded-lg transition">
                保存
              </button>
              <button @click="editMode=!editMode"
                class="px-3 py-1.5 text-sm border border-gray-200 text-gray-600 hover:bg-gray-50 rounded-lg transition"
                x-text="editMode ? '预览' : '编辑'"></button>
            </div>
            <!-- 对话模式操作按钮 -->
            <div x-show="genMode==='chat'" class="flex items-center gap-2">
              <button @click="chatMessages=[]; chatSectionId=null; switchGenMode('chat')"
                class="px-3 py-1.5 text-sm border border-gray-200 text-gray-500 hover:bg-gray-50 rounded-lg transition flex items-center gap-1.5">
                <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
                </svg>
                清空对话
              </button>
            </div>
          </div>
        </div>

        <!-- 生成中提示 -->
        <div x-show="selectedSection && selectedSection.status==='generating'"
          class="px-6 py-3 bg-purple-50 border-b border-purple-100 flex items-center gap-2 text-purple-700 text-sm">
          <div class="w-4 h-4 border-2 border-purple-300 border-t-purple-600 rounded-full animate-spin"></div>
          AI 正在生成内容，请稍候...
        </div>

        <!-- ── 自动生成模式内容区 ── -->
        <div x-show="genMode==='auto'" class="p-6">
          <!-- 章节引言（非叶节点） -->
          <div x-show="sectionContent && sectionContent.intro_content" class="mb-4 p-4 bg-blue-50 rounded-xl border border-blue-100">
            <p class="text-xs font-medium text-blue-600 mb-2">章节引言</p>
            <p class="text-sm text-gray-700 whitespace-pre-wrap leading-7" x-text="sectionContent && sectionContent.intro_content"></p>
          </div>
          <!-- 编辑模式 -->
          <div x-show="editMode">
            <textarea x-model="editContent" rows="20"
              class="w-full px-4 py-3 border border-gray-200 rounded-xl text-sm leading-7 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none font-mono"
              placeholder="在此输入或编辑章节内容..."></textarea>
          </div>
          <!-- 预览模式 -->
          <div x-show="!editMode" class="min-h-48">
            <div x-show="!sectionContent || (!sectionContent.content && !sectionContent.intro_content)"
              class="py-16 text-center text-gray-400">
              <svg class="w-10 h-10 mx-auto mb-3 text-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
              </svg>
              <p class="text-sm">暂无内容，点击"AI 生成"或切换"对话生成"模式</p>
            </div>
            <div x-show="sectionContent && sectionContent.content"
              class="prose-content text-gray-800 leading-8"
              x-text="sectionContent && sectionContent.content"></div>
          </div>
        </div>

        <!-- ── 对话生成模式内容区 ── -->
        <div x-show="genMode==='chat'" class="flex flex-col" style="height:600px">
          <!-- 消息列表 -->
          <div id="chatMsgList" class="flex-1 overflow-y-auto px-6 py-4 space-y-4">
            <template x-for="msg in chatMessages" :key="msg.id">
              <div>
                <!-- 用户消息（右侧） -->
                <div x-show="msg.role==='user'" class="flex justify-end">
                  <div class="max-w-[80%] bg-indigo-600 text-white rounded-2xl rounded-tr-sm px-4 py-3 text-sm">
                    <p x-text="msg.content" class="whitespace-pre-wrap leading-6"></p>
                  </div>
                </div>
                <!-- AI 消息（左侧） -->
                <div x-show="msg.role==='assistant'" class="flex justify-start gap-2.5">
                  <div class="w-7 h-7 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center flex-shrink-0 mt-1">
                    <span class="text-white text-xs font-bold">AI</span>
                  </div>
                  <div class="max-w-[85%]">
                    <div :class="msg.isError ? 'bg-red-50 border-red-200 text-red-700' : 'bg-gray-50 border-gray-200 text-gray-800'"
                      class="border rounded-2xl rounded-tl-sm px-4 py-3 text-sm">
                      <p x-text="msg.content" class="whitespace-pre-wrap leading-7"></p>
                    </div>
                    <!-- 采用内容按钮（非欢迎语、非错误） -->
                    <button x-show="!msg.isWelcome && !msg.isError"
                      @click="applyChatContent(msg.content)"
                      class="mt-2 flex items-center gap-1.5 text-xs text-indigo-600 hover:text-indigo-800 font-medium transition">
                      <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
                      </svg>
                      采用此内容 → 填入编辑框
                    </button>
                  </div>
                </div>
              </div>
            </template>
            <!-- 加载动画 -->
            <div x-show="chatLoading" class="flex justify-start gap-2.5">
              <div class="w-7 h-7 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center flex-shrink-0 mt-1">
                <span class="text-white text-xs font-bold">AI</span>
              </div>
              <div class="bg-gray-50 border border-gray-200 rounded-2xl rounded-tl-sm px-5 py-3.5">
                <div class="flex gap-1.5 items-center">
                  <span class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay:0ms"></span>
                  <span class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay:150ms"></span>
                  <span class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay:300ms"></span>
                </div>
              </div>
            </div>
          </div>

          <!-- 输入区 -->
          <div class="border-t border-gray-100 px-6 py-4 bg-gray-50 rounded-b-2xl">
            <div class="flex gap-3 items-end">
              <textarea x-model="chatInput"
                @keydown.enter.prevent="if(!$event.shiftKey){ sendChatMessage() }"
                :disabled="chatLoading"
                rows="3"
                placeholder="描述您的需求，例如：请生成该章节完整内容，重点强调质量管控措施并给出具体标准编号… (Enter 发送，Shift+Enter 换行)"
                class="flex-1 px-4 py-3 border border-gray-200 rounded-xl text-sm resize-none focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white leading-6 disabled:opacity-60"></textarea>
              <button @click="sendChatMessage()"
                :disabled="chatLoading || !chatInput.trim()"
                class="flex-shrink-0 px-5 py-3 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium rounded-xl transition disabled:opacity-50 flex items-center gap-2">
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>
                </svg>
                发送
              </button>
            </div>
            <p class="text-xs text-gray-400 mt-2">点击 AI 回复下方的「采用此内容 → 填入编辑框」将内容写入编辑器，再点「保存」完成。</p>
          </div>
        </div>
      </div>
    </div>

    <!-- ═══ TAB 5：合规检查 ═══ -->
    <div x-show="activeTab==='check'" class="space-y-4">
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-6">
        <h2 class="text-base font-bold text-gray-800 mb-2 flex items-center gap-2">
          <span class="w-6 h-6 bg-orange-100 text-orange-600 rounded-md flex items-center justify-center text-xs font-bold">4</span>
          合规性检查
        </h2>
        <p class="text-sm text-gray-500 mb-4">AI 将对照招标要求检查标书内容的覆盖情况，给出改进建议。</p>
        <button @click="runCheck()" :disabled="checking"
          class="flex items-center gap-2 px-5 py-2.5 bg-orange-500 hover:bg-orange-600 text-white text-sm font-medium rounded-xl transition disabled:opacity-60">
          <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
          </svg>
          <span x-show="!checking">开始合规检查</span>
          <span x-show="checking" class="flex items-center gap-1.5">
            <span class="w-3.5 h-3.5 border-2 border-white/40 border-t-white rounded-full animate-spin"></span>
            检查中...
          </span>
        </button>

        <!-- 检查结果：用 x-if 而非 x-show，null 时完全不渲染内部绑定 -->
        <template x-if="checkResult">
          <div class="mt-6 space-y-4">
            <!-- 总分 -->
            <div class="flex items-center gap-4 p-4 bg-gradient-to-r from-green-50 to-emerald-50 rounded-xl border border-green-100">
              <div class="w-16 h-16 rounded-full border-4 flex items-center justify-center flex-shrink-0"
                :class="(checkResult.overall_score||0) >= 80 ? 'border-green-400' : (checkResult.overall_score||0) >= 60 ? 'border-yellow-400' : 'border-red-400'">
                <span class="text-xl font-bold" x-text="checkResult.overall_score || '--'"></span>
              </div>
              <div>
                <p class="font-bold text-gray-800 text-lg" x-text="checkResult.status || '—'"></p>
                <p class="text-sm text-gray-500">综合评分</p>
              </div>
            </div>

            <!-- 检查项目 -->
            <template x-if="checkResult.items && checkResult.items.length">
              <div class="space-y-2">
                <h4 class="font-medium text-gray-700 text-sm">检查项目</h4>
                <template x-for="item in checkResult.items" :key="item.requirement">
                  <div class="flex items-start gap-3 p-3 rounded-xl border"
                    :class="item.covered ? 'bg-green-50 border-green-100' : 'bg-red-50 border-red-100'">
                    <svg x-show="item.covered" class="w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
                      <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
                    </svg>
                    <svg x-show="!item.covered" class="w-4 h-4 text-red-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
                      <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V10a1 1 0 00-1-1zm1 8a1 1 0 100-2 1 1 0 000 2z"/>
                    </svg>
                    <div>
                      <p class="text-sm font-medium text-gray-800" x-text="item.requirement"></p>
                      <p class="text-xs text-gray-500 mt-0.5" x-text="item.note"></p>
                    </div>
                  </div>
                </template>
              </div>
            </template>

            <!-- 缺失要点 -->
            <template x-if="checkResult.missing_points && checkResult.missing_points.length">
              <div class="p-4 bg-yellow-50 rounded-xl border border-yellow-100">
                <h4 class="font-medium text-yellow-800 text-sm mb-2">⚠️ 未覆盖要点</h4>
                <ul class="space-y-1">
                  <template x-for="pt in checkResult.missing_points" :key="pt">
                    <li class="text-sm text-yellow-700 flex items-start gap-2">
                      <span class="mt-1 flex-shrink-0">·</span><span x-text="pt"></span>
                    </li>
                  </template>
                </ul>
              </div>
            </template>

            <!-- 改进建议 -->
            <template x-if="checkResult.suggestions && checkResult.suggestions.length">
              <div class="p-4 bg-blue-50 rounded-xl border border-blue-100">
                <h4 class="font-medium text-blue-800 text-sm mb-2">💡 改进建议</h4>
                <ul class="space-y-1">
                  <template x-for="sg in checkResult.suggestions" :key="sg">
                    <li class="text-sm text-blue-700 flex items-start gap-2">
                      <span class="mt-1 flex-shrink-0">·</span><span x-text="sg"></span>
                    </li>
                  </template>
                </ul>
              </div>
            </template>
          </div>
        </template>
      </div>
    </div>

    <!-- ═══ TAB 6：知识库 ═══ -->
    <div x-show="activeTab==='knowledge'" class="space-y-5">

      <!-- 状态栏 -->
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-5">
        <div class="flex items-center justify-between flex-wrap gap-3">
          <div class="flex items-center gap-3">
            <div class="w-10 h-10 rounded-xl bg-teal-50 flex items-center justify-center flex-shrink-0">
              <svg class="w-5 h-5 text-teal-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
              </svg>
            </div>
            <div>
              <h2 class="text-base font-bold text-gray-800">企业知识库</h2>
              <p class="text-xs text-gray-400 mt-0.5">上传历史标书，AI 生成时自动检索企业优势内容</p>
            </div>
          </div>
          <!-- 检索模式 chip -->
          <div x-show="knowledgeStatus" class="flex items-center gap-2 flex-wrap">
            <!-- 向量语义检索 -->
            <span x-show="knowledgeStatus && knowledgeStatus.search_mode === 'vector'"
              class="inline-flex items-center gap-1.5 text-xs font-medium px-3 py-1.5 rounded-full bg-teal-50 text-teal-700 border border-teal-200">
              <span class="w-2 h-2 rounded-full bg-teal-400 inline-block"></span>
              语义向量检索 · <span x-text="(knowledgeStatus && knowledgeStatus.doc_count) || 0"></span> 个文本块
            </span>
            <!-- 关键词降级检索 -->
            <span x-show="knowledgeStatus && knowledgeStatus.search_mode === 'keyword'"
              class="inline-flex items-center gap-1.5 text-xs font-medium px-3 py-1.5 rounded-full bg-amber-50 text-amber-700 border border-amber-200">
              <span class="w-2 h-2 rounded-full bg-amber-400 inline-block"></span>
              关键词检索模式 · <span x-text="(knowledgeStatus && knowledgeStatus.doc_count) || 0"></span> 个文本块
            </span>
          </div>
        </div>

        <!-- 关键词模式说明（DeepSeek/Ollama 无 Embedding API 时提示） -->
        <div x-show="knowledgeStatus && knowledgeStatus.search_mode === 'keyword' && knowledgeFiles.length > 0"
          class="mt-4 p-4 bg-amber-50 border border-amber-200 rounded-xl text-sm text-amber-800">
          <p class="font-medium mb-1">💡 当前使用关键词检索</p>
          <p class="text-xs text-amber-700">DeepSeek / Ollama 暂不提供 Embedding API，知识库将以关键词匹配方式检索相关内容。
            切换为 Qwen 或 OpenAI 模型（在首页 AI 配置中设置）可启用更精准的语义向量检索。</p>
        </div>
      </div>

      <!-- 上传区 -->
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-5">
        <h3 class="text-sm font-semibold text-gray-700 mb-4 flex items-center gap-2">
          <svg class="w-4 h-4 text-teal-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
          </svg>
          添加知识文档
        </h3>

        <!-- 拖拽上传区 -->
        <label
          @dragover.prevent="knowledgeDragover=true"
          @dragleave="knowledgeDragover=false"
          @drop.prevent="onKnowledgeDrop($event)"
          :class="knowledgeDragover ? 'border-teal-400 bg-teal-50' : 'border-gray-200 bg-gray-50 hover:border-teal-300 hover:bg-teal-50/50'"
          class="flex flex-col items-center justify-center border-2 border-dashed rounded-xl p-8 cursor-pointer transition-all">
          <input type="file" class="hidden" accept=".pdf,.doc,.docx" @change="onKnowledgeSelect($event)">
          <div x-show="!knowledgeUploading && knowledgeProcessing.length === 0">
            <svg class="w-10 h-10 text-teal-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
            </svg>
            <p class="text-sm font-medium text-gray-600 text-center">拖拽文件到此处，或点击选择</p>
            <p class="text-xs text-gray-400 mt-1 text-center">支持 PDF、DOC、DOCX 格式</p>
          </div>
          <div x-show="knowledgeUploading" class="flex flex-col items-center">
            <div class="w-8 h-8 border-3 border-teal-200 border-t-teal-500 rounded-full animate-spin mb-3"></div>
            <p class="text-sm text-teal-600">正在上传文件...</p>
          </div>
          <div x-show="!knowledgeUploading && knowledgeProcessing.length > 0" class="flex flex-col items-center">
            <div class="w-8 h-8 border-3 border-teal-200 border-t-teal-500 rounded-full animate-spin mb-3"></div>
            <p class="text-sm text-teal-600">AI 正在向量化文档，请稍候...</p>
            <p class="text-xs text-gray-400 mt-1" x-text="'处理中：' + knowledgeProcessing.join(', ')"></p>
          </div>
        </label>

        <p class="text-xs text-gray-400 mt-3">
          推荐上传：历史技术方案、同类项目标书、企业资质简介、施工工法说明等。<br>
          上传后 AI 在生成章节内容时将自动检索相关片段作为写作参考。
        </p>
      </div>

      <!-- 文件列表 -->
      <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-5">
        <h3 class="text-sm font-semibold text-gray-700 mb-4 flex items-center gap-2">
          <svg class="w-4 h-4 text-teal-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
          </svg>
          已上传文件
          <span class="ml-auto text-xs font-normal text-gray-400" x-text="knowledgeFiles.length + ' 个文件'"></span>
        </h3>

        <!-- 处理中的文件（还未出现在列表里） -->
        <template x-for="fn in knowledgeProcessing.filter(fn => !knowledgeFiles.some(f => f.name === fn))" :key="fn">
          <div class="flex items-center gap-3 px-4 py-3 mb-2 rounded-xl border border-teal-100 bg-teal-50">
            <div class="w-4 h-4 border-2 border-teal-300 border-t-teal-600 rounded-full animate-spin flex-shrink-0"></div>
            <div class="flex-1 min-w-0">
              <p class="text-sm font-medium text-teal-700 truncate" x-text="fn"></p>
              <p class="text-xs text-teal-500">正在向量化入库...</p>
            </div>
          </div>
        </template>

        <!-- 已完成的文件 -->
        <template x-if="knowledgeFiles.length > 0">
          <div class="space-y-2">
            <template x-for="file in knowledgeFiles" :key="file.name">
              <div class="flex items-center gap-3 px-4 py-3 rounded-xl border border-gray-100 bg-gray-50 hover:bg-gray-100 transition group">
                <div class="w-8 h-8 rounded-lg bg-teal-100 flex items-center justify-center flex-shrink-0">
                  <svg class="w-4 h-4 text-teal-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
                  </svg>
                </div>
                <div class="flex-1 min-w-0">
                  <p class="text-sm font-medium text-gray-800 truncate" x-text="file.name"></p>
                  <p class="text-xs text-gray-400" x-text="file.chunks + ' 个文本块 · ' + (file.added_at || '').slice(0, 10)"></p>
                </div>
                <button @click.stop="deleteKnowledgeFile(file.name)"
                  class="opacity-0 group-hover:opacity-100 transition flex-shrink-0 p-1.5 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-lg">
                  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
                  </svg>
                </button>
              </div>
            </template>
          </div>
        </template>

        <!-- 空状态 -->
        <div x-show="knowledgeFiles.length === 0 && knowledgeProcessing.length === 0"
          class="py-10 flex flex-col items-center text-gray-300">
          <svg class="w-12 h-12 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
          </svg>
          <p class="text-sm">知识库暂无文件</p>
          <p class="text-xs mt-1">上传历史标书后，AI 生成内容时将自动引用</p>
        </div>
      </div>

      <!-- 使用说明 -->
      <div class="bg-gradient-to-br from-teal-50 to-cyan-50 rounded-2xl border border-teal-100 p-5">
        <h3 class="text-sm font-semibold text-teal-800 mb-3 flex items-center gap-2">
          <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
          </svg>
          使用说明
        </h3>
        <ul class="space-y-2 text-xs text-teal-700">
          <li class="flex items-start gap-2">
            <span class="flex-shrink-0 w-4 h-4 rounded-full bg-teal-200 text-teal-800 flex items-center justify-center font-bold text-[10px] mt-0.5">1</span>
            <span>上传企业历史技术标书、施工方案、资质简介等文档（支持 PDF/DOC/DOCX）</span>
          </li>
          <li class="flex items-start gap-2">
            <span class="flex-shrink-0 w-4 h-4 rounded-full bg-teal-200 text-teal-800 flex items-center justify-center font-bold text-[10px] mt-0.5">2</span>
            <span>系统自动将文档切分并向量化入库（首次入库需等待 AI 处理完成）</span>
          </li>
          <li class="flex items-start gap-2">
            <span class="flex-shrink-0 w-4 h-4 rounded-full bg-teal-200 text-teal-800 flex items-center justify-center font-bold text-[10px] mt-0.5">3</span>
            <span>生成章节内容（步骤 3）时，系统将自动检索知识库中最相关的段落供 AI 参考写作</span>
          </li>
          <li class="flex items-start gap-2">
            <span class="flex-shrink-0 w-4 h-4 rounded-full bg-teal-200 text-teal-800 flex items-center justify-center font-bold text-[10px] mt-0.5">4</span>
            <span>知识库为全局共享，对所有项目均有效；可随时添加或删除文档</span>
          </li>
        </ul>
      </div>

    </div>

  </main>
</div>

<!-- 底部导航（移动端） -->
<nav class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 flex md:hidden z-40">
  <template x-for="(step, i) in steps" :key="i">
    <button @click="activeTab=step.key" class="flex-1 py-3 flex flex-col items-center gap-0.5"
      :class="activeTab===step.key ? 'text-blue-600' : 'text-gray-400'">
      <span class="text-lg" x-text="step.icon"></span>
      <span class="text-xs" x-text="step.label"></span>
    </button>
  </template>
</nav>

<script>
const PROJECT_ID = {{ project.id }}

function projectApp(projectId) {
  return {
    projectId,
    activeTab: 'parse',
    project: {},
    sections: [],
    selectedSection: null,
    sectionContent: null,
    editMode: false,
    editContent: '',
    progress: { total: 0, done: 0, running: 0, errors: 0, percent: 0 },
    checkResult: null,
    checking: false,
    exporting: false,
    parsing: false,
    uploading: false,
    dragover: false,
    knowledgeFiles: [],
    knowledgeStatus: null,
    knowledgeUploading: false,
    knowledgeDragover: false,
    knowledgeProcessing: [],
    knowledgePollTimer: null,
    pollTimer: null,
    editSummary: false,
    editSummaryText: '',
    editRating: false,
    editRatingText: '',
    savingTenderData: false,
    uploadingBoq: false,
    parsingBoq: false,
    editBoq: false,
    editBoqText: '',
    tenderKind: 'engineering',
    savingBoq: false,
    showBoqAnalysisDetail: false,
    editOutline: false,
    editOutlineText: '',
    savingOutline: false,
    expandingOutline: false,
    outlineDirty: false,
    genMode: 'auto',
    chatMessages: [],
    chatInput: '',
    chatLoading: false,
    chatSectionId: null,
    showAnonPanel: false,
    anonEnabled: false,
    anonText: '',
    savingAnon: false,
    showDiagramPanel: false,
    enableFigure: false,
    enableTable: false,
    savingDiagram: false,
    steps: [
      { key: 'parse', label: '解析', icon: '📄' },
      { key: 'outline', label: '大纲', icon: '📋' },
      { key: 'content', label: '生成', icon: '✍️' },
      { key: 'editor', label: '编辑', icon: '📝' },
      { key: 'check', label: '检查', icon: '✅' },
      { key: 'knowledge', label: '知识库', icon: '🗂️' },
    ],

    async init() {
      await this.loadProject(true)
      await this.loadSections()
      await this.loadKnowledge()
      await this.loadKnowledgeStatus()
      this.startPolling()
    },

    boqStructuredItemCount() {
      const a = this.project.boq_analysis
      if (!a || !a.categories) return 0
      return a.categories.reduce((s, c) => s + ((c.items && c.items.length) || 0), 0)
    },
    boqStructuredCatCount() {
      const a = this.project.boq_analysis
      if (!a || !a.categories) return 0
      return a.categories.length
    },

    async loadProject(isInitial = false) {
      const localOutlineDraft = this.editOutline && this.outlineDirty ? this.editOutlineText : ''
      const res = await fetch(`/api/projects/${this.projectId}`)
      this.project = await res.json()
      if (localOutlineDraft) {
        // 编辑未保存时，保护草稿不被轮询数据覆盖
        this.editOutlineText = localOutlineDraft
      }
      this.tenderKind = this.project.tender_kind || 'engineering'
      this.parsing = this.project.parse_status === 'parsing'
      // 只在首次加载时同步用户可编辑的设置状态，轮询更新时不覆盖，
      // 避免用户改动开关后被下一次 poll 自动还原
      if (isInitial) {
        const anon = this.project.anon_requirements || ''
        this.anonText = anon
        this.anonEnabled = anon.trim().length > 0
        this.enableFigure = !!this.project.enable_figure
        this.enableTable = !!this.project.enable_table
      }
      // BOQ 解析状态：轮询中同步（完成/出错时停止 loading）
      if (this.parsingBoq && this.project.boq_status !== 'parsing') {
        this.parsingBoq = false
      }
    },

    async loadSections() {
      const res = await fetch(`/api/projects/${this.projectId}/sections`)
      const data = await res.json()
      this.sections = data.sections || []
      await this.updateProgress()
    },

    async loadKnowledge() {
      const res = await fetch('/api/knowledge/files')
      const data = await res.json()
      this.knowledgeFiles = data.files || []
    },

    async loadKnowledgeStatus() {
      try {
        const res = await fetch('/api/knowledge/status')
        this.knowledgeStatus = await res.json()
        this.knowledgeProcessing = (this.knowledgeStatus && this.knowledgeStatus.processing) || []
      } catch (e) {
        this.knowledgeStatus = { available: false, reason: '无法连接服务器' }
      }
    },

    // 知识库：拖拽上传
    onKnowledgeDrop(event) {
      this.knowledgeDragover = false
      const file = event.dataTransfer.files[0]
      if (file) this.uploadKnowledgeFile(file)
    },
    onKnowledgeSelect(event) {
      const file = event.target.files[0]
      if (file) this.uploadKnowledgeFile(file)
      event.target.value = ''
    },

    async uploadKnowledgeFile(file) {
      this.knowledgeUploading = true
      const fd = new FormData()
      fd.append('file', file)
      try {
        const res = await fetch('/api/knowledge/upload', { method: 'POST', body: fd })
        const data = await res.json()
        if (data.success || data.queued) {
          // 上传后台处理中，轮询直到文件出现在列表
          this._startKnowledgePoll(data.filename)
        } else {
          alert('上传失败: ' + (data.error || '未知错误'))
        }
      } catch (e) {
        alert('上传失败: ' + e.message)
      } finally {
        this.knowledgeUploading = false
      }
    },

    _startKnowledgePoll(filename) {
      if (this.knowledgePollTimer) clearInterval(this.knowledgePollTimer)
      this.knowledgeProcessing = [...new Set([...this.knowledgeProcessing, filename])]
      this.knowledgePollTimer = setInterval(async () => {
        await this.loadKnowledge()
        await this.loadKnowledgeStatus()
        // 当处理中的文件出现在列表里，视为完成
        const done = this.knowledgeProcessing.filter(
          fn => this.knowledgeFiles.some(f => f.name === fn)
        )
        if (done.length > 0) {
          this.knowledgeProcessing = this.knowledgeProcessing.filter(fn => !done.includes(fn))
        }
        if (this.knowledgeProcessing.length === 0) {
          clearInterval(this.knowledgePollTimer)
          this.knowledgePollTimer = null
        }
      }, 2000)
    },

    async deleteKnowledgeFile(fileName) {
      if (!confirm(`确定删除知识库文件「${fileName}」？此操作不可恢复。`)) return
      const res = await fetch('/api/knowledge/delete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ file_name: fileName })
      })
      const data = await res.json()
      if (data.success) {
        await this.loadKnowledge()
        await this.loadKnowledgeStatus()
      } else {
        alert('删除失败: ' + (data.error || ''))
      }
    },

    async updateProgress() {
      if (this.sections.length === 0) return
      const res = await fetch(`/api/projects/${this.projectId}/section-progress`)
      this.progress = await res.json()
    },

    startPolling() {
      const poll = async () => {
        const prevParse = this.project.parse_status
        const prevOutline = this.project.outline_status
        await this.loadProject()
        await this.loadSections()

        if (prevParse === 'parsing' && this.project.parse_status === 'done') {
          this.parsing = false
        }
        if (prevOutline === 'outline_generating' && this.project.outline_status === 'outline_done') {
          this.activeTab = 'content'
        }

        if (this.selectedSection) {
          const updated = this.sections.find(s => s.id === this.selectedSection.id)
          if (updated) {
            const wasGenerating = this.selectedSection.status === 'generating'
            this.selectedSection = updated
            if (wasGenerating && updated.status === 'done') {
              await this.loadSectionContent(updated.id)
            }
          }
        }

        // Adaptive interval: fast when busy, slow when idle
        const busy = this.progress.running > 0 || this.parsing || this.project.outline_status === 'outline_generating'
        const interval = busy ? 1500 : 5000
        this.pollTimer = setTimeout(poll, interval)
      }
      this.pollTimer = setTimeout(poll, 2000)
    },

    // ── 文件操作 ──
    onDrop(event) {
      this.dragover = false
      const file = event.dataTransfer.files[0]
      if (file) this.uploadFile(file)
    },

    onFileSelect(event) {
      const file = event.target.files[0]
      if (file) this.uploadFile(file)
    },

    async uploadFile(file) {
      this.uploading = true
      const fd = new FormData()
      fd.append('file', file)
      const res = await fetch(`/api/projects/${this.projectId}/upload`, { method: 'POST', body: fd })
      const data = await res.json()
      this.uploading = false
      if (data.success) {
        this.project.file_name = data.file_name
        this.project.parse_status = 'uploaded'
      } else {
        alert('上传失败: ' + (data.error || ''))
      }
    },

    async parseTender() {
      this.parsing = true
      this.project.parse_status = 'parsing'
      const res = await fetch(`/api/projects/${this.projectId}/parse`, { method: 'POST' })
      const data = await res.json()
      if (!data.success) {
        this.parsing = false
        alert('启动解析失败: ' + (data.error || ''))
      }
    },

    // ── 保存手动编辑的解析结果 ──
    async saveTenderData(field) {
      this.savingTenderData = true
      const payload = {}
      if (field === 'summary') {
        payload.summary = this.editSummaryText
      } else if (field === 'rating') {
        payload.rating_requirements = this.editRatingText
      } else if (field === 'kind') {
        payload.tender_kind = this.tenderKind
      }
      try {
        const res = await fetch(`/api/projects/${this.projectId}/tender-data`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload)
        })
        const data = await res.json()
        if (data.success) {
          if (field === 'summary') {
            this.project.summary = this.editSummaryText
            this.editSummary = false
          } else if (field === 'rating') {
            this.project.rating_requirements = this.editRatingText
            this.editRating = false
          } else if (field === 'kind') {
            this.project.tender_kind = this.tenderKind
          }
        } else {
          alert('保存失败: ' + (data.error || ''))
        }
      } catch (e) {
        alert('保存失败: ' + e.message)
      } finally {
        this.savingTenderData = false
      }
    },

    // ── 工程量清单 ──
    async uploadBoqFile(file) {
      this.uploadingBoq = true
      const fd = new FormData()
      fd.append('file', file)
      try {
        const res = await fetch(`/api/projects/${this.projectId}/upload-boq`, { method: 'POST', body: fd })
        const data = await res.json()
        if (data.success) {
          this.project.boq_file_name = data.file_name
          this.project.boq_status = 'uploaded'
        } else {
          alert('上传失败: ' + (data.error || ''))
        }
      } catch (e) {
        alert('上传失败: ' + e.message)
      } finally {
        this.uploadingBoq = false
      }
    },

    async parseBoq() {
      this.parsingBoq = true
      this.project.boq_status = 'parsing'
      try {
        const res = await fetch(`/api/projects/${this.projectId}/parse-boq`, { method: 'POST' })
        const data = await res.json()
        if (!data.success) alert('解析启动失败: ' + (data.error || ''))
      } catch (e) {
        alert('解析失败: ' + e.message)
        this.parsingBoq = false
      }
    },

    async saveBoqSummary() {
      this.savingBoq = true
      try {
        await fetch(`/api/projects/${this.projectId}/boq`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ boq_summary: this.editBoqText })
        })
        this.project.boq_summary = this.editBoqText
        this.editBoq = false
      } catch (e) {
        alert('保存失败: ' + e.message)
      } finally {
        this.savingBoq = false
      }
    },

    // ── 保存手动编辑的大纲 ──
    async saveOutline() {
      if (!this.editOutlineText.trim()) return
      const hasDone = this.sections.some(s => s.status === 'done')
      if (hasDone && !confirm('已有章节内容生成完成，保存新大纲后这些内容将被清除，确定继续？')) return
      this.savingOutline = true
      try {
        const res = await fetch(`/api/projects/${this.projectId}/outline`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ outline: this.editOutlineText })
        })
        const data = await res.json()
        if (data.success) {
          // 用后端重排序号后的规范文本更新本地，下次打开编辑器显示正确编号
          this.project.outline = data.normalized_outline || this.editOutlineText
          this.editOutlineText = this.project.outline
          this.outlineDirty = false
          this.editOutline = false
          await this.loadProject()
          await this.loadSections()
          alert(`大纲已保存并用于生成（章节数：${data.section_count || this.sections.length}，持久化长度：${data.persisted_outline_len || this.project.outline.length}）`)
        } else {
          alert('保存失败: ' + (data.error || ''))
        }
      } catch (e) {
        alert('保存失败: ' + e.message)
      } finally {
        this.savingOutline = false
      }
    },

    // ── 大纲生成 ──
    async generateOutline() {
      const hasExistingOutline = !!(this.project.outline && this.project.outline.trim())
      let force = false
      if (hasExistingOutline) {
        if (!confirm('当前项目已有大纲，重新生成会覆盖现有大纲。是否继续覆盖？')) return
        force = true
      }
      this.project.outline_status = 'outline_generating'
      const res = await fetch(`/api/projects/${this.projectId}/generate-outline`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ force })
      })
      const data = await res.json()
      if (!data.success) alert('启动失败: ' + (data.error || ''))
    },

    enterEditOutline() {
      this.editOutline = true
      this.editOutlineText = this.project.outline || ''
      this.outlineDirty = false
    },

    cancelEditOutline() {
      this.editOutline = false
      this.editOutlineText = this.project.outline || ''
      this.outlineDirty = false
    },

    async expandOutline() {
      if (!this.editOutlineText.trim()) return
      this.expandingOutline = true
      try {
        const res = await fetch(`/api/projects/${this.projectId}/expand-outline`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ outline: this.editOutlineText })
        })
        const data = await res.json()
        if (data.success) {
          const normalized = data.normalized_outline || data.expanded_outline || this.editOutlineText
          this.project.outline = normalized
          this.editOutlineText = normalized
          this.outlineDirty = false
          await this.loadProject()
          await this.loadSections()
          alert(`AI填充已保存并用于生成（章节数：${data.section_count || this.sections.length}，持久化长度：${data.persisted_outline_len || this.project.outline.length}）`)
        } else {
          alert('AI填充失败: ' + (data.error || ''))
        }
      } catch (e) {
        alert('AI填充失败: ' + e.message)
      } finally {
        this.expandingOutline = false
      }
    },

    // ── 章节生成 ──
    async generateSection(sectionId) {
      const s = this.sections.find(x => x.id === sectionId)
      if (s) s.status = 'generating'
      if (this.selectedSection && this.selectedSection.id === sectionId) {
        this.selectedSection.status = 'generating'
      }
      const res = await fetch(`/api/projects/${this.projectId}/generate-section`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ section_id: sectionId })
      })
      await this.updateProgress()
    },

    async generateAllSections() {
      const pending = this.progress.total - this.progress.done - this.progress.errors
      if (!confirm(`将并发生成 ${pending > 0 ? pending : this.progress.total} 个章节内容，确定继续？`)) return
      const res = await fetch(`/api/projects/${this.projectId}/generate-all-sections`, { method: 'POST' })
      const data = await res.json()
      if (!data.success) alert('启动失败: ' + (data.error || ''))
    },

    // ── 章节选择与编辑 ──
    async selectSection(s) {
      if (this.chatSectionId !== s.id) {
        this.chatMessages = []
        this.chatSectionId = s.id
      }
      this.selectedSection = s
      this.editMode = false
      await this.loadSectionContent(s.id)
    },

    switchGenMode(mode) {
      this.genMode = mode
      if (mode === 'chat' && this.chatMessages.length === 0 && this.selectedSection) {
        this.chatMessages = [{
          role: 'assistant',
          content: `您好！我可以协助您以对话方式生成「${this.selectedSection.title}」的章节内容。\n\n您可以这样提问：\n· 请生成该章节完整内容\n· 重点强调××方面的技术措施\n· 帮我补充关于××的具体数据和标准编号\n· 将刚才的内容改得更具体一些\n\n每条 AI 回复下方有「采用此内容」按钮，点击后内容将填入左侧编辑框。`,
          id: Date.now(),
          isWelcome: true
        }]
      }
    },

    async sendChatMessage() {
      const text = this.chatInput.trim()
      if (!text || this.chatLoading || !this.selectedSection) return
      const userMsg = { role: 'user', content: text, id: Date.now() }
      this.chatMessages.push(userMsg)
      this.chatInput = ''
      this.chatLoading = true
      this.$nextTick(() => {
        const el = document.getElementById('chatMsgList')
        if (el) el.scrollTop = el.scrollHeight
      })
      const sendMsgs = this.chatMessages.filter(m => !m.isWelcome)
      try {
        const res = await fetch(
          `/api/projects/${this.projectId}/sections/${this.selectedSection.id}/chat`,
          {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ messages: sendMsgs })
          }
        )
        const data = await res.json()
        if (data.success) {
          this.chatMessages.push({ role: 'assistant', content: data.content, id: Date.now() })
        } else {
          this.chatMessages.push({ role: 'assistant', content: '生成失败：' + (data.error || '未知错误'), id: Date.now(), isError: true })
        }
      } catch (e) {
        this.chatMessages.push({ role: 'assistant', content: '请求失败：' + e.message, id: Date.now(), isError: true })
      } finally {
        this.chatLoading = false
        this.$nextTick(() => {
          const el = document.getElementById('chatMsgList')
          if (el) el.scrollTop = el.scrollHeight
        })
      }
    },

    applyChatContent(content) {
      this.editContent = content
      if (this.sectionContent) this.sectionContent.content = content
      this.genMode = 'auto'
      this.editMode = true
    },

    async loadSectionContent(sectionId) {
      const res = await fetch(`/api/projects/${this.projectId}/sections/${sectionId}`)
      const data = await res.json()
      this.sectionContent = data
      this.editContent = data.content || ''
    },

    async saveContent() {
      if (!this.selectedSection) return
      await fetch(`/api/projects/${this.projectId}/sections/${this.selectedSection.id}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ content: this.editContent })
      })
      if (this.sectionContent) this.sectionContent.content = this.editContent
      this.editMode = false
      const s = this.sections.find(x => x.id === this.selectedSection.id)
      if (s) { s.status = 'done'; s.has_content = true }
    },

    // ── 合规检查 ──
    async runCheck() {
      this.checking = true
      this.checkResult = null
      const res = await fetch(`/api/projects/${this.projectId}/check`, { method: 'POST' })
      const data = await res.json()
      this.checking = false
      if (data.error) {
        alert('检查失败: ' + data.error)
      } else {
        this.checkResult = data
      }
    },

    // ── 导出 ──
    async exportDoc() {
      if (this.sections.length === 0) { alert('请先生成大纲'); return }
      this.exporting = true
      const res = await fetch(`/api/projects/${this.projectId}/export`, { method: 'POST' })
      const data = await res.json()
      this.exporting = false
      if (data.url) {
        const a = document.createElement('a')
        a.href = data.url
        a.download = data.filename
        a.click()
      } else {
        alert('导出失败: ' + (data.error || ''))
      }
    },

    // ── 知识库 ──
    async uploadKnowledge(event) {
      const file = event.target.files[0]
      if (!file) return
      const fd = new FormData()
      fd.append('file', file)
      const res = await fetch('/api/knowledge/upload', { method: 'POST', body: fd })
      const data = await res.json()
      if (data.success) {
        await this.loadKnowledge()
        alert(`上传成功，共处理 ${data.chunks} 个文本块`)
      } else {
        alert('上传失败: ' + (data.error || ''))
      }
    },

    async deleteKnowledge(fileName) {
      if (!confirm(`删除知识文件"${fileName}"？`)) return
      const res = await fetch('/api/knowledge/delete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ file_name: fileName })
      })
      const data = await res.json()
      if (data.success) await this.loadKnowledge()
      else alert('删除失败: ' + (data.error || ''))
    },

    // ── 暗标设置 ──
    addAnonPreset(text) {
      const sep = this.anonText.trim() ? '\n' : ''
      this.anonText = (this.anonText.trim() + sep + '- ' + text).trimStart()
    },

    async saveAnon() {
      this.savingAnon = true
      const anon = this.anonEnabled ? this.anonText.trim() : ''
      await fetch(`/api/projects/${this.projectId}/anon`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ anon_requirements: anon })
      })
      this.project.anon_requirements = anon
      this.savingAnon = false
    },

    async saveDiagram() {
      this.savingDiagram = true
      await fetch(`/api/projects/${this.projectId}/diagram`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ enable_figure: this.enableFigure, enable_table: this.enableTable })
      })
      this.project.enable_figure = this.enableFigure
      this.project.enable_table = this.enableTable
      this.savingDiagram = false
    }
  }
}
</script>

<!-- 页脚版权声明 -->
<footer class="py-4 px-6 border-t border-gray-200 bg-white text-center text-xs text-gray-500 space-y-1">
  <p class="font-medium text-gray-600">© 标书老崔</p>
  <p>本工具仅限学习交流免费使用，生成的技术方案请人工核对。本工具不会在任何平台售卖，请注意甄别。</p>
</footer>
</body>
</html>
