2026-03-02 19:18:45 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="upload-data-container">
|
|
|
|
|
|
<!-- 主内容区 -->
|
|
|
|
|
|
<div class="main-content">
|
2026-03-24 21:45:55 +08:00
|
|
|
|
<div v-if="isProjectTagging || isProjectArchived" class="tagging-lock-tip">
|
|
|
|
|
|
{{ isProjectArchived ? "项目已归档,暂不可上传或拉取数据。" : "项目正在进行银行流水打标,暂不可上传或拉取数据。" }}
|
2026-03-18 15:55:55 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-05 14:21:33 +08:00
|
|
|
|
<!-- 文件上传记录列表 -->
|
|
|
|
|
|
<div class="file-list-section">
|
|
|
|
|
|
<div class="list-toolbar">
|
|
|
|
|
|
<el-button icon="el-icon-refresh" @click="handleManualRefresh">刷新</el-button>
|
2026-03-25 14:26:58 +08:00
|
|
|
|
<div class="toolbar-actions">
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon="el-icon-upload2"
|
|
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
|
|
|
|
|
@click="handleOpenBatchUploadDialog"
|
|
|
|
|
|
>
|
|
|
|
|
|
上传流水
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon="el-icon-download"
|
|
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
|
|
|
|
|
@click="handleFetchBankInfo"
|
|
|
|
|
|
>
|
|
|
|
|
|
拉取本行信息
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon="el-icon-upload2"
|
|
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
|
|
|
|
|
@click="handleGoCreditInfoPage"
|
|
|
|
|
|
>
|
|
|
|
|
|
征信导入
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon="el-icon-view"
|
|
|
|
|
|
:disabled="isReportDisabled"
|
|
|
|
|
|
@click="handleViewReport"
|
|
|
|
|
|
>
|
|
|
|
|
|
查看报告
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
2026-03-05 14:21:33 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-11 15:08:36 +08:00
|
|
|
|
<el-table :data="fileUploadList" v-loading="listLoading">
|
2026-03-05 14:21:33 +08:00
|
|
|
|
<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>
|
2026-03-11 16:21:51 +08:00
|
|
|
|
<el-table-column prop="accountNos" label="主体账号" min-width="180">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
{{ scope.row.accountNos || '-' }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-03-29 09:59:56 +08:00
|
|
|
|
<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>
|
2026-03-05 15:45:20 +08:00
|
|
|
|
<el-table-column prop="uploadTime" label="上传时间" width="180">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
{{ formatUploadTime(scope.row.uploadTime) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-03-05 14:21:33 +08:00
|
|
|
|
<el-table-column prop="uploadUser" label="上传人" width="100"></el-table-column>
|
2026-03-16 15:14:54 +08:00
|
|
|
|
<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>
|
2026-03-05 14:21:33 +08:00
|
|
|
|
</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>
|
2026-03-02 19:18:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-11 17:34:31 +08:00
|
|
|
|
<el-dialog
|
|
|
|
|
|
title="拉取本行信息"
|
|
|
|
|
|
:visible.sync="pullBankInfoDialogVisible"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
width="640px"
|
|
|
|
|
|
>
|
2026-03-12 10:46:40 +08:00
|
|
|
|
<el-form
|
|
|
|
|
|
class="pull-bank-info-form"
|
|
|
|
|
|
:model="pullBankInfoForm"
|
|
|
|
|
|
label-width="100px"
|
|
|
|
|
|
>
|
2026-03-11 17:34:31 +08:00
|
|
|
|
<el-form-item label="证件号码">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="pullBankInfoForm.idCardText"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:rows="5"
|
2026-03-24 21:45:55 +08:00
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
2026-05-26 16:37:16 +08:00
|
|
|
|
placeholder="仅支持英文逗号分隔"
|
2026-03-11 17:34:31 +08:00
|
|
|
|
/>
|
2026-03-12 10:46:40 +08:00
|
|
|
|
<div class="pull-bank-field-tip">
|
2026-05-26 16:37:16 +08:00
|
|
|
|
仅支持英文逗号分隔,文件解析结果会自动合并并去重
|
2026-03-12 10:46:40 +08:00
|
|
|
|
</div>
|
2026-03-11 17:34:31 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="身份证文件">
|
2026-03-12 10:46:40 +08:00
|
|
|
|
<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"
|
2026-03-24 21:45:55 +08:00
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
2026-03-12 10:46:40 +08:00
|
|
|
|
: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">
|
|
|
|
|
|
正在解析身份证文件...
|
2026-03-11 17:34:31 +08:00
|
|
|
|
</div>
|
2026-03-11 17:58:39 +08:00
|
|
|
|
</div>
|
2026-03-11 17:34:31 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="时间跨度">
|
|
|
|
|
|
<el-date-picker
|
2026-03-12 10:46:40 +08:00
|
|
|
|
class="pull-bank-range-picker"
|
2026-03-11 17:34:31 +08:00
|
|
|
|
v-model="pullBankInfoForm.dateRange"
|
2026-03-24 21:45:55 +08:00
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
2026-03-11 17:34:31 +08:00
|
|
|
|
type="daterange"
|
|
|
|
|
|
value-format="yyyy-MM-dd"
|
2026-03-12 13:41:40 +08:00
|
|
|
|
:picker-options="pullBankInfoDatePickerOptions"
|
2026-03-11 17:34:31 +08:00
|
|
|
|
range-separator="至"
|
|
|
|
|
|
start-placeholder="开始日期"
|
|
|
|
|
|
end-placeholder="结束日期"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
<span slot="footer">
|
|
|
|
|
|
<el-button @click="pullBankInfoDialogVisible = false">取消</el-button>
|
2026-03-11 17:58:39 +08:00
|
|
|
|
<el-button
|
|
|
|
|
|
type="primary"
|
2026-03-24 21:45:55 +08:00
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
2026-03-11 17:58:39 +08:00
|
|
|
|
:loading="pullBankInfoLoading"
|
|
|
|
|
|
@click="handleConfirmPullBankInfo"
|
|
|
|
|
|
>
|
2026-03-11 17:34:31 +08:00
|
|
|
|
确认拉取
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
2026-03-05 14:21:33 +08:00
|
|
|
|
<!-- 批量上传弹窗 -->
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
title="批量上传流水文件"
|
|
|
|
|
|
:visible.sync="batchUploadDialogVisible"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
width="700px"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
class="batch-upload-area"
|
|
|
|
|
|
drag
|
|
|
|
|
|
action="#"
|
|
|
|
|
|
multiple
|
2026-03-24 21:45:55 +08:00
|
|
|
|
:disabled="isProjectTagging || isProjectArchived"
|
2026-03-05 14:21:33 +08:00
|
|
|
|
:auto-upload="false"
|
|
|
|
|
|
:on-change="handleBatchFileChange"
|
2026-03-10 11:14:12 +08:00
|
|
|
|
:show-file-list="false"
|
2026-03-05 14:21:33 +08:00
|
|
|
|
:file-list="selectedFiles"
|
|
|
|
|
|
>
|
|
|
|
|
|
<i class="el-icon-upload"></i>
|
|
|
|
|
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
|
|
|
<div class="el-upload__tip" slot="tip">
|
2026-03-05 14:41:11 +08:00
|
|
|
|
支持 PDF、CSV、Excel 格式文件,最多100个文件,单个文件不超过50MB
|
2026-03-05 14:21:33 +08:00
|
|
|
|
</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"
|
2026-03-24 21:45:55 +08:00
|
|
|
|
:disabled="isProjectTagging || isProjectArchived || selectedFiles.length === 0"
|
2026-03-05 14:21:33 +08:00
|
|
|
|
:loading="uploadLoading"
|
|
|
|
|
|
@click="handleBatchUpload"
|
|
|
|
|
|
>开始上传</el-button
|
|
|
|
|
|
>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</el-dialog>
|
2026-03-02 19:18:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import {
|
|
|
|
|
|
pullBankInfo,
|
2026-03-11 17:32:18 +08:00
|
|
|
|
parseIdCardFile,
|
2026-03-05 14:21:33 +08:00
|
|
|
|
batchUploadFiles,
|
|
|
|
|
|
getFileUploadList,
|
|
|
|
|
|
getFileUploadStatistics,
|
2026-03-16 15:14:54 +08:00
|
|
|
|
deleteFileUploadRecord,
|
2026-03-02 19:18:45 +08:00
|
|
|
|
} from "@/api/ccdiProjectUpload";
|
2026-03-05 15:45:20 +08:00
|
|
|
|
import { parseTime } from "@/utils/ruoyi";
|
2026-03-16 15:14:54 +08:00
|
|
|
|
import {
|
|
|
|
|
|
getUploadFileAction,
|
|
|
|
|
|
getUploadFileStatusText,
|
|
|
|
|
|
getUploadFileStatusType,
|
2026-03-16 15:47:18 +08:00
|
|
|
|
} from "./uploadFileActionRules";
|
2026-03-02 19:18:45 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: "UploadData",
|
|
|
|
|
|
props: {
|
|
|
|
|
|
projectId: {
|
|
|
|
|
|
type: [String, Number],
|
|
|
|
|
|
default: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
projectInfo: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: () => ({
|
|
|
|
|
|
projectName: "",
|
|
|
|
|
|
updateTime: "",
|
|
|
|
|
|
projectStatus: "0",
|
|
|
|
|
|
}),
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
// 加载状态
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
// 当前选中的菜单
|
|
|
|
|
|
activeMenu: "upload",
|
|
|
|
|
|
// 当前菜单标题
|
|
|
|
|
|
currentMenuTitle: "上传数据",
|
2026-03-11 17:34:31 +08:00
|
|
|
|
pullBankInfoDialogVisible: false,
|
|
|
|
|
|
pullBankInfoLoading: false,
|
|
|
|
|
|
parsingIdCardFile: false,
|
|
|
|
|
|
idCardFileList: [],
|
|
|
|
|
|
pullBankInfoForm: {
|
|
|
|
|
|
idCardText: "",
|
|
|
|
|
|
dateRange: [],
|
|
|
|
|
|
},
|
2026-03-02 19:18:45 +08:00
|
|
|
|
// 侧边栏菜单项
|
|
|
|
|
|
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" },
|
|
|
|
|
|
],
|
2026-03-05 14:21:33 +08:00
|
|
|
|
// === 批量上传相关 ===
|
|
|
|
|
|
batchUploadDialogVisible: false,
|
|
|
|
|
|
selectedFiles: [],
|
|
|
|
|
|
uploadLoading: false,
|
|
|
|
|
|
|
|
|
|
|
|
// === 统计数据 ===
|
|
|
|
|
|
statistics: {
|
|
|
|
|
|
uploading: 0,
|
|
|
|
|
|
parsing: 0,
|
|
|
|
|
|
parsed_success: 0,
|
|
|
|
|
|
parsed_failed: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// === 文件列表相关 ===
|
|
|
|
|
|
fileUploadList: [],
|
|
|
|
|
|
listLoading: false,
|
|
|
|
|
|
queryParams: {
|
|
|
|
|
|
projectId: null,
|
|
|
|
|
|
pageNum: 1,
|
2026-03-10 14:28:38 +08:00
|
|
|
|
pageSize: 10,
|
2026-03-05 14:21:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
|
|
|
|
|
|
// === 轮询相关 ===
|
|
|
|
|
|
pollingTimer: null,
|
|
|
|
|
|
pollingEnabled: false,
|
|
|
|
|
|
pollingInterval: 5000,
|
2026-03-18 15:55:55 +08:00
|
|
|
|
lastFileStatusSignature: "",
|
2026-03-02 19:18:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
2026-03-12 13:41:40 +08:00
|
|
|
|
computed: {
|
|
|
|
|
|
pullBankInfoDatePickerOptions() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
disabledDate: (time) => this.isPullBankInfoDateDisabled(time),
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
2026-03-18 15:55:55 +08:00
|
|
|
|
isProjectTagging() {
|
|
|
|
|
|
return String(this.projectInfo.projectStatus) === "3";
|
|
|
|
|
|
},
|
2026-03-24 21:45:55 +08:00
|
|
|
|
isProjectArchived() {
|
|
|
|
|
|
return String(this.projectInfo.projectStatus) === "2";
|
|
|
|
|
|
},
|
2026-03-23 19:58:03 +08:00
|
|
|
|
isReportDisabled() {
|
|
|
|
|
|
return ["0", "3"].includes(String(this.projectInfo.projectStatus));
|
|
|
|
|
|
},
|
2026-03-18 15:55:55 +08:00
|
|
|
|
},
|
2026-03-02 19:18:45 +08:00
|
|
|
|
created() {
|
|
|
|
|
|
this.updateActiveMenu();
|
|
|
|
|
|
},
|
|
|
|
|
|
mounted() {
|
2026-03-05 14:21:33 +08:00
|
|
|
|
// 加载统计数据和文件列表
|
|
|
|
|
|
this.loadStatistics();
|
|
|
|
|
|
this.loadFileList();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否需要启动轮询
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
if (this.statistics.uploading > 0 || this.statistics.parsing > 0) {
|
|
|
|
|
|
this.startPolling();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
this.stopPolling();
|
2026-03-02 19:18:45 +08:00
|
|
|
|
},
|
|
|
|
|
|
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 = "上传数据";
|
|
|
|
|
|
},
|
2026-03-23 19:58:03 +08:00
|
|
|
|
handleViewReport() {
|
|
|
|
|
|
if (this.isReportDisabled) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$emit("menu-change", { key: "overview", route: "overview" });
|
|
|
|
|
|
},
|
2026-03-24 13:59:36 +08:00
|
|
|
|
handleGoCreditInfoPage() {
|
2026-03-24 21:45:55 +08:00
|
|
|
|
if (this.isProjectTagging || this.isProjectArchived) {
|
2026-03-23 19:58:03 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-24 13:59:36 +08:00
|
|
|
|
this.$router.push("/maintain/creditInfo");
|
2026-03-23 19:58:03 +08:00
|
|
|
|
},
|
2026-03-25 14:11:54 +08:00
|
|
|
|
handleOpenBatchUploadDialog() {
|
|
|
|
|
|
if (this.isProjectTagging || this.isProjectArchived) {
|
|
|
|
|
|
return;
|
2026-03-02 19:18:45 +08:00
|
|
|
|
}
|
2026-03-25 14:11:54 +08:00
|
|
|
|
this.batchUploadDialogVisible = true;
|
|
|
|
|
|
this.selectedFiles = [];
|
2026-03-02 19:18:45 +08:00
|
|
|
|
},
|
2026-03-11 17:34:31 +08:00
|
|
|
|
openPullBankInfoDialog() {
|
|
|
|
|
|
this.pullBankInfoDialogVisible = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
resetPullBankInfoForm() {
|
|
|
|
|
|
this.pullBankInfoForm = {
|
|
|
|
|
|
idCardText: "",
|
2026-05-26 16:37:16 +08:00
|
|
|
|
dateRange: this.buildDefaultPullBankInfoDateRange(),
|
2026-03-11 17:34:31 +08:00
|
|
|
|
};
|
|
|
|
|
|
this.idCardFileList = [];
|
|
|
|
|
|
this.parsingIdCardFile = false;
|
|
|
|
|
|
this.pullBankInfoLoading = false;
|
|
|
|
|
|
},
|
2026-03-11 17:58:39 +08:00
|
|
|
|
parseIdCardText(text) {
|
|
|
|
|
|
return Array.from(
|
|
|
|
|
|
new Set(
|
|
|
|
|
|
(text || "")
|
2026-05-26 16:37:16 +08:00
|
|
|
|
.split(/,+/)
|
2026-03-11 17:58:39 +08:00
|
|
|
|
.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) {
|
2026-03-24 21:45:55 +08:00
|
|
|
|
if (this.isProjectTagging || this.isProjectArchived) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-11 17:58:39 +08:00
|
|
|
|
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;
|
|
|
|
|
|
},
|
2026-03-12 13:41:40 +08:00
|
|
|
|
getPullBankInfoTodayStart() {
|
|
|
|
|
|
const today = new Date();
|
|
|
|
|
|
today.setHours(0, 0, 0, 0);
|
|
|
|
|
|
return today;
|
|
|
|
|
|
},
|
2026-05-26 16:37:16 +08:00
|
|
|
|
getPullBankInfoMinSelectableDate() {
|
|
|
|
|
|
return new Date(2025, 0, 1);
|
|
|
|
|
|
},
|
2026-03-12 13:41:40 +08:00
|
|
|
|
getPullBankInfoMaxSelectableDate() {
|
|
|
|
|
|
const yesterday = this.getPullBankInfoTodayStart();
|
|
|
|
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
|
|
|
|
return yesterday;
|
|
|
|
|
|
},
|
2026-05-26 16:37:16 +08:00
|
|
|
|
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),
|
|
|
|
|
|
];
|
|
|
|
|
|
},
|
2026-03-12 13:41:40 +08:00
|
|
|
|
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) {
|
2026-05-26 16:37:16 +08:00
|
|
|
|
const minSelectableDate = this.getPullBankInfoMinSelectableDate();
|
|
|
|
|
|
const todayStart = this.getPullBankInfoTodayStart();
|
|
|
|
|
|
return (
|
|
|
|
|
|
time.getTime() < minSelectableDate.getTime() ||
|
|
|
|
|
|
time.getTime() >= todayStart.getTime()
|
|
|
|
|
|
);
|
2026-03-12 13:41:40 +08:00
|
|
|
|
},
|
|
|
|
|
|
hasInvalidPullBankInfoDateRange(dateRange) {
|
2026-05-26 16:37:16 +08:00
|
|
|
|
const minSelectableDate = this.getPullBankInfoMinSelectableDate();
|
2026-03-12 13:41:40 +08:00
|
|
|
|
const maxSelectableDate = this.getPullBankInfoMaxSelectableDate();
|
|
|
|
|
|
return (dateRange || []).some((dateValue) => {
|
|
|
|
|
|
const date = this.parsePullBankInfoDate(dateValue);
|
2026-05-26 16:37:16 +08:00
|
|
|
|
return (
|
|
|
|
|
|
!date ||
|
|
|
|
|
|
date.getTime() < minSelectableDate.getTime() ||
|
|
|
|
|
|
date.getTime() > maxSelectableDate.getTime()
|
|
|
|
|
|
);
|
2026-03-12 13:41:40 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-03-11 17:58:39 +08:00
|
|
|
|
buildFinalIdCardList() {
|
|
|
|
|
|
return this.parseIdCardText(this.pullBankInfoForm.idCardText);
|
|
|
|
|
|
},
|
|
|
|
|
|
async handleConfirmPullBankInfo() {
|
2026-03-24 21:45:55 +08:00
|
|
|
|
if (this.isProjectTagging || this.isProjectArchived) {
|
|
|
|
|
|
this.$message.warning(
|
|
|
|
|
|
this.isProjectArchived
|
|
|
|
|
|
? "项目已归档,暂不可上传或拉取数据"
|
|
|
|
|
|
: "项目正在进行银行流水打标,暂不可上传或拉取数据"
|
|
|
|
|
|
);
|
2026-03-18 15:55:55 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-11 17:58:39 +08:00
|
|
|
|
const idCards = this.buildFinalIdCardList();
|
|
|
|
|
|
const [startDate, endDate] = this.pullBankInfoForm.dateRange || [];
|
|
|
|
|
|
|
|
|
|
|
|
if (idCards.length === 0) {
|
|
|
|
|
|
this.$message.warning("请至少输入一个身份证号");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!startDate || !endDate) {
|
|
|
|
|
|
this.$message.warning("请选择完整的时间跨度");
|
|
|
|
|
|
return;
|
2026-03-12 13:41:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.hasInvalidPullBankInfoDateRange([startDate, endDate])) {
|
2026-05-26 16:37:16 +08:00
|
|
|
|
this.$message.warning("时间跨度仅支持 2025-01-01 至昨天");
|
2026-03-12 13:41:40 +08:00
|
|
|
|
return;
|
2026-03-11 17:58:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.pullBankInfoLoading = true;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = {
|
|
|
|
|
|
projectId: this.projectId,
|
|
|
|
|
|
idCards,
|
|
|
|
|
|
startDate,
|
|
|
|
|
|
endDate,
|
|
|
|
|
|
};
|
|
|
|
|
|
const res = await pullBankInfo(payload);
|
|
|
|
|
|
|
|
|
|
|
|
this.pullBankInfoDialogVisible = false;
|
|
|
|
|
|
this.resetPullBankInfoForm();
|
|
|
|
|
|
this.$message.success((res && res.msg) || "拉取任务已提交");
|
|
|
|
|
|
|
|
|
|
|
|
await Promise.all([this.loadStatistics(), this.loadFileList()]);
|
2026-03-18 15:55:55 +08:00
|
|
|
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
|
|
|
|
|
this.$emit("refresh-project");
|
2026-03-11 17:58:39 +08:00
|
|
|
|
|
|
|
|
|
|
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) || "未知错误")
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-03-02 19:18:45 +08:00
|
|
|
|
/** 拉取本行信息 */
|
2026-03-11 17:32:18 +08:00
|
|
|
|
handleFetchBankInfo() {
|
2026-03-24 21:45:55 +08:00
|
|
|
|
if (this.isProjectTagging || this.isProjectArchived) {
|
|
|
|
|
|
this.$message.warning(
|
|
|
|
|
|
this.isProjectArchived
|
|
|
|
|
|
? "项目已归档,暂不可上传或拉取数据"
|
|
|
|
|
|
: "项目正在进行银行流水打标,暂不可上传或拉取数据"
|
|
|
|
|
|
);
|
2026-03-18 15:55:55 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-11 17:34:31 +08:00
|
|
|
|
this.resetPullBankInfoForm();
|
|
|
|
|
|
this.openPullBankInfoDialog();
|
2026-03-02 19:18:45 +08:00
|
|
|
|
},
|
|
|
|
|
|
/** 格式化更新时间 */
|
|
|
|
|
|
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",
|
2026-03-18 15:55:55 +08:00
|
|
|
|
3: "tagging",
|
2026-03-02 19:18:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
return statusMap[status] || "processing";
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 获取状态标签 */
|
|
|
|
|
|
getStatusLabel() {
|
|
|
|
|
|
const status = String(this.projectInfo.projectStatus);
|
|
|
|
|
|
const statusMap = {
|
|
|
|
|
|
0: "进行中",
|
|
|
|
|
|
1: "已完成",
|
|
|
|
|
|
2: "已归档",
|
2026-03-18 15:55:55 +08:00
|
|
|
|
3: "打标中",
|
2026-03-02 19:18:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
return statusMap[status] || "未知";
|
|
|
|
|
|
},
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
|
|
|
|
|
// === 批量上传相关方法 ===
|
|
|
|
|
|
|
|
|
|
|
|
/** 批量上传的文件选择变化 */
|
|
|
|
|
|
handleBatchFileChange(file, fileList) {
|
|
|
|
|
|
if (fileList.length > 100) {
|
|
|
|
|
|
this.$message.warning("最多上传100个文件");
|
|
|
|
|
|
fileList = fileList.slice(0, 100);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 14:41:11 +08:00
|
|
|
|
const validTypes = ['.pdf', '.csv', '.xlsx', '.xls'];
|
2026-03-05 14:21:33 +08:00
|
|
|
|
const invalidFiles = fileList.filter((f) => {
|
|
|
|
|
|
const ext = f.name.substring(f.name.lastIndexOf(".")).toLowerCase();
|
|
|
|
|
|
return !validTypes.includes(ext);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (invalidFiles.length > 0) {
|
2026-03-05 14:41:11 +08:00
|
|
|
|
this.$message.error("仅支持 PDF、CSV、Excel 格式文件");
|
2026-03-05 14:21:33 +08:00
|
|
|
|
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) {
|
|
|
|
|
|
this.selectedFiles.splice(index, 1);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/** 开始批量上传 */
|
|
|
|
|
|
async handleBatchUpload() {
|
2026-03-24 21:45:55 +08:00
|
|
|
|
if (this.isProjectTagging || this.isProjectArchived) {
|
|
|
|
|
|
this.$message.warning(
|
|
|
|
|
|
this.isProjectArchived
|
|
|
|
|
|
? "项目已归档,暂不可上传或拉取数据"
|
|
|
|
|
|
: "项目正在进行银行流水打标,暂不可上传或拉取数据"
|
|
|
|
|
|
);
|
2026-03-18 15:55:55 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-05 14:21:33 +08:00
|
|
|
|
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()]);
|
2026-03-18 15:55:55 +08:00
|
|
|
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
|
|
|
|
|
this.$emit("refresh-project");
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
2026-03-18 15:55:55 +08:00
|
|
|
|
if (!this.lastFileStatusSignature) {
|
|
|
|
|
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
|
|
|
|
|
}
|
2026-03-05 14:21:33 +08:00
|
|
|
|
} 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(() => {
|
2026-03-18 15:55:55 +08:00
|
|
|
|
const currentSignature = this.buildFileStatusSignature();
|
|
|
|
|
|
if (currentSignature !== this.lastFileStatusSignature) {
|
|
|
|
|
|
this.lastFileStatusSignature = currentSignature;
|
|
|
|
|
|
this.$emit("refresh-project");
|
|
|
|
|
|
}
|
2026-03-05 14:21:33 +08:00
|
|
|
|
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()]);
|
2026-03-18 15:55:55 +08:00
|
|
|
|
this.lastFileStatusSignature = this.buildFileStatusSignature();
|
|
|
|
|
|
this.$emit("refresh-project");
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
|
|
|
|
|
this.$message.success("刷新成功");
|
|
|
|
|
|
|
|
|
|
|
|
if (this.statistics.uploading > 0 || this.statistics.parsing > 0) {
|
|
|
|
|
|
this.startPolling();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// === 辅助方法 ===
|
|
|
|
|
|
|
|
|
|
|
|
/** 分页变化 */
|
|
|
|
|
|
handlePageChange(pageNum) {
|
|
|
|
|
|
this.queryParams.pageNum = pageNum;
|
|
|
|
|
|
this.loadFileList();
|
|
|
|
|
|
},
|
2026-03-18 15:55:55 +08:00
|
|
|
|
buildFileStatusSignature() {
|
|
|
|
|
|
return (this.fileUploadList || [])
|
|
|
|
|
|
.map((item) => `${item.id || ""}:${item.fileStatus || ""}`)
|
|
|
|
|
|
.join("|");
|
|
|
|
|
|
},
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
2026-03-16 15:14:54 +08:00
|
|
|
|
getRowAction(row) {
|
2026-03-29 09:59:56 +08:00
|
|
|
|
return getUploadFileAction(row);
|
2026-03-16 15:14:54 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await this.$confirm(
|
2026-03-19 16:05:40 +08:00
|
|
|
|
"删除该文件后将同步删除流水分析平台中的文件,并清除本系统中该文件对应的所有银行流水数据,项目内流水也将重新打标,是否继续?",
|
2026-03-16 15:14:54 +08:00
|
|
|
|
"提示",
|
|
|
|
|
|
{
|
|
|
|
|
|
confirmButtonText: "确定",
|
|
|
|
|
|
cancelButtonText: "取消",
|
|
|
|
|
|
type: "warning",
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
await deleteFileUploadRecord(row.id);
|
2026-03-19 16:05:40 +08:00
|
|
|
|
this.$message.success("删除成功,已开始项目重新打标");
|
2026-03-16 15:14:54 +08:00
|
|
|
|
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) || "未知错误"));
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
hasActivePollingRecords() {
|
|
|
|
|
|
return (
|
|
|
|
|
|
this.statistics.uploading > 0 ||
|
|
|
|
|
|
this.statistics.parsing > 0 ||
|
|
|
|
|
|
this.fileUploadList.some((item) =>
|
|
|
|
|
|
["uploading", "parsing"].includes(item.fileStatus)
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-03-05 14:21:33 +08:00
|
|
|
|
/** 状态文本映射 */
|
|
|
|
|
|
getStatusText(status) {
|
2026-03-16 15:14:54 +08:00
|
|
|
|
return getUploadFileStatusText(status);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/** 状态标签类型映射 */
|
|
|
|
|
|
getStatusType(status) {
|
2026-03-16 15:14:54 +08:00
|
|
|
|
return getUploadFileStatusType(status);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/** 格式化文件大小 */
|
|
|
|
|
|
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];
|
|
|
|
|
|
},
|
2026-03-05 15:45:20 +08:00
|
|
|
|
/** 格式化上传时间 */
|
|
|
|
|
|
formatUploadTime(time) {
|
|
|
|
|
|
const formatted = parseTime(time, "{y}-{m}-{d} {h}:{i}:{s}");
|
|
|
|
|
|
return formatted || "-";
|
|
|
|
|
|
},
|
2026-03-02 19:18:45 +08:00
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.upload-data-container {
|
|
|
|
|
|
padding: 16px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
background: transparent;
|
2026-03-02 19:18:45 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.update-time {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 主内容区
|
|
|
|
|
|
.main-content {
|
2026-03-18 15:55:55 +08:00
|
|
|
|
.tagging-lock-tip {
|
|
|
|
|
|
margin-bottom: 16px;
|
2026-03-25 14:11:54 +08:00
|
|
|
|
padding: 10px 14px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: #a56a2a;
|
|
|
|
|
|
background: #fbf3ea;
|
|
|
|
|
|
border: 1px solid #eddcc8;
|
2026-03-25 14:11:54 +08:00
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.65);
|
2026-03-02 19:18:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 14:21:33 +08:00
|
|
|
|
// 文件列表区域
|
|
|
|
|
|
.file-list-section {
|
|
|
|
|
|
background: #fff;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
border: 1px solid var(--ccdi-border);
|
2026-03-25 14:11:54 +08:00
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
padding: 20px 24px 24px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
box-shadow: var(--ccdi-shadow);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
|
|
|
|
|
.list-toolbar {
|
|
|
|
|
|
display: flex;
|
2026-03-25 14:26:58 +08:00
|
|
|
|
justify-content: space-between;
|
2026-03-05 14:21:33 +08:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 16px;
|
2026-03-25 14:11:54 +08:00
|
|
|
|
padding-bottom: 12px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
border-bottom: 1px solid var(--ccdi-line);
|
2026-03-25 14:26:58 +08:00
|
|
|
|
|
|
|
|
|
|
.toolbar-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
min-width: 104px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-25 14:11:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
::v-deep .el-table {
|
2026-04-03 11:00:33 +08:00
|
|
|
|
border: 1px solid var(--ccdi-border);
|
2026-03-25 14:11:54 +08:00
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
::v-deep .el-table th {
|
|
|
|
|
|
font-weight: 600;
|
2026-03-05 14:21:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 19:18:45 +08:00
|
|
|
|
// 上传弹窗样式
|
|
|
|
|
|
::v-deep .el-dialog__wrapper {
|
2026-03-12 10:46:40 +08:00
|
|
|
|
.upload-area,
|
|
|
|
|
|
.batch-upload-area {
|
2026-03-02 19:18:45 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 10:46:40 +08:00
|
|
|
|
.upload-area .el-upload,
|
|
|
|
|
|
.batch-upload-area .el-upload {
|
2026-03-02 19:18:45 +08:00
|
|
|
|
width: 100%;
|
2026-03-12 10:46:40 +08:00
|
|
|
|
}
|
2026-03-02 19:18:45 +08:00
|
|
|
|
|
2026-03-12 10:46:40 +08:00
|
|
|
|
.upload-area .el-upload-dragger,
|
|
|
|
|
|
.batch-upload-area .el-upload-dragger {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 200px;
|
2026-03-02 19:18:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-upload__tip {
|
|
|
|
|
|
margin-top: 8px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-muted);
|
2026-03-02 19:18:45 +08:00
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 10:46:40 +08:00
|
|
|
|
.pull-bank-info-form {
|
|
|
|
|
|
.pull-bank-field-tip {
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pull-bank-file-panel {
|
|
|
|
|
|
padding: 16px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
border: 1px solid var(--ccdi-border);
|
2026-03-12 10:46:40 +08:00
|
|
|
|
border-radius: 8px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
background: #f8fbfe;
|
2026-03-12 10:46:40 +08:00
|
|
|
|
|
|
|
|
|
|
.pull-bank-file-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pull-bank-file-tip {
|
|
|
|
|
|
font-size: 12px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-secondary);
|
2026-03-12 10:46:40 +08:00
|
|
|
|
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;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
border: 1px solid var(--ccdi-border);
|
2026-03-12 10:46:40 +08:00
|
|
|
|
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;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-primary);
|
2026-03-12 10:46:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 14:21:33 +08:00
|
|
|
|
// 批量上传弹窗样式
|
|
|
|
|
|
.batch-upload-area {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
::v-deep .el-upload {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
.el-upload-dragger {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 180px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.selected-files {
|
|
|
|
|
|
margin-top: 16px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
border: 1px solid var(--ccdi-border);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
max-height: 300px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
|
|
|
|
|
|
.files-header {
|
|
|
|
|
|
padding: 12px 16px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
background: #f7fafd;
|
|
|
|
|
|
border-bottom: 1px solid var(--ccdi-line);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-secondary);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.files-list {
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.file-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
transition: background 0.3s;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
2026-04-03 11:00:33 +08:00
|
|
|
|
background: #f7fafd;
|
2026-03-05 14:21:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
i {
|
|
|
|
|
|
font-size: 18px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-primary);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-name {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
font-size: 14px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-primary);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-size {
|
|
|
|
|
|
font-size: 12px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-muted);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
margin: 0 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
padding: 4px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-muted);
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
|
|
|
|
|
&:hover {
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: #b55252;
|
2026-03-05 14:21:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 17:58:39 +08:00
|
|
|
|
.parse-tip {
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
font-size: 12px;
|
2026-04-03 11:00:33 +08:00
|
|
|
|
color: var(--ccdi-text-muted);
|
2026-03-11 17:58:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 19:18:45 +08:00
|
|
|
|
// 响应式
|
|
|
|
|
|
@media (max-width: 1200px) {
|
2026-03-25 14:26:58 +08:00
|
|
|
|
.file-list-section .list-toolbar .toolbar-actions {
|
|
|
|
|
|
gap: 10px;
|
2026-03-02 19:18:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.upload-data-container {
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sidebar {
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-25 14:26:58 +08:00
|
|
|
|
.file-list-section .list-toolbar {
|
2026-03-02 19:18:45 +08:00
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
|
2026-03-25 14:26:58 +08:00
|
|
|
|
.toolbar-actions {
|
2026-03-25 14:11:54 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
}
|
2026-03-02 19:18:45 +08:00
|
|
|
|
}
|
2026-03-05 14:21:33 +08:00
|
|
|
|
|
2026-03-12 10:46:40 +08:00
|
|
|
|
.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;
|
|
|
|
|
|
}
|
2026-03-02 19:18:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|