<template>
  <div class="upload-data-container">
    <!-- 主内容区 -->
    <div class="main-content">
      <div v-if="isWriteLocked" class="tagging-lock-tip">
        {{ writeLockedMessage }}
      </div>

      <!-- 文件上传记录列表 -->
      <div class="file-list-section">
        <div class="list-toolbar">
          <el-button icon="el-icon-refresh" @click="handleManualRefresh">刷新</el-button>
          <div class="toolbar-actions">
            <el-button
              size="small"
              icon="el-icon-upload2"
              :disabled="isWriteLocked"
              @click="handleOpenBatchUploadDialog"
            >
              上传流水
            </el-button>
            <el-button
              size="small"
              icon="el-icon-download"
              :disabled="isWriteLocked"
              @click="handleFetchBankInfo"
            >
              拉取流水
            </el-button>
            <el-button
              size="small"
              icon="el-icon-upload2"
              :disabled="isWriteLocked"
              @click="handleGoCreditInfoPage"
            >
              征信导入
            </el-button>
            <el-button
              size="small"
              type="primary"
              icon="el-icon-view"
              :disabled="isReportDisabled"
              @click="handleViewReport"
            >
              查看报告
            </el-button>
          </div>
        </div>

        <el-table :data="fileUploadList" v-loading="listLoading">
          <el-table-column prop="fileName" label="文件名" min-width="200"></el-table-column>
          <el-table-column prop="fileSize" label="文件大小" width="120">
            <template slot-scope="scope">
              {{ formatFileSize(scope.row.fileSize) }}
            </template>
          </el-table-column>
          <el-table-column prop="fileStatus" label="状态" width="120">
            <template slot-scope="scope">
              <el-tag :type="getStatusType(scope.row.fileStatus)" size="small">
                {{ getStatusText(scope.row.fileStatus) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="enterpriseNames" label="主体名称" min-width="150">
            <template slot-scope="scope">
              {{ scope.row.enterpriseNames || '-' }}
            </template>
          </el-table-column>
          <el-table-column prop="accountNos" label="主体账号" min-width="180">
            <template slot-scope="scope">
              {{ scope.row.accountNos || '-' }}
            </template>
          </el-table-column>
          <el-table-column prop="sourceProjectName" label="来源" min-width="180">
            <template slot-scope="scope">
              <span v-if="scope.row.sourceType === 'HISTORY_IMPORT'">
                历史导入 · {{ scope.row.sourceProjectName || '-' }}
              </span>
              <span v-else>-</span>
            </template>
          </el-table-column>
          <el-table-column prop="uploadTime" label="上传时间" width="180">
            <template slot-scope="scope">
              {{ formatUploadTime(scope.row.uploadTime) }}
            </template>
          </el-table-column>
          <el-table-column prop="uploadUser" label="上传人" width="100"></el-table-column>
          <el-table-column label="操作" width="160" fixed="right">
            <template slot-scope="scope">
              <el-button
                v-if="getRowAction(scope.row)"
                type="text"
                @click="handleRowAction(scope.row)"
              >
                {{ getRowAction(scope.row).text }}
              </el-button>
            </template>
          </el-table-column>
        </el-table>

        <el-pagination
          @current-change="handlePageChange"
          :current-page="queryParams.pageNum"
          :page-size="queryParams.pageSize"
          :total="total"
          layout="total, prev, pager, next, jumper"
          style="margin-top: 16px; text-align: right"
        ></el-pagination>
      </div>
    </div>

    <el-dialog
      title="拉取行内/金综流水"
      :visible.sync="pullBankInfoDialogVisible"
      :close-on-click-modal="false"
      width="640px"
    >
      <el-form
        class="pull-bank-info-form"
        :model="pullBankInfoForm"
        label-width="100px"
      >
        <el-form-item label="流水类型">
          <el-radio-group
            v-model="pullBankInfoForm.dataChannelCode"
            :disabled="isWriteLocked"
            class="pull-bank-channel-group"
          >
            <el-radio label="ZJRCU">
              <span class="pull-bank-channel-title">拉取行内流水</span>
              <span class="pull-bank-channel-tip">备注:仅支持获取近一年流水</span>
            </el-radio>
            <el-radio label="JZL">
              <span class="pull-bank-channel-title">拉取金综流水</span>
              <span class="pull-bank-channel-tip">
                备注:自动获取最近30天内授权的流水文件
              </span>
            </el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="证件号码">
          <el-input
            v-model="pullBankInfoForm.idCardText"
            type="textarea"
            :rows="5"
            :disabled="isWriteLocked"
            placeholder="仅支持英文逗号分隔"
          />
          <div class="pull-bank-field-tip">
            仅支持英文逗号分隔，文件解析结果会自动合并并去重
          </div>
        </el-form-item>
        <el-form-item label="身份证文件">
          <div class="pull-bank-file-panel">
            <div class="pull-bank-file-actions">
              <el-upload
                class="pull-bank-file-upload"
                action="#"
                :auto-upload="false"
                :limit="1"
                :show-file-list="false"
                :file-list="idCardFileList"
                accept=".xls,.xlsx"
                :disabled="isWriteLocked"
                :on-change="handleIdCardFileChange"
                :on-remove="handleIdCardFileRemove"
              >
                <el-button slot="trigger" size="small" type="primary" plain>
                  选择文件
                </el-button>
              </el-upload>
              <div class="pull-bank-file-tip">
                支持 .xls、.xlsx 文件，解析后自动补充证件号码
              </div>
            </div>
            <div v-if="idCardFileList.length > 0" class="selected-id-card-file">
              <div class="selected-id-card-file__info">
                <i class="el-icon-document"></i>
                <span class="selected-id-card-file__name">
                  {{ idCardFileList[0].name }}
                </span>
              </div>
              <el-button type="text" @click="handleIdCardFileRemove">
                移除
              </el-button>
            </div>
            <div v-if="parsingIdCardFile" class="parse-tip">
              正在解析身份证文件...
            </div>
          </div>
        </el-form-item>
        <el-form-item v-if="isZjrcuPullBankInfo" label="时间跨度">
          <el-date-picker
            class="pull-bank-range-picker"
            v-model="pullBankInfoForm.dateRange"
            :disabled="isWriteLocked"
            type="daterange"
            value-format="yyyy-MM-dd"
            :picker-options="pullBankInfoDatePickerOptions"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          />
        </el-form-item>
      </el-form>
      <span slot="footer">
        <el-button @click="pullBankInfoDialogVisible = false">取消</el-button>
        <el-button
          type="primary"
          :disabled="isWriteLocked"
          :loading="pullBankInfoLoading"
          @click="handleConfirmPullBankInfo"
        >
          确认拉取
        </el-button>
      </span>
    </el-dialog>

    <!-- 批量上传弹窗 -->
    <el-dialog
      title="批量上传流水文件"
      :visible.sync="batchUploadDialogVisible"
      :close-on-click-modal="false"
      width="700px"
    >
      <el-upload
        class="batch-upload-area"
        drag
        action="#"
        multiple
        :disabled="isWriteLocked"
        :auto-upload="false"
        :on-change="handleBatchFileChange"
        :show-file-list="false"
        :file-list="selectedFiles"
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
        <div class="el-upload__tip" slot="tip">
          <div>支持 PDF、CSV、XLSX 格式文件，最多100个文件，单个文件不超过50MB</div>
          <div>行外流水文件命名规则：客户身份证号-其他内容</div>
        </div>
      </el-upload>

      <div v-if="selectedFiles.length > 0" class="selected-files">
        <div class="files-header">
          <span>已选择 {{ selectedFiles.length }} 个文件</span>
        </div>
        <div class="files-list">
          <div
            v-for="(file, index) in selectedFiles"
            :key="index"
            class="file-item"
          >
            <i class="el-icon-document"></i>
            <span class="file-name">{{ file.name }}</span>
            <span class="file-size">{{ formatFileSize(file.size) }}</span>
            <el-button
              type="text"
              icon="el-icon-close"
              @click="handleRemoveFile(index)"
            ></el-button>
          </div>
        </div>
      </div>

      <span slot="footer">
        <el-button @click="batchUploadDialogVisible = false">取消</el-button>
        <el-button
          type="primary"
          :disabled="isWriteLocked || selectedFiles.length === 0"
          :loading="uploadLoading"
          @click="handleBatchUpload"
          >开始上传</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

<script>
import {
  pullBankInfo,
  parseIdCardFile,
  batchUploadFiles,
  getFileUploadList,
  getFileUploadStatistics,
  deleteFileUploadRecord,
} from "@/api/ccdiProjectUpload";
import { parseTime } from "@/utils/ruoyi";
import {
  getUploadFileAction,
  getUploadFileStatusText,
  getUploadFileStatusType,
} from "./uploadFileActionRules";

export default {
  name: "UploadData",
  props: {
    projectId: {
      type: [String, Number],
      default: null,
    },
    projectInfo: {
      type: Object,
      default: () => ({
        projectName: "",
        updateTime: "",
        projectStatus: "0",
      }),
    },
    canOperate: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      // 加载状态
      loading: false,
      // 当前选中的菜单
      activeMenu: "upload",
      // 当前菜单标题
      currentMenuTitle: "上传数据",
      pullBankInfoDialogVisible: false,
      pullBankInfoLoading: false,
      parsingIdCardFile: false,
      idCardFileList: [],
      pullBankInfoForm: {
        dataChannelCode: "ZJRCU",
        idCardText: "",
        dateRange: [],
      },
      // 侧边栏菜单项
      menuItems: [
        { key: "upload", label: "上传数据", route: "upload" },
        { key: "config", label: "参数配置", route: "config" },
        { key: "overview", label: "结果总览", route: "overview" },
        { key: "special", label: "专项排查", route: "special" },
        { key: "detail", label: "流水明细查询", route: "detail" },
      ],
      // === 批量上传相关 ===
      batchUploadDialogVisible: false,
      selectedFiles: [],
      uploadLoading: false,

      // === 统计数据 ===
      statistics: {
        uploading: 0,
        parsing: 0,
        parsed_success: 0,
        parsed_failed: 0,
      },

      // === 文件列表相关 ===
      fileUploadList: [],
      listLoading: false,
      queryParams: {
        projectId: null,
        pageNum: 1,
        pageSize: 10,
      },
      total: 0,

      // === 轮询相关 ===
      pollingTimer: null,
      pollingEnabled: false,
      pollingInterval: 5000,
      lastFileStatusSignature: "",
    };
  },
  computed: {
    pullBankInfoDatePickerOptions() {
      return {
        disabledDate: (time) => this.isPullBankInfoDateDisabled(time),
      };
    },
    isProjectTagging() {
      return String(this.projectInfo.projectStatus) === "3";
    },
    isProjectArchived() {
      return String(this.projectInfo.projectStatus) === "2";
    },
    isWriteLocked() {
      return !this.canOperate || this.isProjectTagging || this.isProjectArchived;
    },
    isZjrcuPullBankInfo() {
      return this.pullBankInfoForm.dataChannelCode === "ZJRCU";
    },
    writeLockedMessage() {
      if (!this.canOperate) {
        return "当前项目仅可查看，暂不可上传或拉取数据。";
      }
      return this.isProjectArchived
        ? "项目已归档，暂不可上传或拉取数据。"
        : "项目正在进行银行流水打标，暂不可上传或拉取数据。";
    },
    isReportDisabled() {
      return ["0", "3", "4"].includes(String(this.projectInfo.projectStatus));
    },
  },
  created() {
    this.updateActiveMenu();
  },
  mounted() {
    // 加载统计数据和文件列表
    this.loadStatistics();
    this.loadFileList();

    // 检查是否需要启动轮询
    this.$nextTick(() => {
      if (this.statistics.uploading > 0 || this.statistics.parsing > 0) {
        this.startPolling();
      }
    });
  },
  beforeDestroy() {
    this.stopPolling();
  },
  methods: {
    /** 菜单点击 */
    handleMenuClick(key, route) {
      const menuItem = this.menuItems.find((m) => m.key === key);
      if (menuItem) {
        this.currentMenuTitle = menuItem.label;
      }
      if (key === "upload") {
        this.activeMenu = key;
      } else {
        // 其他菜单项通知父组件跳转
        this.$emit("menu-change", { key, route });
      }
    },
    /** 更新当前选中菜单 */
    updateActiveMenu() {
      this.activeMenu = "upload";
      this.currentMenuTitle = "上传数据";
    },
    handleViewReport() {
      if (this.isReportDisabled) {
        return;
      }
      this.$emit("menu-change", { key: "overview", route: "overview" });
    },
    handleGoCreditInfoPage() {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      this.$router.push("/maintain/creditInfo");
    },
    handleOpenBatchUploadDialog() {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      this.batchUploadDialogVisible = true;
      this.selectedFiles = [];
    },
    openPullBankInfoDialog() {
      this.pullBankInfoDialogVisible = true;
    },
    resetPullBankInfoForm() {
      this.pullBankInfoForm = {
        dataChannelCode: "ZJRCU",
        idCardText: "",
        dateRange: this.buildDefaultPullBankInfoDateRange(),
      };
      this.idCardFileList = [];
      this.parsingIdCardFile = false;
      this.pullBankInfoLoading = false;
    },
    parseIdCardText(text) {
      return Array.from(
        new Set(
          (text || "")
            .split(/,+/)
            .map((item) => item.trim())
            .filter(Boolean)
        )
      );
    },
    mergeIdCards(currentText, parsedIdCards) {
      const merged = [
        ...this.parseIdCardText(currentText),
        ...((parsedIdCards || [])
          .map((item) => String(item || "").trim())
          .filter(Boolean)),
      ];
      return Array.from(new Set(merged)).join(", ");
    },
    async handleIdCardFileChange(file, fileList) {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      const latestFile = (fileList || []).slice(-1);
      const currentFile = latestFile[0] || file;
      const fileName = (currentFile && currentFile.name) || "";
      const isExcel = /\.(xls|xlsx)$/i.test(fileName);

      if (!isExcel) {
        this.idCardFileList = [];
        this.$message.error("仅支持上传 .xls 或 .xlsx 文件");
        return;
      }

      if (!currentFile || !currentFile.raw) {
        this.idCardFileList = [];
        this.$message.error("未获取到有效文件");
        return;
      }

      this.idCardFileList = latestFile;
      this.parsingIdCardFile = true;

      try {
        const res = await parseIdCardFile(currentFile.raw);
        const parsedIdCards =
          (res && res.data && Array.isArray(res.data.idCards) && res.data.idCards) ||
          [];
        this.pullBankInfoForm.idCardText = this.mergeIdCards(
          this.pullBankInfoForm.idCardText,
          parsedIdCards
        );
        this.$message.success(
          `身份证文件解析成功，共 ${parsedIdCards.length} 条有效身份证`
        );
      } catch (error) {
        this.idCardFileList = [];
        this.$message.error(
          "身份证文件解析失败：" +
            ((error && error.message) || "未知错误")
        );
      } finally {
        this.parsingIdCardFile = false;
      }
    },
    handleIdCardFileRemove() {
      this.idCardFileList = [];
      this.parsingIdCardFile = false;
    },
    getPullBankInfoTodayStart() {
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      return today;
    },
    getPullBankInfoMinSelectableDate() {
      const minSelectableDate = this.getPullBankInfoMaxSelectableDate();
      minSelectableDate.setFullYear(minSelectableDate.getFullYear() - 1);
      return minSelectableDate;
    },
    getPullBankInfoMaxSelectableDate() {
      const yesterday = this.getPullBankInfoTodayStart();
      yesterday.setDate(yesterday.getDate() - 1);
      return yesterday;
    },
    formatPullBankInfoDate(date) {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      return `${year}-${month}-${day}`;
    },
    buildDefaultPullBankInfoDateRange() {
      const minSelectableDate = this.getPullBankInfoMinSelectableDate();
      const maxSelectableDate = this.getPullBankInfoMaxSelectableDate();
      const defaultStartDate = new Date(maxSelectableDate.getTime());
      defaultStartDate.setFullYear(defaultStartDate.getFullYear() - 1);
      if (defaultStartDate.getTime() < minSelectableDate.getTime()) {
        return [
          this.formatPullBankInfoDate(minSelectableDate),
          this.formatPullBankInfoDate(maxSelectableDate),
        ];
      }
      return [
        this.formatPullBankInfoDate(defaultStartDate),
        this.formatPullBankInfoDate(maxSelectableDate),
      ];
    },
    parsePullBankInfoDate(dateValue) {
      if (!dateValue) return null;
      const [year, month, day] = String(dateValue)
        .split("-")
        .map((item) => Number(item));
      if (![year, month, day].every(Number.isFinite)) {
        return null;
      }
      return new Date(year, month - 1, day);
    },
    isPullBankInfoDateDisabled(time) {
      const minSelectableDate = this.getPullBankInfoMinSelectableDate();
      const maxSelectableDate = this.getPullBankInfoMaxSelectableDate();
      return (
        time.getTime() < minSelectableDate.getTime() ||
        time.getTime() > maxSelectableDate.getTime()
      );
    },
    hasInvalidPullBankInfoDateRange(dateRange) {
      const minSelectableDate = this.getPullBankInfoMinSelectableDate();
      const maxSelectableDate = this.getPullBankInfoMaxSelectableDate();
      return (dateRange || []).some((dateValue) => {
        const date = this.parsePullBankInfoDate(dateValue);
        return (
          !date ||
          date.getTime() < minSelectableDate.getTime() ||
          date.getTime() > maxSelectableDate.getTime()
        );
      });
    },
    buildFinalIdCardList() {
      return this.parseIdCardText(this.pullBankInfoForm.idCardText);
    },
    async handleConfirmPullBankInfo() {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      const idCards = this.buildFinalIdCardList();
      const [startDate, endDate] = this.pullBankInfoForm.dateRange || [];
      const dataChannelCode = this.pullBankInfoForm.dataChannelCode;

      if (idCards.length === 0) {
        this.$message.warning("请至少输入一个身份证号");
        return;
      }

      if (this.isZjrcuPullBankInfo && (!startDate || !endDate)) {
        this.$message.warning("请选择完整的时间跨度");
        return;
      }

      if (
        this.isZjrcuPullBankInfo &&
        this.hasInvalidPullBankInfoDateRange([startDate, endDate])
      ) {
        this.$message.warning("时间跨度仅支持近一年内日期，且最晚只能选择到昨天");
        return;
      }

      this.pullBankInfoLoading = true;

      try {
        const payload = {
          projectId: this.projectId,
          idCards,
          dataChannelCode,
          startDate: this.isZjrcuPullBankInfo ? startDate : null,
          endDate: this.isZjrcuPullBankInfo ? endDate : null,
        };
        const res = await pullBankInfo(payload);

        this.pullBankInfoDialogVisible = false;
        this.resetPullBankInfoForm();
        this.$message.success((res && res.msg) || "拉取任务已提交");

        await Promise.all([this.loadStatistics(), this.loadFileList()]);
        this.lastFileStatusSignature = this.buildFileStatusSignature();
        this.$emit("refresh-project");

        const hasPollingRecords =
          this.statistics.uploading > 0 ||
          this.statistics.parsing > 0 ||
          this.fileUploadList.some((item) =>
            ["uploading", "parsing"].includes(item.fileStatus)
          );

        if (hasPollingRecords) {
          this.startPolling();
        }
      } catch (error) {
        this.pullBankInfoLoading = false;
        this.$message.error(
          "拉取流水失败：" + ((error && error.message) || "未知错误")
        );
      }
    },
    /** 拉取流水 */
    handleFetchBankInfo() {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      this.resetPullBankInfoForm();
      this.openPullBankInfoDialog();
    },
    /** 格式化更新时间 */
    formatUpdateTime(time) {
      if (!time) return "-";
      const date = new Date(time);
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      const hours = String(date.getHours()).padStart(2, "0");
      const minutes = String(date.getMinutes()).padStart(2, "0");
      return `${year}-${month}-${day} ${hours}:${minutes}`;
    },
    /** 获取状态样式类 */
    getStatusClass() {
      const status = String(this.projectInfo.projectStatus);
      const statusMap = {
        0: "processing",
        1: "success",
        2: "archived",
        3: "tagging",
        4: "failed",
      };
      return statusMap[status] || "processing";
    },
    /** 获取状态标签 */
    getStatusLabel() {
      const status = String(this.projectInfo.projectStatus);
      const statusMap = {
        0: "进行中",
        1: "已完成",
        2: "已归档",
        3: "打标中",
        4: "打标失败",
      };
      return statusMap[status] || "未知";
    },

    // === 批量上传相关方法 ===

    /** 批量上传的文件选择变化 */
    handleBatchFileChange(file, fileList) {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      if (fileList.length > 100) {
        this.$message.warning("最多上传100个文件");
        fileList = fileList.slice(0, 100);
      }

      const validTypes = ['.pdf', '.csv', '.xlsx'];
      const invalidFiles = fileList.filter((f) => {
        const ext = f.name.substring(f.name.lastIndexOf(".")).toLowerCase();
        return !validTypes.includes(ext);
      });

      if (invalidFiles.length > 0) {
        this.$message.error("仅支持 PDF、CSV、XLSX 格式文件");
        return;
      }

      const oversizedFiles = fileList.filter((f) => f.size > 50 * 1024 * 1024);
      if (oversizedFiles.length > 0) {
        this.$message.error("单个文件不能超过50MB");
        return;
      }

      this.selectedFiles = fileList;
    },

    /** 删除已选文件 */
    handleRemoveFile(index) {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      this.selectedFiles.splice(index, 1);
    },

    /** 开始批量上传 */
    async handleBatchUpload() {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      if (this.selectedFiles.length === 0) {
        this.$message.warning("请选择要上传的文件");
        return;
      }

      this.uploadLoading = true;

      try {
        const res = await batchUploadFiles(
          this.projectId,
          this.selectedFiles.map((f) => f.raw)
        );

        this.uploadLoading = false;
        this.batchUploadDialogVisible = false;

        this.$message.success("上传任务已提交，请查看处理进度");

        // 刷新数据并启动轮询
        await Promise.all([this.loadStatistics(), this.loadFileList()]);
        this.lastFileStatusSignature = this.buildFileStatusSignature();
        this.$emit("refresh-project");

        this.startPolling();
      } catch (error) {
        this.uploadLoading = false;
        this.$message.error("上传失败：" + (error.msg || "未知错误"));
      }
    },

    // === 统计和列表相关方法 ===

    /** 加载统计数据 */
    async loadStatistics() {
      try {
        const res = await getFileUploadStatistics(this.projectId);
        this.statistics = res.data || {
          uploading: 0,
          parsing: 0,
          parsed_success: 0,
          parsed_failed: 0,
        };
      } catch (error) {
        console.error("加载统计数据失败:", error);
      }
    },

    /** 加载文件列表 */
    async loadFileList() {
      this.listLoading = true;

      try {
        const params = {
          projectId: this.projectId,
          pageNum: this.queryParams.pageNum,
          pageSize: this.queryParams.pageSize,
        };

        const res = await getFileUploadList(params);
        this.fileUploadList = res.rows || [];
        this.total = res.total || 0;
        if (!this.lastFileStatusSignature) {
          this.lastFileStatusSignature = this.buildFileStatusSignature();
        }
      } catch (error) {
        this.$message.error("加载文件列表失败");
        console.error(error);
      } finally {
        this.listLoading = false;
      }
    },

    // === 轮询相关方法 ===

    /** 启动轮询 */
    startPolling() {
      if (this.pollingEnabled) return;
      this.pollingEnabled = true;

      const poll = () => {
        if (!this.pollingEnabled) return;

        Promise.all([this.loadStatistics(), this.loadFileList()])
          .then(() => {
            const currentSignature = this.buildFileStatusSignature();
            if (currentSignature !== this.lastFileStatusSignature) {
              this.lastFileStatusSignature = currentSignature;
              this.$emit("refresh-project");
            }
            if (
              this.statistics.uploading === 0 &&
              this.statistics.parsing === 0
            ) {
              this.stopPolling();
              return;
            }
            this.pollingTimer = setTimeout(poll, this.pollingInterval);
          })
          .catch((error) => {
            console.error("轮询失败:", error);
            this.pollingTimer = setTimeout(poll, this.pollingInterval);
          });
      };

      poll();
    },

    /** 停止轮询 */
    stopPolling() {
      this.pollingEnabled = false;
      if (this.pollingTimer) {
        clearTimeout(this.pollingTimer);
        this.pollingTimer = null;
      }
    },

    /** 手动刷新 */
    async handleManualRefresh() {
      await Promise.all([this.loadStatistics(), this.loadFileList()]);
      this.lastFileStatusSignature = this.buildFileStatusSignature();
      this.$emit("refresh-project");

      this.$message.success("刷新成功");

      if (this.statistics.uploading > 0 || this.statistics.parsing > 0) {
        this.startPolling();
      }
    },

    // === 辅助方法 ===

    /** 分页变化 */
    handlePageChange(pageNum) {
      this.queryParams.pageNum = pageNum;
      this.loadFileList();
    },
    buildFileStatusSignature() {
      return (this.fileUploadList || [])
        .map((item) => `${item.id || ""}:${item.fileStatus || ""}`)
        .join("|");
    },

    getRowAction(row) {
      const action = getUploadFileAction(row);
      if (!this.canOperate && action && action.key === "delete") {
        return null;
      }
      return action;
    },

    handleRowAction(row) {
      const action = this.getRowAction(row);
      if (!action) {
        return;
      }

      if (action.key === "viewError") {
        this.handleViewError(row);
        return;
      }

      if (action.key === "delete") {
        this.handleDeleteFile(row);
      }
    },

    handleViewError(row) {
      this.$alert(row.errorMessage || "未知错误", "错误信息", {
        confirmButtonText: "确定",
        type: "error",
      });
    },

    async handleDeleteFile(row) {
      if (this.isWriteLocked) {
        this.showWriteLockedMessage();
        return;
      }
      try {
        await this.$confirm(
          "删除该文件后将同步删除流水分析平台中的文件，并清除本系统中该文件对应的所有银行流水数据，项目内流水也将重新打标，是否继续？",
          "提示",
          {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          }
        );

        await deleteFileUploadRecord(row.id);
        this.$message.success("删除成功，已开始项目重新打标");
        await Promise.all([this.loadStatistics(), this.loadFileList()]);

        if (this.hasActivePollingRecords()) {
          this.startPolling();
        } else {
          this.stopPolling();
        }
      } catch (error) {
        if (error === "cancel" || error === "close") {
          return;
        }

        this.$message.error("删除失败：" + ((error && error.message) || "未知错误"));
      }
    },
    showWriteLockedMessage() {
      this.$message.warning(this.writeLockedMessage);
    },

    hasActivePollingRecords() {
      return (
        this.statistics.uploading > 0 ||
        this.statistics.parsing > 0 ||
        this.fileUploadList.some((item) =>
          ["uploading", "parsing"].includes(item.fileStatus)
        )
      );
    },

    /** 状态文本映射 */
    getStatusText(status) {
      return getUploadFileStatusText(status);
    },

    /** 状态标签类型映射 */
    getStatusType(status) {
      return getUploadFileStatusType(status);
    },

    /** 格式化文件大小 */
    formatFileSize(bytes) {
      if (bytes === 0) return "0 B";
      const k = 1024;
      const sizes = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
    /** 格式化上传时间 */
    formatUploadTime(time) {
      const formatted = parseTime(time, "{y}-{m}-{d} {h}:{i}:{s}");
      return formatted || "-";
    },
  },
};
</script>

<style lang="scss" scoped>
.upload-data-container {
  padding: 16px;
  background: transparent;
  min-height: 100%;
}

// 侧边栏
.sidebar {
  background: #ffffff;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  padding: 16px;
  height: 100%;
  display: flex;
  flex-direction: column;

  .sidebar-header {
    font-size: 16px;
    font-weight: 600;
    color: #303133;
    margin-bottom: 20px;
    padding-bottom: 12px;
    border-bottom: 1px solid #ebeef5;
  }

  .sidebar-menu {
    list-style: none;
    padding: 0;
    margin: 0 0 auto 0;
    flex: 1;

    .menu-item {
      display: flex;
      align-items: center;
      padding: 10px 12px;
      margin-bottom: 4px;
      cursor: pointer;
      border-radius: 4px;
      font-size: 14px;
      color: #606266;
      transition: all 0.3s;
      position: relative;

      &:hover {
        background: #f5f7fa;
        color: #303133;
      }

      &.active {
        background: #1890ff;
        color: #ffffff;

        .menu-dot {
          display: block;
        }
      }

      .menu-dot {
        display: none;
        width: 4px;
        height: 4px;
        background: #ffffff;
        border-radius: 50%;
        margin-right: 8px;
      }
    }
  }

  .sidebar-footer {
    border-top: 1px solid #ebeef5;
    padding-top: 16px;
    margin-top: 16px;

    .status {
      font-size: 13px;
      color: #606266;
      margin-bottom: 8px;

      .status-processing {
        color: #1890ff;
        font-weight: 500;
      }

      .status-success {
        color: #52c41a;
        font-weight: 500;
      }

      .status-archived {
        color: #909399;
        font-weight: 500;
      }

      .status-failed {
        color: #f56c6c;
        font-weight: 500;
      }
    }

    .update-time {
      font-size: 12px;
      color: #909399;
    }
  }
}

// 主内容区
.main-content {
  .tagging-lock-tip {
    margin-bottom: 16px;
    padding: 10px 14px;
    color: #a56a2a;
    background: #fbf3ea;
    border: 1px solid #eddcc8;
    border-radius: 10px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.65);
  }
}

// 文件列表区域
.file-list-section {
  background: #fff;
  border: 1px solid var(--ccdi-border);
  border-radius: 12px;
  padding: 20px 24px 24px;
  box-shadow: var(--ccdi-shadow);

  .list-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--ccdi-line);

    .toolbar-actions {
      display: flex;
      flex-wrap: wrap;
      justify-content: flex-end;
      gap: 12px;

      .el-button {
        min-width: 104px;
      }
    }
  }

  ::v-deep .el-table {
    border: 1px solid var(--ccdi-border);
    border-radius: 10px;
    overflow: hidden;
  }

  ::v-deep .el-table th {
    font-weight: 600;
  }
}

// 上传弹窗样式
::v-deep .el-dialog__wrapper {
  .upload-area,
  .batch-upload-area {
    width: 100%;
  }

  .upload-area .el-upload,
  .batch-upload-area .el-upload {
    width: 100%;
  }

  .upload-area .el-upload-dragger,
  .batch-upload-area .el-upload-dragger {
    width: 100%;
    height: 200px;
  }

  .el-upload__tip {
    margin-top: 8px;
    color: var(--ccdi-text-muted);
    font-size: 12px;
  }
}

.pull-bank-info-form {
  .pull-bank-channel-group {
    display: flex;
    flex-direction: column;
    gap: 12px;

    ::v-deep .el-radio {
      display: flex;
      align-items: center;
      margin-right: 0;
      line-height: 24px;
    }

    ::v-deep .el-radio__label {
      display: inline-flex;
      align-items: baseline;
      gap: 16px;
      flex-wrap: wrap;
      padding-left: 10px;
    }
  }

  .pull-bank-channel-title {
    font-size: 15px;
    color: #303133;
  }

  .pull-bank-channel-tip {
    font-size: 13px;
    color: #909399;
  }

  .pull-bank-field-tip {
    margin-top: 8px;
    font-size: 12px;
    color: #909399;
    line-height: 1.5;
  }
}

.pull-bank-file-panel {
  padding: 16px;
  border: 1px solid var(--ccdi-border);
  border-radius: 8px;
  background: #f8fbfe;

  .pull-bank-file-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
  }

  .pull-bank-file-tip {
    font-size: 12px;
    color: var(--ccdi-text-secondary);
    line-height: 1.5;
  }
}

.pull-bank-file-upload {
  display: inline-flex;
  flex: 0 0 auto;
}

.selected-id-card-file {
  margin-top: 12px;
  padding: 10px 12px;
  border-radius: 6px;
  background: #ffffff;
  border: 1px solid var(--ccdi-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;

  .selected-id-card-file__info {
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--ccdi-text-primary);
  }

  .selected-id-card-file__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .el-button {
    flex-shrink: 0;
    padding: 0;
  }
}

.pull-bank-range-picker {
  width: 100%;
}

// 批量上传弹窗样式
.batch-upload-area {
  width: 100%;

  ::v-deep .el-upload {
    width: 100%;

    .el-upload-dragger {
      width: 100%;
      height: 180px;
    }
  }
}

.selected-files {
  margin-top: 16px;
  border: 1px solid var(--ccdi-border);
  border-radius: 4px;
  max-height: 300px;
  overflow-y: auto;

  .files-header {
    padding: 12px 16px;
    background: #f7fafd;
    border-bottom: 1px solid var(--ccdi-line);
    font-size: 14px;
    font-weight: 500;
    color: var(--ccdi-text-secondary);
  }

  .files-list {
    padding: 8px;

    .file-item {
      display: flex;
      align-items: center;
      padding: 8px 12px;
      border-radius: 4px;
      transition: background 0.3s;

      &:hover {
        background: #f7fafd;
      }

      i {
        font-size: 18px;
        color: var(--ccdi-primary);
        margin-right: 8px;
      }

      .file-name {
        flex: 1;
        font-size: 14px;
        color: var(--ccdi-text-primary);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .file-size {
        font-size: 12px;
        color: var(--ccdi-text-muted);
        margin: 0 12px;
      }

      .el-button {
        padding: 4px;
        color: var(--ccdi-text-muted);

        &:hover {
          color: #b55252;
        }
      }
    }
  }
}

.parse-tip {
  margin-top: 8px;
  font-size: 12px;
  color: var(--ccdi-text-muted);
}

// 响应式
@media (max-width: 1200px) {
  .file-list-section .list-toolbar .toolbar-actions {
    gap: 10px;
  }
}

@media (max-width: 768px) {
  .upload-data-container {
    padding: 8px;
  }

  .sidebar {
    margin-bottom: 16px;
  }

  .file-list-section .list-toolbar {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;

    .toolbar-actions {
      width: 100%;
      justify-content: flex-start;
    }
  }

  .pull-bank-file-panel {
    padding: 12px;

    .pull-bank-file-actions {
      align-items: stretch;
      gap: 10px;
    }
  }

  .pull-bank-file-upload {
    width: 100%;
  }

  .selected-id-card-file {
    align-items: flex-start;
    flex-direction: column;
  }
}
</style>
