<template>
  <div class="page-wrap">
    <page-section title="用例管理">
      <el-form :inline="true" size="small" @submit.native.prevent>
        <el-form-item label="产品">
          <el-select
            v-model="selectedProductId"
            filterable
            clearable
            placeholder="请选择产品"
            style="width: 220px;"
            @change="handleProductChange"
            @focus="loadProductOptions">
            <el-option
              v-for="item in productOptions"
              :key="item.id"
              :label="item.name"
              :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="项目名称">
          <el-select
            v-model="selectedProjectId"
            filterable
            clearable
            placeholder="请选择项目"
            style="width: 240px;"
            :disabled="!selectedProductId"
            @change="handleProjectChange">
            <el-option
              v-for="item in projectOptions"
              :key="item.id"
              :label="item.name"
              :value="item.id" />
          </el-select>
        </el-form-item>
      </el-form>

      <el-tabs v-model="activeTab" style="margin-top: 8px;">
        <el-tab-pane label="模块列表" name="modules">
          <div class="toolbar-wrap">
            <el-button type="primary" size="small" :disabled="!selectedProjectId" @click="openModuleCreate">新增模块</el-button>
          </div>
          <el-form :inline="true" :model="moduleSearchForm" size="small" style="margin-top: 8px;" @submit.native.prevent>
            <el-form-item label="模块名称">
              <el-input v-model="moduleSearchForm.keyword" clearable style="width: 180px;" @keyup.enter.native="handleModuleSearch"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" :disabled="!selectedProjectId" @click="handleModuleSearch">查询</el-button>
            </el-form-item>
            <el-form-item>
              <el-button size="small" @click="resetModuleSearch">重置</el-button>
            </el-form-item>
          </el-form>
          <el-table v-loading="moduleLoading" :data="pagedModuleData" border style="margin-top: 8px;">
            <el-table-column prop="id" label="模块ID" width="100"></el-table-column>
            <el-table-column prop="name" label="模块名称" min-width="160"></el-table-column>
            <el-table-column prop="parent_name" label="父模块名称" min-width="160"></el-table-column>
            <el-table-column prop="sort_order" label="排序" width="90"></el-table-column>
            <el-table-column prop="path" label="路径" min-width="180"></el-table-column>
            <el-table-column label="操作" width="140" fixed="right">
              <template slot-scope="scope">
                <el-button type="text" @click="openModuleEdit(scope.row)">编辑</el-button>
                <el-button type="text" style="color: #F56C6C;" @click="removeModule(scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <div style="margin-top: 16px; text-align: right;">
            <el-pagination
              :current-page="modulePageNo"
              :page-size="modulePageSize"
              :page-sizes="[10, 20, 50, 100]"
              :total="moduleTotal"
              layout="total, sizes, prev, pager, next, jumper"
              @size-change="handleModuleSizeChange"
              @current-change="handleModuleCurrentChange">
            </el-pagination>
          </div>
        </el-tab-pane>

        <el-tab-pane label="用例列表" name="cases">
          <el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
            <el-form-item label="用例标题">
              <el-input v-model="queryForm.keyword" clearable style="width: 180px;" @keyup.enter.native="fetchList"></el-input>
            </el-form-item>
            <el-form-item label="优先级">
              <el-select v-model="queryForm.priority" clearable style="width: 100px;">
                <el-option label="P0" :value="0"></el-option>
                <el-option label="P1" :value="1"></el-option>
                <el-option label="P2" :value="2"></el-option>
                <el-option label="P3" :value="3"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="标签">
              <el-input v-model="queryForm.tag" clearable style="width: 140px;" @keyup.enter.native="fetchList"></el-input>
            </el-form-item>
            <el-form-item label="创建人">
              <el-input v-model="queryForm.creator" clearable style="width: 140px;" @keyup.enter.native="fetchList"></el-input>
            </el-form-item>
            <el-form-item label="创建时间">
              <el-date-picker
                v-model="createdTimeRange"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                value-format="yyyy-MM-dd"
                style="width: 260px;"
                @change="handleCreatedTimeChange">
              </el-date-picker>
            </el-form-item>
            <el-form-item class="more-filter-item">
              <el-popover
                v-model="moreFilterVisible"
                placement="bottom-start"
                width="560"
                trigger="click">
                <div class="more-filter-wrap">
                  <el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
                    <el-form-item label="模块">
                      <el-select v-model="queryForm.moduleId" clearable filterable style="width: 180px;">
                        <el-option
                          v-for="item in flatModuleOptions"
                          :key="item.id"
                          :label="item.name"
                          :value="item.id">
                        </el-option>
                      </el-select>
                    </el-form-item>
                    <el-form-item label="类型">
                      <el-select v-model="queryForm.caseType" clearable style="width: 180px;">
                        <el-option label="功能" :value="1"></el-option>
                        <el-option label="性能" :value="2"></el-option>
                        <el-option label="安全" :value="3"></el-option>
                        <el-option label="接口" :value="4"></el-option>
                      </el-select>
                    </el-form-item>
                    <el-form-item label="状态">
                      <el-select v-model="queryForm.status" clearable style="width: 180px;">
                        <el-option label="正常" :value="1"></el-option>
                        <el-option label="已废弃" :value="2"></el-option>
                        <el-option label="评审中" :value="3"></el-option>
                        <el-option label="评审通过" :value="4"></el-option>
                      </el-select>
                    </el-form-item>
                    <el-form-item label="是否自动化">
                      <el-select v-model="queryForm.isAuto" clearable style="width: 180px;">
                        <el-option label="未实现" :value="0"></el-option>
                        <el-option label="已实现" :value="1"></el-option>
                      </el-select>
                    </el-form-item>
                  </el-form>
                  <div class="more-filter-footer">
                    <el-button size="small" @click="moreFilterVisible = false">取消</el-button>
                    <el-button type="primary" size="small" :disabled="!selectedProjectId" @click="applyMoreFilters">搜索</el-button>
                  </div>
                </div>
                <el-button slot="reference" size="small">更多筛选</el-button>
              </el-popover>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" :disabled="!selectedProjectId" @click="fetchList">查询</el-button>
            </el-form-item>
            <el-form-item>
              <el-button size="small" @click="resetQuery">重置</el-button>
            </el-form-item>
            <el-form-item class="case-action-buttons-item">
              <el-button type="primary" size="small" :disabled="!selectedProjectId" @click="goEditor()">新建用例</el-button>
              <el-button size="small" :disabled="!selectedProjectId" @click="openCaseImportDialog">导入Excel</el-button>
              <el-popover
                v-model="columnSettingVisible"
                placement="bottom-end"
                width="300"
                trigger="click">
                <div class="column-setting-wrap">
                  <div class="column-setting-title">自定义列表展示字段</div>
                  <el-checkbox-group v-model="selectedCaseColumnKeys" @change="handleColumnSelectionChange">
                    <el-checkbox v-for="item in allCaseColumns" :key="item.key" :label="item.key">{{ item.label }}</el-checkbox>
                  </el-checkbox-group>
                </div>
                <el-button slot="reference" size="small">自定义列表展示字段</el-button>
              </el-popover>
            </el-form-item>
          </el-form>

          <el-table v-loading="loading" :data="tableData" border style="margin-top: 8px;">
            <el-table-column
              v-for="column in visibleCaseColumns"
              :key="column.key"
              :label="column.label"
              :prop="column.prop"
              :min-width="column.minWidth"
              :width="column.width">
              <template slot-scope="scope">
                <template v-if="column.key === 'tags'">
                  <template v-if="Array.isArray(getCaseColumnValue(scope.row, 'tags'))">
                    <el-tag v-for="tag in getCaseColumnValue(scope.row, 'tags')" :key="tag" size="mini" style="margin-right: 4px;">{{ tag }}</el-tag>
                  </template>
                  <span v-else>{{ formatTags(getCaseColumnValue(scope.row, 'tags')) }}</span>
                </template>
                <template v-else>
                  {{ formatCaseColumnValue(column.key, getCaseColumnValue(scope.row, column.key)) }}
                </template>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="140" fixed="right">
              <template slot-scope="scope">
                <el-button type="text" @click="goEditor(scope.row)">编辑</el-button>
                <el-button type="text" style="color: #F56C6C;" @click="remove(scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <div style="margin-top: 16px; text-align: right;">
            <el-pagination
              :current-page="pageNo"
              :page-size="pageSize"
              :page-sizes="[10, 20, 50, 100]"
              :total="total"
              layout="total, sizes, prev, pager, next, jumper"
              @size-change="handleSizeChange"
              @current-change="handleCurrentChange">
            </el-pagination>
          </div>
        </el-tab-pane>
        <el-tab-pane label="用例脑图" name="case-mindmap">
          <div class="toolbar-wrap">
            <el-button size="small" :disabled="!selectedProjectId" @click="fetchCaseMindmapData">刷新结构</el-button>
            <el-button size="small" :disabled="!selectedProjectId" @click="toggleMindmapCollapsed">{{ mindmapCollapsed ? '展开展示' : '合并展示' }}</el-button>
          </div>
          <div v-loading="caseMindmapLoading" class="mindmap-wrap">
            <el-tree
              v-if="caseMindmapTreeData && caseMindmapTreeData.length > 0"
              ref="mindmapTreeRef"
              :key="mindmapRenderKey"
              :data="caseMindmapTreeData"
              node-key="id"
              :default-expand-all="!mindmapCollapsed"
              :expand-on-click-node="false"
              :props="{ children: 'children', label: 'name' }"
              class="xmind-tree">
              <div slot-scope="{ data }" class="mindmap-node-wrap">
                <span class="mindmap-node" :class="{
                  'mindmap-node-project': data.nodeTypeLabel === '项目',
                  'mindmap-node-module': data.nodeTypeLabel === '模块',
                  'mindmap-node-case': data.nodeTypeLabel === '用例',
                  'mindmap-node-active': selectedMindmapCase && selectedMindmapCase.id === data.id
                }" @click.stop="handleMindmapNodeClick(data)">
                  <span class="mindmap-node-title">{{ data.name }}</span>
                  <span class="mindmap-node-meta">
                    <el-tag size="mini" :type="data.nodeTypeLabel === '项目' ? 'success' : (data.nodeTypeLabel === '模块' ? 'warning' : 'info')">{{ data.nodeTypeLabel }}</el-tag>
                    <span v-if="data.creator" class="mindmap-meta-text">创建人：{{ data.creator }}</span>
                    <span v-if="data.updatedTime" class="mindmap-meta-text">更新时间：{{ data.updatedTime }}</span>
                  </span>
                </span>
                <div v-if="data.nodeTypeLabel === '用例' && selectedMindmapCase && selectedMindmapCase.id === data.id" class="mindmap-inline-detail">
                  <div class="mindmap-inline-detail-line"></div>
                  <div class="mindmap-inline-detail-card">
                    <div class="mindmap-inline-detail-header">
                      <div class="mindmap-inline-detail-title">用例详情</div>
                      <el-tag size="mini" :type="formatStatusTagType(data.status)">{{ formatStatus(data.status) || '未知状态' }}</el-tag>
                    </div>
                    <template v-if="mindmapCaseEditing">
                      <div class="mindmap-inline-detail-item">
                        <b>前置条件：</b>
                        <el-input v-model="mindmapCaseEditForm.preconditions" type="textarea" :rows="2"></el-input>
                      </div>
                      <div class="mindmap-inline-detail-item">
                        <b>测试步骤：</b>
                        <el-input v-model="mindmapCaseEditForm.steps" type="textarea" :rows="4"></el-input>
                      </div>
                      <div class="mindmap-inline-detail-item">
                        <b>预期结果：</b>
                        <el-input v-model="mindmapCaseEditForm.expectedResults" type="textarea" :rows="2"></el-input>
                      </div>
                      <div class="mindmap-inline-actions">
                        <el-button size="mini" @click="cancelMindmapCaseEdit">取消</el-button>
                        <el-button type="primary" size="mini" :loading="mindmapCaseSaving" @click="saveMindmapCaseEdit">保存</el-button>
                      </div>
                    </template>
                    <template v-else>
                      <div class="mindmap-inline-detail-item"><b>前置条件：</b>{{ data.preconditions || '无' }}</div>
                      <div class="mindmap-inline-detail-item"><b>测试步骤：</b>{{ formatMindmapSteps(data.steps) || '无' }}</div>
                      <div class="mindmap-inline-detail-item"><b>预期结果：</b>{{ data.expectedResults || '无' }}</div>
                      <div class="mindmap-inline-actions">
                        <el-button size="mini" :loading="mindmapCaseReviewing" @click="startMindmapCaseReview">评审</el-button>
                        <el-button type="primary" plain size="mini" @click="startMindmapCaseEdit">编辑</el-button>
                      </div>
                    </template>
                  </div>
                </div>
              </div>
            </el-tree>
            <div v-else class="mindmap-empty">暂无结构数据</div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </page-section>

    <el-dialog :title="moduleDialogMode === 'edit' ? '编辑模块' : '新增模块'" :visible.sync="moduleDialogVisible" width="520px" @close="resetModuleForm">
      <el-form ref="moduleForm" :model="moduleForm" :rules="moduleRules" label-width="100px" size="small">
        <el-form-item label="产品" prop="productId">
          <el-select v-model="moduleForm.productId" filterable clearable placeholder="请选择产品" style="width: 100%;" @change="handleModuleProductChange" @focus="loadProductOptions">
            <el-option v-for="item in productOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="项目" prop="projectId">
          <el-select v-model="moduleForm.projectId" filterable clearable placeholder="请选择项目" style="width: 100%;" :disabled="!moduleForm.productId" @change="handleModuleProjectChange">
            <el-option v-for="item in moduleProjectOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="模块名称" prop="name">
          <el-input v-model="moduleForm.name"></el-input>
        </el-form-item>
        <el-form-item label="父模块">
          <el-select v-model="moduleForm.parentId" filterable placeholder="不选则为根模块" style="width: 100%;" :disabled="!moduleForm.projectId">
            <el-option label="无（根模块）" value="0"></el-option>
            <el-option v-for="item in moduleParentOptions" :key="item.id" :label="item.name" :value="String(item.id)"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="排序">
          <el-input v-model="moduleForm.sortOrder"></el-input>
        </el-form-item>
        <el-form-item label="路径">
          <el-input v-model="moduleForm.path"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="moduleDialogVisible = false">取消</el-button>
        <el-button type="primary" size="small" :loading="moduleSubmitting" @click="submitModule">{{ moduleDialogMode === 'edit' ? '保存' : '确定' }}</el-button>
      </span>
    </el-dialog>

    <el-dialog title="批量上传" :visible.sync="caseImportDialogVisible" width="780px" @close="resetImportDialog">
      <div class="case-import-panel">
        <div class="case-import-icon">X</div>
        <div class="case-import-title">上传用例</div>
        <div class="case-import-subtitle">仅支持 xlsx、xls 文件，系统将自动解析用例数据</div>
        <div
          class="case-import-dropzone"
          @dragover.prevent
          @drop.prevent="onImportFileDrop">
          <div class="case-import-drop-text">
            拖拽文件到此处，或<span class="link-text" @click="triggerImportFileSelect">点击选择</span>
          </div>
          <div class="case-import-file-tip">仅支持 .xlsx 和 .xls 格式，最大文件大小 20MB</div>
          <div v-if="importFile" class="case-import-file-name">当前文件：{{ importFile.name }}</div>
          <input
            ref="importFileInput"
            class="hidden-file-input"
            type="file"
            accept=".xls,.xlsx"
            @change="onImportFileChange">
        </div>
        <div class="case-import-actions">
          <el-button type="text" @click="downloadImportTemplate">下载标准模板</el-button>
          <el-button type="primary" :disabled="!importFile || !selectedProjectId" :loading="importSubmitting" @click="submitCaseImport">开始上传并解析</el-button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import PageSection from '@/components/TestPlatform/common/PageSection'
import { createModule, deleteCase, deleteModule, downloadCaseImportTemplate, getCaseList, getModuleTree, importCaseExcel, updateCase, updateModule } from '@/api/caseApi'
import { getProductList } from '@/api/productApi'
import { getProjectDetail, getProjectList } from '@/api/projectApi'
import {
  readLastProductProjectCache,
  saveLastProductProjectCache,
  pickIdFromOptions
} from '@/utils/lastProductProjectCache'

export default {
  name: 'CaseList',
  components: { PageSection },
  data() {
    const routeTab = this.$route.query.tab
    return {
      activeTab: routeTab === 'cases' ? 'cases' : 'modules',
      loading: false,
      moduleLoading: false,
      moduleSubmitting: false,
      moduleDialogVisible: false,
      moduleDialogMode: 'create',
      editingModuleId: '',
      caseMindmapLoading: false,
      caseMindmapTreeData: [],
      mindmapCollapsed: false,
      mindmapRenderKey: 0,
      selectedMindmapCase: null,
      mindmapCaseEditing: false,
      mindmapCaseSaving: false,
      mindmapCaseReviewing: false,
      mindmapCaseEditForm: {
        preconditions: '',
        steps: '',
        expectedResults: ''
      },
      projectId: this.$route.query.projectId || '',
      selectedProductId: '',
      selectedProjectId: this.$route.query.projectId ? Number(this.$route.query.projectId) : '',
      productOptions: [],
      projectOptions: [],
      moduleProjectOptions: [],
      moduleParentOptions: [],
      moduleSearchForm: {
        keyword: ''
      },
      modulePageNo: 1,
      modulePageSize: 20,
      pageNo: 1,
      pageSize: 20,
      total: 0,
      moreFilterVisible: false,
      columnSettingVisible: false,
      caseImportDialogVisible: false,
      importSubmitting: false,
      createdTimeRange: [],
      importFile: null,
      queryForm: {
        keyword: '',
        priority: '',
        creator: '',
        createdStartTime: '',
        createdEndTime: '',
        moduleId: '',
        caseType: '',
        status: '',
        isAuto: '',
        tag: ''
      },
      allCaseColumns: [
        { key: 'title', label: '用例标题', prop: 'title', minWidth: 220 },
        { key: 'moduleName', label: '模块名称', prop: 'module_name', minWidth: 140 },
        { key: 'priority', label: '优先级', prop: 'priority', width: 90 },
        { key: 'tags', label: '标签', prop: 'tags', minWidth: 180 },
        { key: 'creator', label: '创建人', prop: 'creator', minWidth: 120 },
        { key: 'createdTime', label: '创建时间', prop: 'created_time', minWidth: 160 },
        { key: 'caseType', label: '类型', prop: 'case_type', width: 90 },
        { key: 'status', label: '状态', prop: 'status', width: 100 },
        { key: 'isAuto', label: '是否自动化', prop: 'is_auto', width: 110 },
        { key: 'caseKey', label: '用例编号', prop: 'case_key', minWidth: 120 },
        { key: 'projectName', label: '项目名称', prop: 'project_name', minWidth: 140 }
      ],
      selectedCaseColumnKeys: ['title', 'moduleName', 'priority', 'tags', 'creator', 'createdTime'],
      moduleQueryForm: {
        projectId: this.$route.query.projectId || ''
      },
      moduleForm: {
        productId: '',
        projectId: this.$route.query.projectId || '',
        name: '',
        parentId: '0',
        sortOrder: '0',
        path: ''
      },
      moduleRules: {
        productId: [{ required: true, message: '请选择产品', trigger: 'change' }],
        projectId: [{ required: true, message: '请选择项目', trigger: 'change' }],
        name: [{ required: true, message: '请输入模块名称', trigger: 'blur' }]
      },
      tableData: [],
      moduleData: []
    }
  },
  computed: {
    visibleCaseColumns() {
      return this.allCaseColumns.filter(item => this.selectedCaseColumnKeys.includes(item.key))
    },
    filteredModuleData() {
      const keyword = (this.moduleSearchForm.keyword || '').trim().toLowerCase()
      if (!keyword) {
        return this.moduleData
      }
      return (this.moduleData || []).filter(item => {
        const source = [
          item.id,
          item.name,
          item.parent_name,
          item.path
        ].map(v => String(v || '').toLowerCase()).join(' ')
        return source.includes(keyword)
      })
    },
    moduleTotal() {
      return (this.filteredModuleData || []).length
    },
    pagedModuleData() {
      const start = (this.modulePageNo - 1) * this.modulePageSize
      const end = start + this.modulePageSize
      return (this.filteredModuleData || []).slice(start, end)
    },
    flatModuleOptions() {
      const result = []
      const walk = (list, prefix) => {
        ;(list || []).forEach(item => {
          const name = prefix ? `${prefix} / ${item.name}` : item.name
          result.push({
            id: item.id,
            name
          })
          const children = item.children || item.child_list || item.childList || []
          if (Array.isArray(children) && children.length > 0) {
            walk(children, name)
          }
        })
      }
      walk(this.moduleData, '')
      return result
    }
  },
  watch: {
    activeTab(val) {
      if (val === 'case-mindmap') {
        this.fetchCaseMindmapData()
      }
    }
  },
  methods: {
    loadProductOptions() {
      if (this.productOptions && this.productOptions.length > 0) {
        return Promise.resolve()
      }
      return getProductList({ pageNo: 1, pageSize: 1000, status: 1 }).then(res => {
        const data = res && res.data ? res.data : res || {}
        this.productOptions = data.items || data.list || data.data || []
      }).catch(() => {
        this.productOptions = []
      })
    },
    loadProjectOptionsByProduct(productId) {
      if (!productId) {
        this.projectOptions = []
        return Promise.resolve()
      }
      return getProjectList({ pageNo: 1, pageSize: 1000, status: 1, productId }).then(res => {
        const data = res && res.data ? res.data : res || {}
        this.projectOptions = data.items || data.list || data.data || []
      }).catch(() => {
        this.projectOptions = []
      })
    },
    loadModuleProjectOptionsByProduct(productId) {
      if (!productId) {
        this.moduleProjectOptions = []
        return Promise.resolve()
      }
      return getProjectList({ pageNo: 1, pageSize: 1000, status: 1, productId }).then(res => {
        const data = res && res.data ? res.data : res || {}
        this.moduleProjectOptions = data.items || data.list || data.data || []
      }).catch(() => {
        this.moduleProjectOptions = []
      })
    },
    loadModuleParentOptionsByProject(projectId) {
      if (!projectId) {
        this.moduleParentOptions = []
        return Promise.resolve()
      }
      return getModuleTree({ projectId }).then(res => {
        const data = (res && res.data) || res || {}
        const list = data.list || data.items || []
        this.moduleParentOptions = Array.isArray(list) ? list : []
      }).catch(() => {
        this.moduleParentOptions = []
      })
    },
    handleProductChange(val) {
      this.selectedProjectId = ''
      this.projectId = ''
      this.moduleQueryForm.projectId = ''
      this.tableData = []
      this.total = 0
      this.moduleData = []
      this.loadProjectOptionsByProduct(val)
    },
    handleProjectChange(val) {
      this.projectId = val || ''
      this.moduleQueryForm.projectId = val || ''
      this.pageNo = 1
      this.modulePageNo = 1
      if (!val) {
        this.tableData = []
        this.total = 0
        this.moduleData = []
        return
      }
      saveLastProductProjectCache(this.selectedProductId, val)
      this.fetchModuleList()
      this.fetchList()
      this.fetchCaseMindmapData()
    },
    restoreSharedProductProjectCache() {
      const cached = readLastProductProjectCache()
      if (!cached) return Promise.resolve()
      const { productId: pid, projectId: projId } = cached
      if (pid === '' || pid === undefined || pid === null || projId === '' || projId === undefined || projId === null) {
        return Promise.resolve()
      }
      const hasProduct = (this.productOptions || []).some(p => String(p.id) === String(pid))
      if (!hasProduct) return Promise.resolve()
      this.selectedProductId = pickIdFromOptions(this.productOptions, pid)
      return this.loadProjectOptionsByProduct(this.selectedProductId).then(() => {
        const hasProject = (this.projectOptions || []).some(p => String(p.id) === String(projId))
        if (!hasProject) return
        const picked = pickIdFromOptions(this.projectOptions, projId)
        this.selectedProjectId = picked
        this.projectId = picked
        this.moduleQueryForm.projectId = picked
      })
    },
    fetchModuleList() {
      if (!this.moduleQueryForm.projectId) {
        this.moduleData = []
        return
      }
      this.moduleLoading = true
      getModuleTree(this.cleanParams(this.moduleQueryForm)).then(res => {
        const data = (res && res.data) || res || {}
        const list = data.list || data.items || []
        this.moduleData = Array.isArray(list) ? list : []
        this.modulePageNo = 1
      }).catch(() => {
        this.moduleData = []
      }).finally(() => {
        this.moduleLoading = false
      })
    },
    fetchCaseMindmapData() {
      if (!this.projectId) {
        this.caseMindmapTreeData = []
        this.selectedMindmapCase = null
        this.mindmapCaseEditing = false
        return
      }
      this.caseMindmapLoading = true
      getModuleTree({
        projectId: this.projectId,
        parentId: 0,
        pageNo: 1,
        pageSize: 200
      }).then(res => {
        const data = (res && res.data) || res || {}
        const rootModules = data.list || data.items || []
        this.caseMindmapTreeData = this.buildCaseMindmapTree(Array.isArray(rootModules) ? rootModules : [])
        this.mindmapRenderKey += 1
        this.selectedMindmapCase = null
        this.mindmapCaseEditing = false
      }).catch(() => {
        this.caseMindmapTreeData = []
        this.selectedMindmapCase = null
        this.mindmapCaseEditing = false
      }).finally(() => {
        this.caseMindmapLoading = false
      })
    },
    toggleMindmapCollapsed() {
      this.mindmapCollapsed = !this.mindmapCollapsed
      this.mindmapRenderKey += 1
    },
    handleMindmapNodeClick(node) {
      if (!node) {
        return
      }
      if (node.nodeTypeLabel === '模块') {
        this.expandMindmapModuleCases(node)
        return
      }
      if (node.nodeTypeLabel === '用例') {
        this.selectedMindmapCase = node
        this.mindmapCaseEditing = false
      }
    },
    expandMindmapModuleCases(moduleNode) {
      if (!moduleNode || !moduleNode.rawId) {
        return
      }
      const moduleId = moduleNode.rawId
      if (moduleNode._childrenChecked && moduleNode._casesLoaded) {
        return
      }
      if (moduleNode._childrenLoading || moduleNode._casesLoading) {
        return
      }
      moduleNode._childrenLoading = true
      getModuleTree({
        projectId: this.projectId,
        parentId: moduleId,
        pageNo: 1,
        pageSize: 200
      }).then(res => {
        const data = (res && res.data) || res || {}
        const children = data.list || data.items || []
        const childList = Array.isArray(children) ? children : []
        moduleNode._childrenChecked = true
        if (childList.length > 0) {
          const childNodes = childList.map(item => this.buildMindmapModuleNode(item))
          moduleNode.children = (moduleNode.children || []).concat(childNodes)
          this.mindmapRenderKey += 1
          return
        }
        moduleNode._casesLoading = true
        const query = Object.assign({}, this.queryForm, {
          moduleId,
          created_by_name: this.queryForm.creator
        })
        delete query.creator
        const params = this.cleanParams(Object.assign({}, query, {
          pageNo: 1,
          pageSize: 20
        }))
        return getCaseList(this.projectId, params).then(caseRes => {
          const caseData = (caseRes && caseRes.data) || caseRes || {}
          const list = caseData.list || caseData.items || []
          const caseNodes = (Array.isArray(list) ? list : []).map(item => this.buildMindmapCaseNode(item))
          moduleNode.children = (moduleNode.children || []).concat(caseNodes)
          moduleNode._casesLoaded = true
          this.mindmapRenderKey += 1
        }).finally(() => {
          moduleNode._casesLoading = false
        })
      }).finally(() => {
        moduleNode._childrenLoading = false
      })
    },
    startMindmapCaseEdit() {
      if (!this.selectedMindmapCase) return
      this.mindmapCaseEditForm = {
        preconditions: this.selectedMindmapCase.preconditions || '',
        steps: this.formatMindmapSteps(this.selectedMindmapCase.steps) || '',
        expectedResults: this.selectedMindmapCase.expectedResults || ''
      }
      this.mindmapCaseEditing = true
    },
    cancelMindmapCaseEdit() {
      this.mindmapCaseEditing = false
    },
    saveMindmapCaseEdit() {
      if (!this.selectedMindmapCase || !this.selectedMindmapCase.rawId) {
        return
      }
      const caseId = this.selectedMindmapCase.rawId
      const payload = this.cleanParams({
        preconditions: this.mindmapCaseEditForm.preconditions,
        steps: this.mindmapCaseEditForm.steps,
        expectedResults: this.mindmapCaseEditForm.expectedResults
      })
      this.mindmapCaseSaving = true
      updateCase(this.projectId, caseId, payload).then(() => {
        this.$message.success('保存成功')
        this.selectedMindmapCase.preconditions = this.mindmapCaseEditForm.preconditions
        this.selectedMindmapCase.steps = this.mindmapCaseEditForm.steps
        this.selectedMindmapCase.expectedResults = this.mindmapCaseEditForm.expectedResults
        this.mindmapCaseEditing = false
        this.fetchList()
      }).finally(() => {
        this.mindmapCaseSaving = false
      })
    },
    startMindmapCaseReview() {
      if (!this.selectedMindmapCase || !this.selectedMindmapCase.rawId) {
        return
      }
      const caseId = this.selectedMindmapCase.rawId
      this.mindmapCaseReviewing = true
      // 先进入评审中
      updateCase(this.projectId, caseId, { status: 3 }).then(() => {
        this.selectedMindmapCase.status = 3
        return this.$confirm('是否评审通过？', '评审确认', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          // 确定后评审通过，状态置为 4
          return updateCase(this.projectId, caseId, { status: 4 }).then(() => {
            this.selectedMindmapCase.status = 4
            this.$message.success('评审通过')
          })
        }).catch(() => {
          // 取消仅关闭弹框，保持评审中
          this.selectedMindmapCase.status = 3
        })
      }).finally(() => {
        this.mindmapCaseReviewing = false
        this.fetchList()
      })
    },
    formatMindmapSteps(steps) {
      if (!steps) return ''
      if (typeof steps === 'string') return steps
      if (Array.isArray(steps)) {
        return steps.map(item => {
          if (typeof item === 'string') return item
          return item.action || item.step || item.text || item.content || ''
        }).filter(Boolean).join('\n')
      }
      return String(steps)
    },
    formatStatusTagType(status) {
      if (status === 1) return 'success'
      if (status === 3) return 'warning'
      if (status === 2) return 'info'
      if (status === 4) return 'success'
      return ''
    },
    buildCaseMindmapTree(rootModules) {
      const moduleChildren = (rootModules || []).map(item => this.buildMindmapModuleNode(item))
      const currentProject = (this.projectOptions || []).find(item => String(item.id) === String(this.projectId))
      return [{
        id: `project-${this.projectId}`,
        rawId: this.projectId,
        nodeTypeLabel: '项目',
        name: (currentProject && currentProject.name) || `项目-${this.projectId}`,
        creator: '',
        updatedTime: '',
        children: moduleChildren
      }]
    },
    buildMindmapModuleNode(item) {
      return {
        id: `module-${item.id}`,
        rawId: item.id,
        nodeTypeLabel: '模块',
        name: item.name || `模块-${item.id}`,
        creator: '',
        updatedTime: item.updated_time || item.updatedTime || '',
        parentId: item.parent_id || item.parentId || 0,
        _childrenChecked: false,
        _childrenLoading: false,
        _casesLoaded: false,
        _casesLoading: false,
        children: []
      }
    },
    buildMindmapCaseNode(item) {
      return {
        id: `case-${item.id}`,
        rawId: item.id,
        nodeTypeLabel: '用例',
        name: item.title || item.case_key || `用例-${item.id}`,
        creator: item.created_by_name || '',
        updatedTime: item.updated_time || item.updatedTime || '',
        status: item.status,
        preconditions: item.preconditions || '',
        steps: item.steps || '',
        expectedResults: item.expected_results || item.expectedResults || ''
      }
    },
    handleModuleSearch() {
      this.modulePageNo = 1
    },
    resetModuleSearch() {
      this.moduleSearchForm.keyword = ''
      this.modulePageNo = 1
    },
    handleModuleSizeChange(size) {
      this.modulePageSize = size
      this.modulePageNo = 1
    },
    handleModuleCurrentChange(page) {
      this.modulePageNo = page
    },
    openModuleCreate() {
      this.moduleDialogMode = 'create'
      this.editingModuleId = ''
      this.moduleDialogVisible = true
      this.moduleForm.productId = this.selectedProductId || ''
      this.moduleForm.projectId = this.selectedProjectId || this.moduleQueryForm.projectId || this.projectId || ''
      this.moduleForm.parentId = '0'
      this.loadModuleProjectOptionsByProduct(this.moduleForm.productId)
      this.loadModuleParentOptionsByProject(this.moduleForm.projectId)
    },
    openModuleEdit(row) {
      this.moduleDialogMode = 'edit'
      this.editingModuleId = row.id
      this.moduleDialogVisible = true
      this.moduleForm.productId = this.selectedProductId || ''
      this.moduleForm.projectId = row.project_id || row.projectId || this.selectedProjectId || this.moduleQueryForm.projectId || this.projectId || ''
      this.moduleForm.name = row.name || ''
      this.moduleForm.parentId = String(row.parent_id || row.parentId || 0)
      this.moduleForm.sortOrder = String(row.sort_order || row.sortOrder || 0)
      this.moduleForm.path = row.path || ''
      this.loadModuleProjectOptionsByProduct(this.moduleForm.productId)
      this.loadModuleParentOptionsByProject(this.moduleForm.projectId).then(() => {
        this.moduleParentOptions = (this.moduleParentOptions || []).filter(item => item.id !== row.id)
      })
    },
    handleModuleProductChange(val) {
      this.moduleForm.projectId = ''
      this.moduleForm.parentId = '0'
      this.moduleParentOptions = []
      this.loadModuleProjectOptionsByProduct(val)
    },
    handleModuleProjectChange(val) {
      this.moduleForm.parentId = '0'
      this.loadModuleParentOptionsByProject(val)
    },
    resetModuleForm() {
      this.moduleDialogMode = 'create'
      this.editingModuleId = ''
      this.moduleForm = {
        productId: this.selectedProductId || '',
        projectId: this.selectedProjectId || this.moduleQueryForm.projectId || this.projectId || '',
        name: '',
        parentId: '0',
        sortOrder: '0',
        path: ''
      }
      this.loadModuleParentOptionsByProject(this.moduleForm.projectId)
      this.$nextTick(() => {
        this.$refs.moduleForm && this.$refs.moduleForm.clearValidate()
      })
    },
    submitModule() {
      this.$refs.moduleForm.validate(valid => {
        if (!valid) {
          return
        }
        this.moduleSubmitting = true
        const payload = this.cleanParams({
          projectId: this.moduleForm.projectId,
          name: this.moduleForm.name,
          parentId: this.moduleForm.parentId,
          sortOrder: this.moduleForm.sortOrder,
          path: this.moduleForm.path
        })
        const request = this.moduleDialogMode === 'edit'
          ? updateModule(Object.assign({ moduleId: this.editingModuleId }, payload))
          : createModule(payload)
        request.then(() => {
          this.$message({ type: 'success', message: this.moduleDialogMode === 'edit' ? '保存成功' : '新增成功' })
          this.moduleDialogVisible = false
          if (this.selectedProjectId !== this.moduleForm.projectId) {
            this.selectedProjectId = this.moduleForm.projectId
            this.projectId = this.moduleForm.projectId
            this.moduleQueryForm.projectId = this.moduleForm.projectId
          }
          this.fetchModuleList()
        }).finally(() => {
          this.moduleSubmitting = false
        })
      })
    },
    removeModule(row) {
      this.$confirm('确认删除该模块吗？', '提示', { type: 'warning' }).then(() => {
        deleteModule({ moduleId: row.id }).then(() => {
          this.$message({ type: 'success', message: '删除成功' })
          this.fetchModuleList()
        })
      }).catch(() => {})
    },
    fetchList() {
      if (!this.projectId) {
        this.tableData = []
        this.total = 0
        return
      }
      this.loading = true
      const query = Object.assign({}, this.queryForm, {
        // 后端创建人字段为 created_by_name
        created_by_name: this.queryForm.creator
      })
      delete query.creator
      const params = this.cleanParams(Object.assign({}, query, {
        pageNo: this.pageNo,
        pageSize: this.pageSize
      }))
      getCaseList(this.projectId, params).then(res => {
        const data = (res && res.data) || res || {}
        const list = data.list || data.items || []
        this.tableData = Array.isArray(list) ? list : []
        this.total = Number(data.total || this.tableData.length || 0)
      }).catch(() => {
        this.tableData = []
        this.total = 0
      }).finally(() => {
        this.loading = false
      })
    },
    handleSizeChange(size) {
      this.pageSize = size
      this.pageNo = 1
      this.fetchList()
    },
    handleCurrentChange(page) {
      this.pageNo = page
      this.fetchList()
    },
    handleCreatedTimeChange(value) {
      const range = Array.isArray(value) ? value : []
      this.queryForm.createdStartTime = range[0] || ''
      this.queryForm.createdEndTime = range[1] || ''
    },
    applyMoreFilters() {
      this.moreFilterVisible = false
      this.pageNo = 1
      this.fetchList()
      if (this.activeTab === 'case-mindmap') {
        this.fetchCaseMindmapData()
      }
    },
    resetQuery() {
      this.queryForm = {
        keyword: '',
        priority: '',
        creator: '',
        createdStartTime: '',
        createdEndTime: '',
        moduleId: '',
        caseType: '',
        status: '',
        isAuto: '',
        tag: ''
      }
      this.createdTimeRange = []
      this.pageNo = 1
      this.fetchList()
      if (this.activeTab === 'case-mindmap') {
        this.fetchCaseMindmapData()
      }
    },
    handleColumnSelectionChange(value) {
      if (value.length === 0) {
        this.$message.warning('至少保留一个展示字段')
        this.selectedCaseColumnKeys = ['title']
      }
    },
    getCaseColumnValue(row, key) {
      const map = {
        title: row.title,
        moduleName: row.module_name,
        priority: row.priority,
        tags: row.tags,
        creator: row.created_by_name || row.creator || row.creator_name || row.create_user || row.createUser || row.created_by || '',
        createdTime: row.created_time || row.create_time || row.createdTime || row.createTime || '',
        caseType: row.case_type,
        status: row.status,
        isAuto: row.is_auto,
        caseKey: row.case_key,
        projectName: row.project_name
      }
      return map[key]
    },
    formatCaseColumnValue(key, value) {
      if (key === 'priority') return this.formatPriority(value)
      if (key === 'caseType') return this.formatCaseType(value)
      if (key === 'status') return this.formatStatus(value)
      if (key === 'isAuto') return this.formatIsAuto(value)
      return value
    },
    openCaseImportDialog() {
      this.caseImportDialogVisible = true
    },
    resetImportDialog() {
      this.importFile = null
      if (this.$refs.importFileInput) {
        this.$refs.importFileInput.value = ''
      }
    },
    triggerImportFileSelect() {
      this.$refs.importFileInput && this.$refs.importFileInput.click()
    },
    onImportFileChange(event) {
      const files = event && event.target && event.target.files ? event.target.files : []
      const file = files[0]
      this.setImportFile(file)
    },
    onImportFileDrop(event) {
      const files = event && event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : []
      const file = files[0]
      this.setImportFile(file)
    },
    setImportFile(file) {
      if (!file) {
        return
      }
      const lowerName = String(file.name || '').toLowerCase()
      const isExcel = lowerName.endsWith('.xls') || lowerName.endsWith('.xlsx')
      if (!isExcel) {
        this.$message.warning('仅支持 .xls 或 .xlsx 文件')
        return
      }
      const maxSize = 20 * 1024 * 1024
      if (file.size > maxSize) {
        this.$message.warning('文件大小不能超过 20MB')
        return
      }
      this.importFile = file
    },
    downloadImportTemplate() {
      if (!this.selectedProjectId) {
        this.$message.warning('请先选择项目')
        return
      }
      downloadCaseImportTemplate(this.selectedProjectId).then(res => {
        const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
        const url = window.URL.createObjectURL(blob)
        const link = document.createElement('a')
        link.href = url
        link.download = '用例导入模板.xlsx'
        link.click()
        window.URL.revokeObjectURL(url)
      })
    },
    submitCaseImport() {
      if (!this.selectedProjectId) {
        this.$message.warning('请先选择项目')
        return
      }
      if (!this.importFile) {
        this.$message.warning('请先选择导入文件')
        return
      }
      this.importSubmitting = true
      importCaseExcel(this.selectedProjectId, this.importFile).then(() => {
        this.$message.success('导入并解析成功')
        this.caseImportDialogVisible = false
        this.fetchList()
      }).finally(() => {
        this.importSubmitting = false
      })
    },
    goEditor(row) {
      this.$router.push({
        path: '/test-platform/case/editor',
        query: {
          productId: this.selectedProductId || undefined,
          projectId: this.projectId,
          caseId: row && row.id
        }
      })
    },
    goReview(row) {
      this.$router.push({ path: '/test-platform/case/review', query: { projectId: this.projectId, caseId: row.id } })
    },
    remove(row) {
      this.$confirm('确认删除该用例吗？', '提示', { type: 'warning' }).then(() => {
        deleteCase(this.projectId, row.id).then(() => {
          this.$message({ type: 'success', message: '删除成功' })
          this.fetchList()
        })
      }).catch(() => {})
    },
    cleanParams(params) {
      return Object.keys(params).reduce((result, key) => {
        if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
          result[key] = params[key]
        }
        return result
      }, {})
    },
    formatPriority(value) {
      const map = { 0: 'P0', 1: 'P1', 2: 'P2', 3: 'P3' }
      return map[value] || value
    },
    formatCaseType(value) {
      const map = { 1: '功能', 2: '性能', 3: '安全', 4: '接口' }
      return map[value] || value
    },
    formatStatus(value) {
      const map = { 1: '正常', 2: '已废弃', 3: '评审中', 4: '评审通过' }
      return map[value] || value
    },
    formatIsAuto(value) {
      const map = { 0: '未实现', 1: '已实现' }
      return map[value] || value
    },
    formatTags(tags) {
      return Array.isArray(tags) ? tags.join('、') : (tags || '')
    }
  },
  created() {
    this.loadProductOptions().then(() => {
      const routeProductId = this.$route.query.productId ? Number(this.$route.query.productId) : ''
      if (routeProductId) {
        this.selectedProductId = routeProductId
        return this.loadProjectOptionsByProduct(routeProductId)
      }
      if (this.selectedProjectId) {
        return getProjectDetail(this.selectedProjectId).then(res => {
          const data = res && res.data ? res.data : res || {}
          const productId = data.productId || data.product_id || ''
          if (productId) {
            this.selectedProductId = productId
            return this.loadProjectOptionsByProduct(productId)
          }
        }).catch(() => {})
      }
      return this.restoreSharedProductProjectCache()
    }).finally(() => {
      if (this.selectedProjectId) {
        this.projectId = this.selectedProjectId
        this.moduleQueryForm.projectId = this.selectedProjectId
        this.fetchModuleList()
        this.fetchList()
      }
    })
  }
}
</script>

<style scoped>
.page-wrap {
  padding: 20px;
}

.toolbar-wrap {
  text-align: right;
  margin-top: 8px;
}

.case-action-buttons-item {
  float: right;
  margin-right: 0 !important;
}

.more-filter-wrap {
  padding: 4px 0;
}

.more-filter-footer {
  border-top: 1px solid #ebeef5;
  text-align: right;
  padding-top: 10px;
}

.column-setting-wrap {
  max-height: 320px;
  overflow-y: auto;
}

.column-setting-title {
  color: #606266;
  font-weight: 500;
  margin-bottom: 8px;
}

.case-import-panel {
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 28px 24px;
  text-align: center;
}

.case-import-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  color: #fff;
  background: #409eff;
  font-weight: 600;
  margin-bottom: 8px;
}

.case-import-title {
  font-size: 28px;
  color: #303133;
  margin-bottom: 6px;
}

.case-import-subtitle {
  color: #909399;
  margin-bottom: 16px;
}

.case-import-dropzone {
  border: 1px dashed #dcdfe6;
  border-radius: 4px;
  padding: 28px 20px;
  margin: 0 auto;
  max-width: 520px;
}

.case-import-drop-text {
  color: #606266;
  margin-bottom: 10px;
}

.link-text {
  color: #409eff;
  cursor: pointer;
}

.case-import-file-tip {
  color: #909399;
}

.case-import-file-name {
  margin-top: 8px;
  color: #303133;
}

.case-import-actions {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.hidden-file-input {
  display: none;
}

.mindmap-wrap {
  margin-top: 8px;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  background: #fafcff;
  padding: 14px 12px;
  min-height: 280px;
  overflow: auto;
}

.xmind-tree {
  min-width: 980px;
  background: transparent;
}

.xmind-tree /deep/ .el-tree-node {
  position: relative;
}

.xmind-tree /deep/ .el-tree-node__content {
  height: auto;
  padding: 8px 0;
}

.xmind-tree /deep/ .el-tree-node__children {
  position: relative;
  margin-left: 12px;
  padding-left: 22px;
}

.xmind-tree /deep/ .el-tree-node__children:before {
  content: '';
  position: absolute;
  left: 8px;
  top: 0;
  bottom: 10px;
  border-left: 1px solid #b7d0e8;
}

.xmind-tree /deep/ .el-tree-node__content:before {
  content: '';
  position: absolute;
  left: -14px;
  top: 50%;
  width: 14px;
  border-top: 1px solid #8fb8de;
}

.xmind-tree /deep/ .el-tree > .el-tree-node > .el-tree-node__content:before {
  display: none;
}

.mindmap-node {
  display: inline-flex;
  flex-direction: column;
  gap: 6px;
  min-width: 220px;
  max-width: 760px;
  background: #fff;
  border: 1px solid #dce6ff;
  border-radius: 8px;
  padding: 8px 10px;
  box-shadow: 0 1px 4px rgba(64, 158, 255, 0.08);
}

.mindmap-node-wrap {
  display: inline-flex;
  align-items: flex-start;
}

.mindmap-node-title {
  color: #303133;
  font-weight: 600;
  line-height: 1.4;
}

.mindmap-node-project {
  border-color: #67c23a;
  box-shadow: 0 1px 6px rgba(103, 194, 58, 0.16);
}

.mindmap-node-module {
  border-color: #e6a23c;
  box-shadow: 0 1px 6px rgba(230, 162, 60, 0.14);
}

.mindmap-node-case {
  border-color: #7fb3e3;
}

.mindmap-inline-detail {
  display: inline-flex;
  align-items: stretch;
  margin-left: 14px;
}

.mindmap-inline-detail-line {
  width: 18px;
  margin-top: 22px;
  border-top: 1px solid #8fb8de;
}

.mindmap-inline-detail-card {
  width: 420px;
  max-width: 620px;
  background: #fff;
  border: 1px solid #d9e8f6;
  border-radius: 8px;
  padding: 10px 12px;
  box-shadow: 0 1px 6px rgba(64, 158, 255, 0.12);
}

.mindmap-inline-detail-title {
  color: #303133;
  font-weight: 600;
}

.mindmap-inline-detail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.mindmap-inline-detail-item {
  color: #606266;
  line-height: 1.6;
  margin-bottom: 6px;
  white-space: pre-wrap;
}

.mindmap-inline-detail-item:last-child {
  margin-bottom: 0;
}

.mindmap-inline-actions {
  text-align: right;
  margin-top: 8px;
}

.mindmap-node-meta {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.mindmap-meta-text {
  color: #909399;
  font-size: 12px;
}

.mindmap-empty {
  color: #909399;
  text-align: center;
  line-height: 220px;
}
</style>
