<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { onBeforeRouteLeave, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import PageCard from '@/components/PageCard.vue'
import {
  activateDatasetFileVersion,
  createDatasetFileVersion,
  downloadFileUrl,
  getDataset,
  getDatasetFileVersionContent,
  getDatasetFileVersions,
} from '@/api/modules/dataset'
import { DATASET_TYPE_MAP, STORAGE_MAP } from '@/constants'
import { parseDatasetRecords, updateDatasetRecord } from './datasetRecords'
import type { DatasetRecord } from './datasetRecords'
import type { DatasetFile, DatasetItem, DatasetVersion } from '@/types'

interface EditField {
  key: string
  value: string
  isJson: boolean
}

const route = useRoute()
const datasetId = route.params.id as string

const loading = ref(false)
const previewLoading = ref(false)
const dataset = ref<DatasetItem | null>(null)
const selectedFileId = ref('')
const previewContent = ref('')
const baseVersionContent = ref('')
const previewError = ref('')
const searchText = ref('')
const currentPage = ref(1)
const pageSize = 10
const editorVisible = ref(false)
const editingRecord = ref<DatasetRecord | null>(null)
const editFields = ref<EditField[]>([])
const rawDraft = ref('')
const originalDraft = ref('')
const savingRecord = ref(false)
const savingVersion = ref(false)
const versions = ref<DatasetVersion[]>([])
const selectedVersionId = ref('')
const activeVersionId = ref('')
const loadedVersionId = ref('')
const versionLoading = ref(false)
const activatingVersion = ref(false)
let versionRequestId = 0

const files = computed<DatasetFile[]>(() => dataset.value?.files || [])
const selectedFile = computed(() => files.value.find((file) => fileKey(file) === selectedFileId.value))
const totalLines = computed(() => previewContent.value ? previewContent.value.split('\n').length : 0)
const recordResult = computed(() => parseDatasetRecords(previewContent.value, selectedFile.value?.name || ''))
const isRecordFile = computed(() => recordResult.value.supported)
const records = computed(() => recordResult.value.records)
const invalidRecordCount = computed(() => records.value.filter((record) => record.kind === 'invalid').length)
const allTableFieldKeys = computed(() => {
  const keys = new Set<string>()
  records.value.forEach((record) => {
    if (record.kind !== 'object') return
    Object.keys(record.value as Record<string, unknown>).forEach((key) => keys.add(key))
  })
  const preferred = ['instruction', 'question', 'prompt', 'input', 'context', 'output', 'answer', 'response']
  return [...keys].sort((a, b) => {
    const rank = (key: string) => {
      const index = preferred.indexOf(key)
      return index === -1 ? preferred.length : index
    }
    return rank(a) - rank(b)
  })
})
const tableFieldKeys = computed(() => allTableFieldKeys.value.slice(0, 6))
const hasExtraTableFields = computed(() => allTableFieldKeys.value.length > tableFieldKeys.value.length)
const viewedVersion = computed(() => versions.value.find((item) => item.id === loadedVersionId.value))
const isViewingActiveVersion = computed(() => Boolean(loadedVersionId.value) && loadedVersionId.value === activeVersionId.value)
const nextVersionNumber = computed(() => Math.max(0, ...versions.value.map((item) => item.version)) + 1)
const filteredRecords = computed(() => {
  const query = searchText.value.trim().toLowerCase()
  if (!query) return records.value
  return records.value.filter((record) => JSON.stringify(record.value).toLowerCase().includes(query))
})
const pagedRecords = computed(() => {
  const start = (currentPage.value - 1) * pageSize
  return filteredRecords.value.slice(start, start + pageSize)
})
const rawPreviewLines = computed(() => previewContent.value ? previewContent.value.split('\n').slice(0, 100) : [])
const currentDraft = computed(() => editingRecord.value?.kind === 'object'
  ? JSON.stringify(editFields.value)
  : rawDraft.value)
const hasEditorDraftChanges = computed(() => editorVisible.value && currentDraft.value !== originalDraft.value)
const hasPendingVersionChanges = computed(() => previewContent.value !== baseVersionContent.value)
const hasUnsavedChanges = computed(() => hasEditorDraftChanges.value || hasPendingVersionChanges.value)
const datasetType = computed(() => {
  const value = String(dataset.value?.type || '').toLowerCase()
  return DATASET_TYPE_MAP[value] || dataset.value?.type || '-'
})
const storageType = computed(() => {
  const value = dataset.value?.storage_type || ''
  return STORAGE_MAP[value] || value || '-'
})
const createdAt = computed(() => dataset.value?.create_time
  ? new Date(dataset.value.create_time).toLocaleString('zh-CN')
  : '-')

function formatVersionTime(value: string) {
  return new Date(value).toLocaleString('zh-CN', { hour12: false })
}

function versionOptionLabel(version: DatasetVersion) {
  const status = version.id === activeVersionId.value ? '当前版本' : '历史版本'
  return `V${version.version} · ${status} · ${formatVersionTime(version.create_time)}`
}

function fileKey(file: DatasetFile) {
  return String(file.id || file.name)
}

function formatFieldValue(value: unknown) {
  if (typeof value === 'string') return value || '（空）'
  return JSON.stringify(value)
}

function asDatasetRecord(row: unknown) {
  return row as DatasetRecord
}

function recordFieldValue(record: DatasetRecord, key: string) {
  if (record.kind !== 'object') return record.kind === 'invalid' ? record.raw : formatFieldValue(record.value)
  const data = record.value as Record<string, unknown>
  return Object.prototype.hasOwnProperty.call(data, key) ? formatFieldValue(data[key]) : '-'
}

function extraFieldValue(record: DatasetRecord) {
  if (record.kind !== 'object') return '-'
  const data = record.value as Record<string, unknown>
  return allTableFieldKeys.value
    .filter((key) => !tableFieldKeys.value.includes(key) && Object.prototype.hasOwnProperty.call(data, key))
    .map((key) => `${key}: ${formatFieldValue(data[key])}`)
    .join('；') || '-'
}

function recordRowClassName({ row }: { row: unknown }) {
  return asDatasetRecord(row).kind === 'invalid' ? 'record-table-row-invalid' : ''
}

async function loadDataset() {
  loading.value = true
  try {
    dataset.value = await getDataset(datasetId)
    const firstFile = files.value[0]
    if (firstFile) {
      selectedFileId.value = fileKey(firstFile)
      await loadVersions(firstFile)
    }
  } finally {
    loading.value = false
  }
}

async function loadVersions(file: DatasetFile) {
  previewLoading.value = true
  previewError.value = ''
  const requestId = ++versionRequestId
  try {
    const result = await getDatasetFileVersions(fileKey(file))
    const activeContent = await getDatasetFileVersionContent(fileKey(file), result.active_version_id)
    if (requestId !== versionRequestId) return
    versions.value = result.versions
    activeVersionId.value = result.active_version_id
    selectedVersionId.value = result.active_version_id
    loadedVersionId.value = result.active_version_id
    previewContent.value = activeContent.content
    baseVersionContent.value = activeContent.content
  } catch {
    if (requestId === versionRequestId) previewError.value = '版本内容加载失败，请稍后重试'
  } finally {
    if (requestId === versionRequestId) previewLoading.value = false
  }
}

async function handleViewedVersionChange(nextVersionId: string) {
  if (!selectedFile.value || nextVersionId === loadedVersionId.value) return
  const previousVersionId = loadedVersionId.value
  if (!await confirmDiscardChanges('切换版本后，当前样本的未保存修改将丢失。')) {
    selectedVersionId.value = previousVersionId
    return
  }

  versionLoading.value = true
  const requestId = ++versionRequestId
  try {
    const result = await getDatasetFileVersionContent(fileKey(selectedFile.value), nextVersionId)
    if (requestId !== versionRequestId) return
    previewContent.value = result.content
    baseVersionContent.value = result.content
    loadedVersionId.value = result.version.id
    currentPage.value = 1
    searchText.value = ''
  } catch {
    if (requestId === versionRequestId) selectedVersionId.value = previousVersionId
  } finally {
    if (requestId === versionRequestId) versionLoading.value = false
  }
}

async function activateViewedVersion() {
  if (!selectedFile.value || !viewedVersion.value || isViewingActiveVersion.value) return
  try {
    await ElMessageBox.confirm(
      `确定将 V${viewedVersion.value.version} 设为当前版本吗？后续任务将使用该版本。`,
      '切换当前版本',
      {
        type: 'warning',
        confirmButtonText: '设为当前版本',
        cancelButtonText: '取消',
      },
    )
  } catch {
    return
  }

  activatingVersion.value = true
  try {
    const result = await activateDatasetFileVersion(
      fileKey(selectedFile.value),
      viewedVersion.value.id,
      activeVersionId.value,
    )
    activeVersionId.value = result.version.id
    previewContent.value = result.content
    baseVersionContent.value = result.content
    ElMessage.success(`已将 V${result.version.version} 设为当前版本`)
  } finally {
    activatingVersion.value = false
  }
}

function openRecordEditor(record: DatasetRecord) {
  if (!isViewingActiveVersion.value) {
    ElMessage.warning('历史版本为只读，请先设为当前版本')
    return
  }
  editingRecord.value = record
  if (record.kind === 'object') {
    editFields.value = Object.entries(record.value as Record<string, unknown>).map(([key, value]) => ({
      key,
      value: typeof value === 'string' ? value : JSON.stringify(value, null, 2),
      isJson: typeof value !== 'string',
    }))
    rawDraft.value = ''
  } else {
    editFields.value = []
    rawDraft.value = record.raw
  }
  editorVisible.value = true
  originalDraft.value = record.kind === 'object' ? JSON.stringify(editFields.value) : rawDraft.value
}

async function confirmDiscardChanges(message = '当前存在尚未保存为版本的修改，放弃后将无法恢复。') {
  if (!hasUnsavedChanges.value) return true
  try {
    await ElMessageBox.confirm(message, '确认放弃修改？', {
      type: 'warning',
      confirmButtonText: '放弃修改',
      cancelButtonText: '继续编辑',
    })
    return true
  } catch {
    return false
  }
}

async function confirmEditorDiscardChanges() {
  if (!hasEditorDraftChanges.value) return true
  try {
    await ElMessageBox.confirm('当前样本存在未暂存修改，放弃后将无法恢复。', '确认放弃修改？', {
      type: 'warning',
      confirmButtonText: '放弃修改',
      cancelButtonText: '继续编辑',
    })
    return true
  } catch {
    return false
  }
}

async function closeRecordEditor() {
  if (savingRecord.value) return
  if (!await confirmEditorDiscardChanges()) return
  editorVisible.value = false
  editingRecord.value = null
}

async function handleDialogBeforeClose(done: () => void) {
  if (savingRecord.value) return
  if (!await confirmEditorDiscardChanges()) return
  editingRecord.value = null
  done()
}

function buildNextRecordValue() {
  if (editingRecord.value?.kind !== 'object') return JSON.parse(rawDraft.value)
  const nextValue: Record<string, unknown> = {}
  for (const field of editFields.value) {
    nextValue[field.key] = field.isJson ? JSON.parse(field.value) : field.value
  }
  return nextValue
}

async function saveRecord() {
  if (!selectedFile.value || !editingRecord.value || savingRecord.value) return
  const record = editingRecord.value
  let nextValue: unknown
  try {
    nextValue = buildNextRecordValue()
  } catch (error) {
    ElMessage.error(`JSON 格式校验失败：${error instanceof Error ? error.message : '内容格式不正确'}`)
    return
  }

  savingRecord.value = true
  try {
    const nextContent = updateDatasetRecord(
      previewContent.value,
      selectedFile.value.name,
      record.sourceIndex,
      nextValue,
    )
    previewContent.value = nextContent
    originalDraft.value = currentDraft.value
    editorVisible.value = false
    editingRecord.value = null
    ElMessage.success(`第 ${record.displayIndex} 条修改已暂存`)
  } finally {
    savingRecord.value = false
  }
}

async function saveVersion() {
  if (
    !selectedFile.value
    || !hasPendingVersionChanges.value
    || !isViewingActiveVersion.value
    || savingVersion.value
  ) return

  savingVersion.value = true
  try {
    const result = await createDatasetFileVersion(
      fileKey(selectedFile.value),
      previewContent.value,
      {
        description: '在线编辑数据样本',
        baseVersionId: loadedVersionId.value,
        expectedCurrentVersionId: activeVersionId.value,
      },
    )
    previewContent.value = result.content
    baseVersionContent.value = result.content
    versions.value = [result.version, ...versions.value]
    activeVersionId.value = result.version.id
    selectedVersionId.value = result.version.id
    loadedVersionId.value = result.version.id
    ElMessage.success(`V${result.version.version} 创建成功，已设为当前版本`)
  } finally {
    savingVersion.value = false
  }
}

function handleEditorKeydown(event: KeyboardEvent) {
  if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
    event.preventDefault()
    saveRecord()
  }
}

function handleBeforeUnload(event: BeforeUnloadEvent) {
  if (!hasUnsavedChanges.value) return
  event.preventDefault()
  event.returnValue = ''
}

function handleDownloadFile(file: DatasetFile) {
  window.open(downloadFileUrl(datasetId, fileKey(file), loadedVersionId.value), '_blank')
}

onBeforeRouteLeave(() => confirmDiscardChanges())
onMounted(() => {
  window.addEventListener('beforeunload', handleBeforeUnload)
  loadDataset()
})
onBeforeUnmount(() => window.removeEventListener('beforeunload', handleBeforeUnload))
</script>

<template>
  <PageCard class="dataset-preview-page" v-loading="loading">
    <template #header>
      <div class="page-header">
        <div class="page-heading">
          <div class="heading-icon" aria-hidden="true"><i class="fa fa-database" /></div>
          <div>
            <div class="heading-row">
              <h1>{{ dataset?.name || '数据集详情' }}</h1>
              <el-tag size="small" type="primary">{{ datasetType }}</el-tag>
              <el-tag size="small" type="success">{{ storageType }}</el-tag>
            </div>
            <p>{{ dataset?.description || '逐条查看和修改数据样本' }}</p>
          </div>
        </div>
      </div>
    </template>

    <section class="dataset-overview" aria-label="数据集概览">
      <div class="overview-item"><span>数据条数</span><strong>{{ (dataset?.count || 0).toLocaleString() }}</strong></div>
      <div class="overview-item"><span>数据大小</span><strong>{{ dataset?.size || '-' }}</strong></div>
      <div class="overview-item overview-item-wide"><span>创建时间</span><strong>{{ createdAt }}</strong></div>
    </section>

    <section class="version-control-bar" aria-label="数据集版本控制">
      <div class="version-selector-group">
        <label for="dataset-version-select">数据版本</label>
        <el-select
          id="dataset-version-select"
          v-model="selectedVersionId"
          class="version-select"
          :loading="versionLoading"
          :disabled="versionLoading || savingRecord"
          aria-label="选择要查看的数据集版本"
          @change="handleViewedVersionChange"
        >
          <el-option
            v-for="version in versions"
            :key="version.id"
            :label="versionOptionLabel(version)"
            :value="version.id"
          />
        </el-select>
      </div>

      <div class="version-status" role="status" aria-live="polite">
        <template v-if="viewedVersion">
          <el-tag v-if="isViewingActiveVersion" type="success">V{{ viewedVersion.version }} · 当前版本</el-tag>
          <el-tag v-else type="info">V{{ viewedVersion.version }} · 历史版本（只读）</el-tag>
          <el-tag v-if="hasPendingVersionChanges" type="warning">有待保存修改</el-tag>
          <span>{{ viewedVersion.description || '无版本说明' }} · {{ formatVersionTime(viewedVersion.create_time) }}</span>
        </template>
      </div>

      <el-button
        v-if="viewedVersion && !isViewingActiveVersion"
        type="primary"
        :loading="activatingVersion"
        @click="activateViewedVersion"
      >
        <i class="fa fa-check-circle" />
        设为当前版本
      </el-button>
    </section>

    <el-alert
      v-if="viewedVersion && !isViewingActiveVersion"
      class="historical-version-alert"
      type="info"
      :closable="false"
      show-icon
      title="你正在查看历史版本，数据默认只读；设为当前版本后才可编辑。"
    />

    <section v-if="files.length" class="preview-workspace" aria-label="数据集文件详情">
      <div class="content-pane">
        <div class="content-toolbar">
          <div class="content-title">
            <span class="file-icon" aria-hidden="true"><i class="fa fa-file-text-o" /></span>
            <div>
              <h2>{{ selectedFile?.name }}</h2>
              <p>
                {{ selectedFile?.size || '未知大小' }} <span aria-hidden="true">·</span>
                <template v-if="isRecordFile">{{ records.length.toLocaleString() }} 条记录</template>
                <template v-else>{{ totalLines.toLocaleString() }} 行</template>
              </p>
            </div>
          </div>
          <div class="toolbar-actions">
            <el-button
              v-if="hasPendingVersionChanges && isViewingActiveVersion"
              type="primary"
              :loading="savingVersion"
              @click="saveVersion"
            >
              <i class="fa fa-save" />保存版本
            </el-button>
            <el-tooltip content="下载当前文件" placement="top">
              <el-button class="download-button" aria-label="下载当前文件" :disabled="!selectedFile" @click="selectedFile && handleDownloadFile(selectedFile)">
                <i class="fa fa-download" />下载文件
              </el-button>
            </el-tooltip>
          </div>
        </div>

        <div class="viewer-shell" v-loading="versionLoading" :aria-busy="previewLoading || versionLoading">
          <div v-if="previewLoading" class="viewer-state"><i class="fa fa-circle-o-notch fa-spin" aria-hidden="true" /><span>正在加载文件内容…</span></div>
          <div v-else-if="previewError" class="viewer-state viewer-state-error" role="alert">
            <i class="fa fa-exclamation-circle" aria-hidden="true" /><strong>{{ previewError }}</strong>
            <el-button size="small" @click="selectedFile && loadVersions(selectedFile)">重新加载</el-button>
          </div>
          <div v-else-if="!previewContent" class="viewer-state"><i class="fa fa-file-o" aria-hidden="true" /><span>当前文件暂无可预览内容</span></div>

          <div v-else-if="isRecordFile" class="records-viewer">
            <div class="records-toolbar">
              <div><strong>样本数据</strong><span>共 {{ records.length.toLocaleString() }} 条</span><el-tag v-if="invalidRecordCount" size="small" type="danger">{{ invalidRecordCount }} 条格式错误</el-tag></div>
              <el-input v-model="searchText" class="record-search" clearable placeholder="搜索样本内容" aria-label="搜索样本内容" @input="currentPage = 1">
                <template #prefix><i class="fa fa-search" /></template>
              </el-input>
            </div>

            <div v-if="!filteredRecords.length" class="viewer-state record-empty-state"><i class="fa fa-search" aria-hidden="true" /><span>没有找到匹配的样本</span></div>
            <div v-else class="record-table-shell" aria-label="数据样本表格">
              <el-table
                :data="pagedRecords"
                row-key="sourceIndex"
                stripe
                border
                class="record-table"
                :row-class-name="recordRowClassName"
              >
                <el-table-column label="序号" width="76" align="center">
                  <template #default="{ row }">
                    <div class="table-index">
                      <strong>{{ asDatasetRecord(row).displayIndex }}</strong>
                      <small>第 {{ asDatasetRecord(row).sourceIndex + 1 }} 行</small>
                    </div>
                  </template>
                </el-table-column>
                <el-table-column v-for="fieldKey in tableFieldKeys" :key="fieldKey" :label="fieldKey" min-width="220">
                  <template #default="{ row }">
                    <el-tooltip :content="recordFieldValue(asDatasetRecord(row), fieldKey)" placement="top" :show-after="400">
                      <div class="table-cell-text">{{ recordFieldValue(asDatasetRecord(row), fieldKey) }}</div>
                    </el-tooltip>
                  </template>
                </el-table-column>
                <el-table-column v-if="hasExtraTableFields" label="其他字段" min-width="240">
                  <template #default="{ row }">
                    <el-tooltip :content="extraFieldValue(asDatasetRecord(row))" placement="top" :show-after="400">
                      <div class="table-cell-text">{{ extraFieldValue(asDatasetRecord(row)) }}</div>
                    </el-tooltip>
                  </template>
                </el-table-column>
                <el-table-column label="状态" width="96" align="center">
                  <template #default="{ row }">
                    <el-tag v-if="asDatasetRecord(row).kind === 'invalid'" size="small" type="danger">格式错误</el-tag>
                    <el-tag v-else-if="asDatasetRecord(row).kind === 'primitive'" size="small" type="warning">非对象</el-tag>
                    <el-tag v-else size="small" type="success">正常</el-tag>
                  </template>
                </el-table-column>
                <el-table-column label="操作" width="96" align="center" fixed="right">
                  <template #default="{ row }">
                    <el-button
                      type="primary"
                      link
                      :disabled="!isViewingActiveVersion"
                      :title="isViewingActiveVersion ? '' : '历史版本为只读，请先设为当前版本'"
                      :aria-label="`${asDatasetRecord(row).kind === 'invalid' ? '修复' : '编辑'}第 ${asDatasetRecord(row).displayIndex} 条数据`"
                      @click="openRecordEditor(asDatasetRecord(row))"
                    >
                      <i :class="asDatasetRecord(row).kind === 'invalid' ? 'fa fa-wrench' : 'fa fa-pencil'" />
                      {{ asDatasetRecord(row).kind === 'invalid' ? '修复' : '编辑' }}
                    </el-button>
                  </template>
                </el-table-column>
              </el-table>
            </div>
            <div v-if="filteredRecords.length > pageSize" class="records-pagination">
              <el-pagination v-model:current-page="currentPage" :page-size="pageSize" :total="filteredRecords.length" layout="prev, pager, next" background />
            </div>
          </div>

          <div v-else class="code-viewer" tabindex="0" :aria-label="`${selectedFile?.name || ''} 文件内容`">
            <div v-for="(line, index) in rawPreviewLines" :key="index" class="code-line"><span class="line-number" aria-hidden="true">{{ index + 1 }}</span><code>{{ line || ' ' }}</code></div>
          </div>
        </div>

        <div class="viewer-footer"><span v-if="isRecordFile"><i class="fa fa-list-alt" aria-hidden="true" /> 逐条查看与编辑</span><span v-else><i class="fa fa-eye" aria-hidden="true" /> 文本预览</span><span>UTF-8</span></div>
      </div>
    </section>

    <section v-else-if="!loading" class="empty-files" aria-label="空文件列表">
      <span class="empty-icon" aria-hidden="true"><i class="fa fa-folder-open-o" /></span><h2>暂无数据文件</h2><p>该数据集还没有可供预览的文件。</p>
    </section>

    <el-dialog v-model="editorVisible" class="record-editor-dialog" width="min(720px, 92vw)" :title="editingRecord ? `编辑第 ${editingRecord.displayIndex} 条数据` : '编辑数据'" :close-on-click-modal="false" :close-on-press-escape="!savingRecord" :show-close="!savingRecord" :before-close="handleDialogBeforeClose" destroy-on-close>
      <div v-if="editingRecord" class="record-editor" @keydown="handleEditorKeydown">
        <div class="editor-hint">
          <i class="fa fa-info-circle" aria-hidden="true" />
          当前修改会先暂存；完成多条修改后，点击文件工具栏中的“保存版本”创建 V{{ nextVersionNumber }}。
        </div>
        <el-form v-if="editingRecord.kind === 'object'" label-position="top">
          <el-form-item v-for="field in editFields" :key="field.key" :label="field.key">
            <el-input v-model="field.value" type="textarea" :rows="field.isJson ? 5 : 3" :aria-label="`编辑字段 ${field.key}`" :placeholder="field.isJson ? '请输入合法 JSON' : `请输入 ${field.key}`" />
            <small v-if="field.isJson" class="field-helper">该字段为对象、数组或数值，请保持合法 JSON 格式。</small>
          </el-form-item>
        </el-form>
        <div v-else class="raw-record-editor">
          <label for="raw-record-draft">{{ editingRecord.kind === 'invalid' ? '修复 JSON 内容' : 'JSON 内容' }}</label>
          <el-input id="raw-record-draft" v-model="rawDraft" type="textarea" :rows="10" aria-label="编辑当前条 JSON 内容" placeholder="请输入合法 JSON" />
        </div>
      </div>
      <template #footer>
        <div class="record-editor-actions">
          <span>{{ hasEditorDraftChanges ? '有未暂存修改' : '尚未修改' }} · Ctrl/⌘ + S 暂存</span>
          <div><el-button :disabled="savingRecord" @click="closeRecordEditor">取消</el-button><el-button type="primary" :loading="savingRecord" :disabled="!hasEditorDraftChanges" @click="saveRecord">暂存修改</el-button></div>
        </div>
      </template>
    </el-dialog>
  </PageCard>
</template>

<style scoped lang="scss">
.dataset-preview-page {
  min-width: 0;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  width: 100%;
  min-width: 0;
}

.page-heading {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.heading-icon {
  display: grid;
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  place-items: center;
  border-radius: 12px;
  background: linear-gradient(145deg, #eef2ff, #e0e7ff);
  color: var(--primary-color);
  font-size: 18px;
}

.heading-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.heading-row h1 {
  margin: 0 4px 0 0;
  color: #172033;
  font-size: 20px;
  font-weight: 650;
  line-height: 1.35;
}

.page-heading p {
  margin: 4px 0 0;
  color: #7c8799;
  font-size: 13px;
  line-height: 1.5;
}

.dataset-overview {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: 20px;
  padding: 16px 20px;
  border: 1px solid #edf0f5;
  border-radius: 12px;
  background: #fbfcfe;
}

.overview-item {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
  padding: 0 20px;
  border-right: 1px solid #e9edf4;
}

.overview-item:first-child {
  padding-left: 0;
}

.overview-item:last-child {
  padding-right: 0;
  border-right: 0;
}

.overview-item span {
  color: #8a94a6;
  font-size: 12px;
}

.overview-item strong {
  overflow: hidden;
  color: #283347;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.version-control-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 64px;
  margin-bottom: 12px;
  padding: 10px 14px;
  box-sizing: border-box;
  border: 1px solid #dfe5ee;
  border-radius: 10px;
  background: #fff;
}

.version-selector-group {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}

.version-selector-group label {
  color: #344054;
  font-size: 13px;
  font-weight: 600;
}

.version-select {
  width: 300px;
}

.version-status {
  display: flex;
  align-items: center;
  flex: 1;
  gap: 10px;
  min-width: 0;
}

.version-status > span {
  overflow: hidden;
  color: #7c8799;
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.version-control-bar > .el-button {
  min-height: 40px;
  flex: 0 0 auto;
}

.version-control-bar > .el-button i {
  margin-right: 6px;
}

.historical-version-alert {
  margin-bottom: 12px;
}

.preview-workspace {
  min-height: 520px;
  overflow: hidden;
  border: 1px solid #e5eaf2;
  border-radius: 12px;
  background: #fff;
}

.content-title h2 {
  margin: 0;
  color: #263146;
  font-size: 14px;
  font-weight: 600;
}

.content-pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: #fff;
}

.content-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 68px;
  padding: 10px 16px;
  border-bottom: 1px solid #e5eaf2;
}

.content-title {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.content-title > div {
  min-width: 0;
}

.content-title h2 {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.content-title p {
  display: flex;
  gap: 6px;
  margin: 3px 0 0;
  color: #8a94a6;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.file-icon {
  display: grid;
  flex: 0 0 34px;
  height: 34px;
  place-items: center;
  border-radius: 8px;
  background: #f1f5f9;
  color: #69768b;
}

.download-button {
  min-height: 36px;
}

.download-button i {
  margin-right: 6px;
}

.toolbar-actions {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
}

.toolbar-actions i {
  margin-right: 6px;
}

.viewer-shell {
  position: relative;
  flex: 1;
  min-height: 400px;
  overflow: hidden;
  background: #fbfcfe;
}

.records-viewer {
  min-height: 468px;
  padding: 16px;
  box-sizing: border-box;
}

.records-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 14px;
}

.records-toolbar > div {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.records-toolbar strong {
  color: #253047;
  font-size: 14px;
}

.records-toolbar span {
  color: #8a94a6;
  font-size: 12px;
}

.record-search {
  width: 240px;
}

.record-table-shell {
  overflow: hidden;
  border: 1px solid #e4e9f1;
  border-radius: 8px;
}

.record-table {
  width: 100%;
}

.record-table :deep(.el-table__header th) {
  height: 44px;
  background: #f5f7fa;
  color: #475467;
  font-size: 12px;
  font-weight: 600;
}

.record-table :deep(.el-table__cell) {
  padding: 10px 0;
}

.record-table :deep(.record-table-row-invalid td) {
  background: #fff7f7 !important;
}

.table-index {
  display: flex;
  align-items: center;
  flex-direction: column;
  gap: 1px;
  color: #667085;
  font-variant-numeric: tabular-nums;
}

.table-index strong {
  font-size: 13px;
  font-weight: 600;
}

.table-index small {
  color: #98a2b3;
  font-size: 10px;
}

.table-cell-text {
  display: -webkit-box;
  overflow: hidden;
  color: #526076;
  font-size: 12px;
  line-height: 1.6;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.record-table .el-button i {
  margin-right: 5px;
}

.records-pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 16px;
}

.record-empty-state {
  min-height: 320px;
}

.code-viewer {
  height: 100%;
  max-height: 468px;
  overflow: auto;
  padding: 12px 0 18px;
  outline: none;
}

.code-viewer:focus-visible {
  box-shadow: inset 0 0 0 2px var(--el-color-primary-light-5);
}

.code-line {
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr);
  min-height: 25px;
  color: #344054;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
  font-size: 12px;
  line-height: 1.65;
}

.code-line:hover {
  background: #f4f6fa;
}

.line-number {
  padding-right: 14px;
  border-right: 1px solid #edf0f5;
  color: #a4adbc;
  font-variant-numeric: tabular-nums;
  text-align: right;
  user-select: none;
}

.code-line code {
  display: block;
  min-width: max-content;
  padding: 0 18px;
  white-space: pre;
}

.viewer-state {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 10px;
  height: 100%;
  min-height: 400px;
  color: #8b96a8;
  font-size: 13px;
}

.viewer-state > i {
  color: #aeb7c5;
  font-size: 24px;
}

.viewer-state-error > i,
.viewer-state-error strong {
  color: #d14343;
}

.viewer-state-error strong {
  font-weight: 500;
}

.viewer-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 34px;
  padding: 6px 14px;
  border-top: 1px solid #e5eaf2;
  background: #f8fafc;
  color: #8a94a6;
  font-size: 11px;
}

.viewer-footer i {
  margin-right: 5px;
}

.editor-hint {
  margin-bottom: 18px;
  padding: 10px 12px;
  border: 1px solid #dbe2ff;
  border-radius: 8px;
  background: #f5f7ff;
  color: #56627a;
  font-size: 13px;
}

.editor-hint i {
  margin-right: 6px;
  color: var(--primary-color);
}

.field-helper {
  margin-top: 5px;
  color: #8a94a6;
  line-height: 1.5;
}

.raw-record-editor label {
  display: block;
  margin-bottom: 8px;
  color: #344054;
  font-size: 13px;
  font-weight: 600;
}

.record-editor-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.record-editor-actions > span {
  color: #8a94a6;
  font-size: 12px;
}

.empty-files {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  min-height: 420px;
  border: 1px dashed #dce2eb;
  border-radius: 12px;
  background: #fbfcfe;
  text-align: center;
}

.empty-icon {
  display: grid;
  width: 54px;
  height: 54px;
  margin-bottom: 14px;
  place-items: center;
  border-radius: 14px;
  background: #eef2f7;
  color: #8a96a8;
  font-size: 22px;
}

.empty-files h2 {
  margin: 0;
  color: #354056;
  font-size: 15px;
}

.empty-files p {
  margin: 6px 0 0;
  color: #929daf;
  font-size: 13px;
}

@media (max-width: 900px) {
  .dataset-overview {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 680px) {
  .page-header {
    align-items: flex-start;
  }

  .dataset-overview {
    grid-template-columns: 1fr;
  }

  .version-control-bar,
  .version-selector-group,
  .version-status {
    align-items: stretch;
    flex-direction: column;
  }

  .version-select,
  .version-control-bar > .el-button {
    width: 100%;
  }

  .overview-item {
    padding: 0 0 12px;
    border-right: 0;
    border-bottom: 1px solid #e9edf4;
  }

  .overview-item:last-child {
    padding-bottom: 0;
    border-bottom: 0;
  }

  .content-toolbar {
    align-items: flex-start;
  }

  .records-toolbar,
  .record-editor-actions {
    align-items: stretch;
    flex-direction: column;
  }

  .record-search {
    width: 100%;
  }

  .record-editor-actions > div {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }

  .download-button {
    width: 44px;
    padding: 8px;
    font-size: 0;
  }

  .download-button i {
    margin: 0;
    font-size: 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .record-table :deep(.el-table__row) { transition: none; }
}
</style>
