<script setup>
import { DataAnalysis, Delete, Document, Edit, Folder, FolderOpened, Upload, Search } from '@element-plus/icons-vue'
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { ElAutoResizer, ElMessage, ElMessageBox, ElTableV2 } from 'element-plus'
import {
  createCategory,
  deleteCategory,
  deleteDataFile,
  downloadDataTemplate,
  getCategoryTree,
  getDataFiles,
  getFileQuality,
  getFileRecords,
  updateCategory,
  updateDataFile,
  uploadDataFile,
} from '@/api/dataManagement'
import DataCurve from './components/DataCurve.vue'

const categoryTree = ref([])
const selectedCategory = ref('all')

const searchForm = reactive({
  filename: '',
})

const fileList = ref([])
const loadingFiles = ref(false)

const currentFile = ref(null)
const recordList = ref([])
const contentLoading = ref(false)
const contentMode = ref('table')

const recordColumns = [
  { key: 'time', dataKey: 'time', title: '时间', width: 170 },
  { key: 'current', dataKey: 'current', title: '电流', width: 110 },
  { key: 'voltage', dataKey: 'voltage', title: '电压', width: 110 },
  { key: 'set_temperature', dataKey: 'set_temperature', title: '设定温度', width: 120 },
  { key: 'actual_temperature', dataKey: 'actual_temperature', title: '实际温度', width: 120 },
]

const curveRecords = computed(() => {
  const r = recordList.value
  if (r.length <= 2000) return r
  const step = Math.ceil(r.length / 2000)
  return r.filter((_, i) => i % step === 0)
})

const categoryDialogVisible = ref(false)
const categoryDialogMode = ref('create')
const editingCategoryId = ref('')
const categoryForm = reactive({
  name: '',
})

const uploadDialogVisible = ref(false)
const uploadForm = reactive({
  categoryId: '',
  remark: '',
})

const qualityDialogVisible = ref(false)
const qualityLoading = ref(false)
const qualityFile = ref(null)
const qualityResult = ref(null)

const fileEditDialogVisible = ref(false)
const fileEditSubmitting = ref(false)
const editingFileId = ref(null)
const fileEditForm = reactive({
  filename: '',
  remark: '',
  categoryId: '',
})

const openFileEditDialog = (row) => {
  editingFileId.value = row.id
  fileEditForm.filename = row.filename
  fileEditForm.remark = row.remark || ''
  fileEditForm.categoryId = row.category_id ? String(row.category_id) : ''
  fileEditDialogVisible.value = true
}

const submitFileEdit = async () => {
  const name = fileEditForm.filename.trim()
  if (!name) {
    ElMessage.warning('文件名不能为空')
    return
  }
  if (!fileEditForm.categoryId) {
    ElMessage.warning('请选择分类')
    return
  }
  fileEditSubmitting.value = true
  const prevCategoryId = fileList.value.find((f) => f.id === editingFileId.value)?.category_id
  try {
    const result = await updateDataFile(editingFileId.value, {
      filename: name,
      remark: fileEditForm.remark.trim() || null,
      category_id: fileEditForm.categoryId,
    })
    ElMessage.success('保存成功')
    fileEditDialogVisible.value = false

    const categoryChanged = String(result.category_id) !== String(prevCategoryId)
    if (categoryChanged) {
      await loadFileList()
      if (currentFile.value?.id === editingFileId.value) {
        currentFile.value = null
        recordList.value = []
      }
    } else {
      const idx = fileList.value.findIndex((f) => f.id === editingFileId.value)
      if (idx !== -1) {
        fileList.value[idx].filename = result.filename
        fileList.value[idx].remark = result.remark || ''
      }
      if (currentFile.value?.id === editingFileId.value) {
        currentFile.value = { ...currentFile.value, filename: result.filename, remark: result.remark || '' }
      }
    }
  } finally {
    fileEditSubmitting.value = false
  }
}

const qualityColorClass = (percent) => {
  if (percent >= 90) return 'quality-good'
  if (percent >= 70) return 'quality-warn'
  return 'quality-bad'
}

const handleQualityCheck = async (row) => {
  qualityFile.value = row
  qualityResult.value = null
  qualityDialogVisible.value = true
  qualityLoading.value = true
  try {
    qualityResult.value = await getFileQuality(row.id)
  } catch {
    ElMessage.error('质量评估失败')
    qualityDialogVisible.value = false
  } finally {
    qualityLoading.value = false
  }
}

const layoutRef = ref(null)
const leftPaneWidth = ref(16)
const middlePaneWidth = ref(38)
const dragState = ref(null)
const pendingDragX = ref(null)
let dragFrameId = 0
const layoutStorageKey = 'data-management-layout-v1'

const MIN_LEFT = 12
const MAX_LEFT = 24
const MIN_MIDDLE = 30
const MAX_MIDDLE = 56
const MIN_RIGHT = 24

const rightPaneWidth = computed(() => {
  const value = 100 - leftPaneWidth.value - middlePaneWidth.value
  return Number(value.toFixed(2))
})

const categoryDialogTitle = computed(() => {
  return categoryDialogMode.value === 'create' ? '新增分类' : '编辑分类'
})

const flatCategoryOptions = computed(() => {
  const root = categoryTree.value.find((item) => String(item.id) === 'all')
  return (root?.children || []).map((item) => ({
    label: item.name,
    value: String(item.id),
  }))
})

const normalizeCategoryTree = (treeData) => {
  const source = Array.isArray(treeData) ? treeData : []
  const oneLevel = source.filter((item) => String(item?.id) !== 'all')

  const children = oneLevel.map((item) => ({
    id: item.id,
    name: item.name,
    parent_id: item.parent_id,
    children: [],
  }))

  return [
    {
      id: 'all',
      name: '全部',
      parent_id: null,
      children,
    },
  ]
}

const setPaneWidths = (nextLeft, nextMiddle, target = 'left') => {
  let left = Math.min(Math.max(nextLeft, MIN_LEFT), MAX_LEFT)
  let middle = Math.min(Math.max(nextMiddle, MIN_MIDDLE), MAX_MIDDLE)

  if (100 - left - middle < MIN_RIGHT) {
    if (target === 'left') {
      left = 100 - middle - MIN_RIGHT
      if (left < MIN_LEFT) {
        left = MIN_LEFT
        middle = 100 - left - MIN_RIGHT
      }
    } else {
      middle = 100 - left - MIN_RIGHT
      if (middle < MIN_MIDDLE) {
        middle = MIN_MIDDLE
        left = 100 - middle - MIN_RIGHT
      }
    }
  }

  leftPaneWidth.value = Number(left.toFixed(2))
  middlePaneWidth.value = Number(middle.toFixed(2))
}

const loadLayoutCache = () => {
  const raw = localStorage.getItem(layoutStorageKey)
  if (!raw) {
    return
  }

  try {
    const parsed = JSON.parse(raw)
    if (typeof parsed.left === 'number' && typeof parsed.middle === 'number') {
      setPaneWidths(parsed.left, parsed.middle, 'middle')
    }
  } catch {
    localStorage.removeItem(layoutStorageKey)
  }
}

const saveLayoutCache = () => {
  localStorage.setItem(
    layoutStorageKey,
    JSON.stringify({
      left: leftPaneWidth.value,
      middle: middlePaneWidth.value,
    }),
  )
}

const applyDragPosition = (clientX) => {
  if (!dragState.value) {
    return
  }

  const delta = ((clientX - dragState.value.startX) / dragState.value.layoutWidth) * 100

  if (dragState.value.target === 'left') {
    setPaneWidths(dragState.value.startLeft + delta, dragState.value.startMiddle, 'left')
  } else {
    setPaneWidths(dragState.value.startLeft, dragState.value.startMiddle + delta, 'middle')
  }
}

const flushDragging = () => {
  dragFrameId = 0
  if (pendingDragX.value === null) {
    return
  }
  applyDragPosition(pendingDragX.value)
}

const stopDragging = () => {
  window.removeEventListener('mousemove', handleDragging)
  window.removeEventListener('mouseup', stopDragging)
  window.removeEventListener('mouseleave', stopDragging)
  window.removeEventListener('blur', stopDragging)

  document.body.style.userSelect = ''
  document.body.style.cursor = ''

  if (dragFrameId) {
    cancelAnimationFrame(dragFrameId)
    dragFrameId = 0
  }

  pendingDragX.value = null

  if (dragState.value) {
    saveLayoutCache()
  }
  dragState.value = null
}

const handleDragging = (event) => {
  if (!dragState.value) {
    return
  }

  pendingDragX.value = event.clientX

  if (!dragFrameId) {
    dragFrameId = requestAnimationFrame(flushDragging)
  }
}

const startDragging = (target, event) => {
  if (!layoutRef.value) {
    return
  }

  event.preventDefault()
  stopDragging()

  dragState.value = {
    target,
    startX: event.clientX,
    startLeft: leftPaneWidth.value,
    startMiddle: middlePaneWidth.value,
    layoutWidth: layoutRef.value.clientWidth,
  }

  document.body.style.userSelect = 'none'
  document.body.style.cursor = 'col-resize'

  window.addEventListener('mousemove', handleDragging)
  window.addEventListener('mouseup', stopDragging)
  window.addEventListener('mouseleave', stopDragging)
  window.addEventListener('blur', stopDragging)
}

const loadCategoryTree = async () => {
  const treeData = await getCategoryTree()
  categoryTree.value = normalizeCategoryTree(treeData)

  const allIds = [
    ...categoryTree.value.map((item) => String(item.id)),
    ...categoryTree.value.flatMap((item) => (item.children || []).map((child) => String(child.id))),
  ]

  if (!allIds.includes(String(selectedCategory.value))) {
    selectedCategory.value = 'all'
  }
}

const loadFileList = async () => {
  loadingFiles.value = true
  try {
    const data = await getDataFiles({
      category_id: selectedCategory.value && selectedCategory.value !== 'all' ? selectedCategory.value : '',
      filename: searchForm.filename.trim(),
    })
    fileList.value = data
  } finally {
    loadingFiles.value = false
  }
}

const handleCategoryClick = async (node) => {
  selectedCategory.value = node.id
  currentFile.value = null
  recordList.value = []
  await loadFileList()
}

const openCreateCategoryDialog = () => {
  categoryDialogMode.value = 'create'
  editingCategoryId.value = ''
  categoryForm.name = ''
  categoryDialogVisible.value = true
}

const openEditCategoryDialog = (node) => {
  categoryDialogMode.value = 'edit'
  editingCategoryId.value = node.id
  categoryForm.name = node.name
  categoryDialogVisible.value = true
}

const submitCategory = async () => {
  const categoryName = categoryForm.name.trim()
  if (!categoryName) {
    ElMessage.warning('请输入分类名称')
    return
  }

  if (categoryDialogMode.value === 'create') {
    await createCategory({
      name: categoryName,
      parent_id: null,
    })
  } else {
    await updateCategory(editingCategoryId.value, {
      name: categoryName,
    })
  }

  categoryDialogVisible.value = false
  ElMessage.success('分类保存成功')
  await loadCategoryTree()
}

const handleDeleteCategory = async (node) => {
  try {
    await ElMessageBox.confirm(`确定删除分类“${node.name}”吗？`, '提示', {
      type: 'warning',
    })
    await deleteCategory(node.id)
    ElMessage.success('分类已删除')
    if (selectedCategory.value === node.id) {
      selectedCategory.value = 'all'
    }
    await loadCategoryTree()
    await loadFileList()
  } catch {
    // 用户取消删除时不提示错误
  }
}

const handleSearch = async () => {
  await loadFileList()
}

const handleReset = async () => {
  searchForm.filename = ''
  await loadFileList()
}

const openUploadDialog = () => {
  uploadForm.categoryId = selectedCategory.value && selectedCategory.value !== 'all' ? String(selectedCategory.value) : ''
  uploadForm.remark = ''
  uploadDialogVisible.value = true
}

const beforeUploadExcel = (file) => {
  if (!uploadForm.categoryId) {
    ElMessage.warning('请先在弹窗中选择分类')
    return false
  }

  const lowerName = file.name.toLowerCase()
  const valid = lowerName.endsWith('.xlsx') || lowerName.endsWith('.xls') || lowerName.endsWith('.csv')
  if (!valid) {
    ElMessage.warning('仅支持数据文件（.xlsx/.xls/.csv）')
  }
  return valid
}

const handleUpload = async (uploadRequest) => {
  if (!uploadForm.categoryId) {
    ElMessage.warning('请先选择分类后再上传')
    uploadRequest.onError(new Error('未选择分类'))
    return
  }

  const formData = new FormData()
  formData.append('file', uploadRequest.file)
  formData.append('category_id', String(uploadForm.categoryId))
  if (uploadForm.remark.trim()) {
    formData.append('remark', uploadForm.remark.trim())
  }

  try {
    await uploadDataFile(formData)
    selectedCategory.value = String(uploadForm.categoryId)
    uploadDialogVisible.value = false
    ElMessage.success('上传成功')
    await loadFileList()
    uploadRequest.onSuccess()
  } catch (error) {
    uploadRequest.onError(error)
  }
}

const handleDownloadTemplate = async () => {
  const blob = await downloadDataTemplate()
  const url = window.URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = '上传文件模板.xlsx'
  document.body.appendChild(link)
  link.click()
  link.remove()
  window.URL.revokeObjectURL(url)
}

const handleViewFile = async (row) => {
  currentFile.value = row
  contentLoading.value = true
  try {
    const result = await getFileRecords(row.id)
    recordList.value = result.records
  } finally {
    contentLoading.value = false
  }
}

const handleDeleteFile = async (row) => {
  try {
    await ElMessageBox.confirm(`确定删除文件“${row.filename}”吗？`, '提示', {
      type: 'warning',
    })
    await deleteDataFile(row.id)
    ElMessage.success('文件已删除')

    if (currentFile.value?.id === row.id) {
      currentFile.value = null
      recordList.value = []
    }
    await loadFileList()
  } catch {
    // 用户取消删除时不提示错误
  }
}

const selectedFiles = ref([])
const handleFileSelectionChange = (rows) => {
  selectedFiles.value = rows
}

const handleBatchDeleteFiles = async () => {
  if (!selectedFiles.value.length) return
  try {
    await ElMessageBox.confirm(
      `确定删除选中的 ${selectedFiles.value.length} 个文件吗？此操作不可恢复。`,
      '批量删除',
      { type: 'warning' },
    )
    await Promise.all(selectedFiles.value.map((f) => deleteDataFile(f.id)))
    ElMessage.success(`已删除 ${selectedFiles.value.length} 个文件`)
    if (selectedFiles.value.some((f) => f.id === currentFile.value?.id)) {
      currentFile.value = null
      recordList.value = []
    }
    selectedFiles.value = []
    await loadFileList()
  } catch {
    // user cancelled
  }
}

onMounted(async () => {
  loadLayoutCache()
  await loadCategoryTree()
  selectedCategory.value = selectedCategory.value || 'all'
  await loadFileList()
})

onBeforeUnmount(() => {
  stopDragging()
})
</script>

<template>
  <section class="data-page" :class="{ dragging: !!dragState }">
    <div ref="layoutRef" class="data-layout">
      <el-card class="pane-card pane-card--tree" shadow="hover" :style="{ width: `${leftPaneWidth}%` }">
        <template #header>
          <div class="pane-header">
            <div>
              <span>分类树</span>
            </div>
            <el-button type="primary" plain size="small" @click="openCreateCategoryDialog">
              新增
            </el-button>
          </div>
        </template>

        <el-tree
          :data="categoryTree"
          node-key="id"
          highlight-current
          default-expand-all
          :current-node-key="selectedCategory || undefined"
          :expand-on-click-node="false"
          @node-click="handleCategoryClick"
        >
          <template #default="{ data }">
            <div class="tree-node">
              <div class="tree-main">
                <el-icon class="tree-icon">
                  <component :is="data.id === 'all' ? FolderOpened : Folder" />
                </el-icon>
                <span class="tree-name">{{ data.name }}</span>
              </div>
              <span v-if="data.id !== 'all'" class="tree-actions">
                <el-button link type="primary" :icon="Edit" @click.stop="openEditCategoryDialog(data)" />
                <el-button link type="danger" :icon="Delete" @click.stop="handleDeleteCategory(data)" />
              </span>
            </div>
          </template>
        </el-tree>
      </el-card>

      <div class="pane-divider" @mousedown.prevent="(event) => startDragging('left', event)" />

      <el-card class="pane-card" shadow="hover" :style="{ width: `${middlePaneWidth}%` }">
        <template #header>
          <div class="pane-header">
            <span>文件列表</span>
          </div>
        </template>

        <el-form :inline="true" class="search-form">
          <el-form-item>
            <el-input v-model="searchForm.filename" placeholder="文件名" clearable :prefix-icon="Search" />
          </el-form-item>
          <el-form-item class="form-actions">
            <el-button
              type="danger"
              plain
              :disabled="!selectedFiles.length"
              @click="handleBatchDeleteFiles"
            >批量删除</el-button>
            <el-button type="primary" @click="handleSearch">搜索</el-button>
            <el-button @click="handleReset">重置</el-button>
            <el-button @click="handleDownloadTemplate">下载模板</el-button>
            <el-button type="primary" @click="openUploadDialog">上传文件</el-button>
          </el-form-item>
        </el-form>

        <el-table :data="fileList" border stripe v-loading="loadingFiles" height="calc(100vh - 250px)" row-class-name="file-row" @row-click="handleViewFile" @selection-change="handleFileSelectionChange">
          <el-table-column type="selection" width="44" @click.stop />
          <el-table-column prop="filename" label="文件名" min-width="160" />
          <el-table-column prop="remark" label="备注" min-width="120" class-name="cell-wrap" />
          <el-table-column prop="uploaded_at" label="上传时间" min-width="170" />
          <el-table-column prop="data_count" label="数据量" width="90" />
          <el-table-column label="操作" width="110" fixed="right">
            <template #default="{ row }">
              <el-button link type="primary" :icon="Edit" @click.stop="openFileEditDialog(row)" />
              <el-button link type="success" :icon="DataAnalysis" @click.stop="handleQualityCheck(row)" title="质量判定" />
              <el-button link type="danger" :icon="Delete" @click.stop="handleDeleteFile(row)" />
            </template>
          </el-table-column>
        </el-table>
      </el-card>

      <div class="pane-divider" @mousedown.prevent="(event) => startDragging('middle', event)" />

      <el-card class="pane-card" shadow="hover" :style="{ width: `${rightPaneWidth}%` }">
        <template #header>
          <div class="pane-header">
            <span>
              文件内容
              <span v-if="currentFile" class="file-title">- {{ currentFile.filename }}</span>
              <span v-if="currentFile && currentFile.remark" class="file-remark">（{{ currentFile.remark }}）</span>
            </span>
            <el-radio-group v-model="contentMode" size="small">
              <el-radio-button value="table">表格</el-radio-button>
              <el-radio-button value="curve">曲线</el-radio-button>
            </el-radio-group>
          </div>
        </template>

        <div class="content-wrap" v-loading="contentLoading">
          <el-empty v-if="!currentFile" description="请选择并查看一个文件" />

          <el-auto-resizer v-else-if="contentMode === 'table'">
            <template #default="{ height, width }">
              <el-table-v2
                :columns="recordColumns"
                :data="recordList"
                :width="width"
                :height="height"
                :row-height="36"
                :header-height="40"
                fixed
              />
            </template>
          </el-auto-resizer>

          <DataCurve v-else :records="curveRecords" :total-count="recordList.length" />
        </div>
      </el-card>
    </div>

    <el-dialog v-model="uploadDialogVisible" title="上传数据文件" width="460px">
      <el-form label-position="top">
        <el-form-item label="选择分类" required>
          <el-select v-model="uploadForm.categoryId" placeholder="请选择要上传到的分类" style="width: 100%">
            <el-option
              v-for="item in flatCategoryOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item label="备注">
          <el-input
            v-model="uploadForm.remark"
            type="textarea"
            :rows="2"
            placeholder="可选，填写文件说明或标注"
            maxlength="500"
          />
        </el-form-item>

        <el-form-item label="上传文件" required>
          <el-upload
            class="upload-panel"
            drag
            :show-file-list="false"
            :http-request="handleUpload"
            :before-upload="beforeUploadExcel"
            accept=".xlsx,.xls,.csv"
          >
            <el-icon class="upload-panel__icon"><Upload /></el-icon>
            <div class="el-upload__text">拖拽文件到此处，或点击选择 Excel / CSV 文件</div>
            <template #tip>
              <div class="el-upload__tip">支持 .xlsx / .xls / .csv，上传前请先选择分类</div>
            </template>
          </el-upload>
        </el-form-item>
      </el-form>

      <template #footer>
        <el-button @click="uploadDialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="categoryDialogVisible" :title="categoryDialogTitle" width="420px">
      <el-form label-position="top">
        <el-form-item label="分类名称" required>
          <el-input v-model="categoryForm.name" maxlength="100" show-word-limit />
        </el-form-item>
      </el-form>

      <template #footer>
        <el-button @click="categoryDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitCategory">保存</el-button>
      </template>
    </el-dialog>

    <!-- 质量判定对话框 -->
    <el-dialog
      v-model="qualityDialogVisible"
      title="数据质量判定"
      width="500px"
      destroy-on-close
    >
      <div v-if="qualityFile" class="quality-filename">
        <el-icon><Document /></el-icon>
        {{ qualityFile.filename }}
      </div>

      <div v-loading="qualityLoading" class="quality-body">
        <template v-if="qualityResult && !qualityLoading">
          <div class="quality-total">共 {{ qualityResult.total }} 条数据行</div>

          <!-- 完整性 -->
          <div class="quality-item">
            <div class="quality-item__header">
              <span class="quality-item__label">完整性</span>
              <span class="quality-item__count">{{ qualityResult.complete_count }} / {{ qualityResult.total }} 条字段完整</span>
              <span class="quality-item__pct" :class="qualityColorClass(qualityResult.completeness)">
                {{ qualityResult.completeness }}%
              </span>
            </div>
            <el-progress
              :percentage="qualityResult.completeness"
              :color="qualityResult.completeness >= 90 ? '#67c23a' : qualityResult.completeness >= 70 ? '#e6a23c' : '#f56c6c'"
              :stroke-width="10"
              :show-text="false"
            />
            <div class="quality-item__desc">每条数据行的电流、电压、设定温度、实际温度均有值</div>
          </div>

          <!-- 连续性 -->
          <div class="quality-item">
            <div class="quality-item__header">
              <span class="quality-item__label">连续性</span>
              <span class="quality-item__count">{{ qualityResult.continuous_count }} / {{ qualityResult.total }} 条有效行</span>
              <span class="quality-item__pct" :class="qualityColorClass(qualityResult.continuity)">
                {{ qualityResult.continuity }}%
              </span>
            </div>
            <el-progress
              :percentage="qualityResult.continuity"
              :color="qualityResult.continuity >= 90 ? '#67c23a' : qualityResult.continuity >= 70 ? '#e6a23c' : '#f56c6c'"
              :stroke-width="10"
              :show-text="false"
            />
            <div class="quality-item__desc">非断点行（至少有一个测量值的行）占比，断点为所有测量字段均为空的行</div>
          </div>

          <!-- 准确性 -->
          <div class="quality-item">
            <div class="quality-item__header">
              <span class="quality-item__label">准确性</span>
              <span class="quality-item__count">{{ qualityResult.accurate_count }} / {{ qualityResult.has_measurement_count }} 条在范围内</span>
              <span class="quality-item__pct" :class="qualityColorClass(qualityResult.accuracy)">
                {{ qualityResult.accuracy }}%
              </span>
            </div>
            <el-progress
              :percentage="qualityResult.accuracy"
              :color="qualityResult.accuracy >= 90 ? '#67c23a' : qualityResult.accuracy >= 70 ? '#e6a23c' : '#f56c6c'"
              :stroke-width="10"
              :show-text="false"
            />
            <div class="quality-item__desc">
              所有非空测量值均在配置范围内的行占比
              <span v-if="qualityResult.quality_config && qualityResult.quality_config.length">
                （当前配置：
                <span v-for="(cfg, idx) in qualityResult.quality_config" :key="cfg.field_name">
                  <template v-if="cfg.min_value !== null || cfg.max_value !== null">
                    {{ cfg.label }}
                    <template v-if="cfg.min_value !== null">≥{{ cfg.min_value }}{{ cfg.unit }}</template>
                    <template v-if="cfg.max_value !== null">≤{{ cfg.max_value }}{{ cfg.unit }}</template>
                    <template v-if="idx < qualityResult.quality_config.length - 1">、</template>
                  </template>
                </span>）
              </span>
              <span v-else class="quality-no-config">（未配置准确性范围，请在 data_quality_config 表中设置）</span>
            </div>
          </div>
        </template>

        <el-empty v-else-if="!qualityLoading" description="暂无质量数据" :image-size="60" />
      </div>

      <template #footer>
        <el-button @click="qualityDialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>

    <!-- 编辑文件对话框 -->
    <el-dialog v-model="fileEditDialogVisible" title="编辑文件信息" width="460px" destroy-on-close>
      <el-form label-position="top">
        <el-form-item label="文件名" required>
          <el-input v-model="fileEditForm.filename" maxlength="255" show-word-limit clearable />
        </el-form-item>
        <el-form-item label="所属分类" required>
          <el-select v-model="fileEditForm.categoryId" placeholder="请选择分类" style="width: 100%">
            <el-option
              v-for="item in flatCategoryOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="备注">
          <el-input
            v-model="fileEditForm.remark"
            type="textarea"
            :rows="3"
            maxlength="500"
            show-word-limit
            placeholder="可选备注"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="fileEditDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="fileEditSubmitting" @click="submitFileEdit">保存</el-button>
      </template>
    </el-dialog>
  </section>
</template>

<style lang="scss" scoped>
.data-page {
  height: 100%;
  background: var(--bg-page);

  &.dragging {
    user-select: none;
    cursor: col-resize;
  }
}

.data-layout {
  height: 100%;
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--bg-white);
  border: 1px solid var(--border-color);
  box-shadow: var(--shadow-card);
}

.pane-divider {
  width: 4px;
  flex-shrink: 0;
  background: var(--border-color);
  cursor: col-resize;
  transition: background 0.15s;
  position: relative;

  &::after {
    content: '';
    position: absolute;
    inset: 0 -2px;
  }

  &:hover,
  .dragging & {
    background: var(--primary-border);
  }
}

.pane-card {
  height: 100%;
  min-width: 0;
  border-radius: 0;
  border: none;
  border-right: 1px solid var(--border-color);
  box-shadow: none !important;
  background: var(--bg-white);

  :deep(.el-card__header) {
    padding: 0 16px;
    height: 44px;
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--border-color);
    background: #F7F8FA;
  }

  :deep(.el-card__body) {
    height: calc(100% - 44px);
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 16px;
  }

  &--tree {
    :deep(.el-card__body) {
      padding: 8px 0;
      overflow-y: auto;
    }
  }
}

.pane-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}

.file-title {
  font-size: 13px;
  color: var(--text-secondary);
  font-weight: 400;
  margin-left: 4px;
}

.file-remark {
  font-size: 12px;
  color: var(--text-tertiary);
  font-weight: 400;
  margin-left: 2px;
}

.tree-node {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-right: 4px;
}

.tree-main {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.tree-icon {
  font-size: 14px;
  color: var(--primary);
  flex: 0 0 auto;
}

.tree-name {
  font-size: 13px;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tree-actions {
  display: none;
  flex-shrink: 0;

  .el-button {
    padding: 0 3px;
    font-size: 13px;
    color: var(--text-tertiary);

    &:hover {
      color: var(--primary);
    }
  }
}

.tree-node:hover .tree-actions {
  display: inline-flex;
}

.upload-panel {
  width: 100%;
}

.upload-panel__icon {
  font-size: 28px;
  color: var(--primary);
  margin-bottom: 8px;
}

.search-form {
  display: flex;
  margin-bottom: 4px;
  flex-shrink: 0;
}
.form-actions {
  margin-left: auto;
}
:deep(.el-table .cell-wrap .cell) {
  white-space: normal;
  word-break: break-word;
  line-height: 1.5;
}
.content-wrap {
  flex: 1;
  min-height: 0;
}



// quality dialog
.quality-filename {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: #475569;
  margin-bottom: 16px;
  word-break: break-all;
}

.quality-total {
  font-size: 13px;
  color: #64748b;
  margin-bottom: 12px;
}

.quality-body {
  min-height: 120px;
}

.quality-item {
  margin-bottom: 20px;

  &__header {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 6px;
  }

  &__label {
    font-size: 14px;
    font-weight: 600;
    color: #0f172a;
    min-width: 48px;
  }

  &__count {
    font-size: 12px;
    color: #64748b;
    flex: 1;
  }

  &__pct {
    font-size: 18px;
    font-weight: 700;
    min-width: 56px;
    text-align: right;

    &.quality-good { color: #67c23a; }
    &.quality-warn { color: #e6a23c; }
    &.quality-bad  { color: #f56c6c; }
  }

  &__desc {
    font-size: 12px;
    color: #94a3b8;
    margin-top: 4px;
    line-height: 1.5;
  }
}

.quality-no-config {
  color: #e6a23c;
}

:deep(.file-row) {
  cursor: pointer;
}
</style>
