366 lines
9.7 KiB
Vue
366 lines
9.7 KiB
Vue
<template>
|
||
<div class="file-container">
|
||
<div class="basic-info-title">
|
||
<div class="title-content">
|
||
<img src="@/assets/enterpriseLibrary/basic-info.png" alt="标段文件">
|
||
<span>{{ title }}</span>
|
||
</div>
|
||
<el-button
|
||
type="text"
|
||
icon="el-icon-plus"
|
||
class="add-tab-btn"
|
||
@click="handleAddTab"
|
||
:disabled="fileTabs.length >= 10"
|
||
>
|
||
添加文件
|
||
</el-button>
|
||
</div>
|
||
|
||
<el-tabs
|
||
v-model="activeTabIndex"
|
||
type="card"
|
||
class="file-tabs"
|
||
@tab-remove="handleRemoveTab"
|
||
v-on="isEditMode ? { 'tab-click': handleTabChange } : {}"
|
||
>
|
||
<el-tab-pane
|
||
v-for="(tab, index) in fileTabs"
|
||
:key="index"
|
||
:label="tab.formData.fileName || ('文件 ' + (index + 1))"
|
||
:name="index.toString()"
|
||
closable
|
||
>
|
||
<el-form
|
||
:model="tab.formData"
|
||
:rules="rules"
|
||
:ref="'sectionFileForm_' + index"
|
||
label-width="120px"
|
||
label-position="top"
|
||
class="file-form"
|
||
>
|
||
<el-row :gutter="24">
|
||
<el-col :span="8">
|
||
<el-form-item label="组成文件名称" prop="fileName">
|
||
<el-input
|
||
v-model="tab.formData.fileName"
|
||
placeholder="请输入"
|
||
maxlength="128"
|
||
@input="handleFileNameChange(index)"
|
||
@change="handleFileNameChange(index)"
|
||
></el-input>
|
||
</el-form-item>
|
||
</el-col>
|
||
|
||
<el-col :span="8">
|
||
<el-form-item label="文件类型" prop="compositionFileType">
|
||
<el-select
|
||
v-model="tab.formData.compositionFileType"
|
||
placeholder="请选择文件类型"
|
||
clearable
|
||
@change="handleTypeChange(index)"
|
||
>
|
||
<el-option
|
||
v-for="item in compositionFileTypeOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="24">
|
||
<el-col :span="8">
|
||
<el-form-item label="文件" prop="fileList">
|
||
<UploadFile
|
||
:fileList="tab.formData.fileList"
|
||
:fileUploadRule="fileUploadRule"
|
||
@file-change="(files, type) => handleFileChange(files, index)"
|
||
@del-file="(file) => handleDelFile(file, index)"
|
||
:uploadType="uploadType"
|
||
:maxFileTips="maxFileTips"
|
||
type="section_file"
|
||
:limitUploadNum="1"
|
||
:show-file-list="true"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import UploadFile from '@/views/common/UploadFile.vue'
|
||
|
||
export default {
|
||
name: 'SectionFile',
|
||
components: { UploadFile },
|
||
props: {
|
||
title: {
|
||
type: String,
|
||
default: '模板组成-标段/标包文件'
|
||
},
|
||
|
||
isEditMode: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
uploadType: 'doc、docx、pdf、xls、xlsx、wps',
|
||
maxFileTips: '50MB',
|
||
fileUploadRule: { fileUploadType: 'section_file', suffix: 'template' },
|
||
activeTabIndex: '0',
|
||
fileTabs: [
|
||
{
|
||
formData: {
|
||
compositionType: '2',
|
||
fileName: '',
|
||
compositionFileType: '',
|
||
fileList: [],
|
||
delFileList: []
|
||
}
|
||
}
|
||
],
|
||
rules: {
|
||
fileName: [
|
||
{ required: true, message: '请输入组成文件名称', trigger: 'blur' }
|
||
],
|
||
compositionFileType: [
|
||
{ required: true, message: '请选择文件类型', trigger: 'change' }
|
||
],
|
||
fileList: [
|
||
{ required: true, message: '请上传文件', trigger: 'change' }
|
||
]
|
||
},
|
||
compositionFileTypeOptions: []
|
||
}
|
||
},
|
||
|
||
created() {
|
||
this.loadFileTypeDict()
|
||
},
|
||
|
||
methods: {
|
||
loadFileTypeDict() {
|
||
this.getDicts('file_type').then(response => {
|
||
this.compositionFileTypeOptions = response.data.map(item => ({
|
||
label: item.dictLabel,
|
||
value: item.dictLabel
|
||
}))
|
||
}).catch(error => {
|
||
console.error('加载文件类型字典失败:', error)
|
||
})
|
||
},
|
||
|
||
handleAddTab() {
|
||
this.fileTabs.push({
|
||
formData: {
|
||
compositionType: '2',
|
||
fileName: '',
|
||
compositionFileType: '',
|
||
fileList: [],
|
||
delFileList: []
|
||
}
|
||
})
|
||
this.activeTabIndex = (this.fileTabs.length - 1).toString()
|
||
},
|
||
|
||
handleRemoveTab(index) {
|
||
if (this.fileTabs.length <= 1) {
|
||
this.$message.warning('至少保留一个文件')
|
||
return
|
||
}
|
||
this.fileTabs.splice(index, 1)
|
||
// 如果删除的是当前激活的tab,需要重新设置激活态
|
||
if (this.activeTabIndex === index.toString()) {
|
||
this.activeTabIndex = this.fileTabs.length > 0 ? (this.fileTabs.length - 1).toString() : '0';
|
||
}
|
||
},
|
||
|
||
handleFileChange(files, index) {
|
||
this.fileTabs[index].formData.fileList = files;
|
||
|
||
if (files.length > 0 && files[0].status === 'success') {
|
||
const currentTab = this.fileTabs[index];
|
||
files[0].compositionName = currentTab.formData.fileName;
|
||
files[0].compositionFileType = currentTab.formData.compositionFileType;
|
||
if (files[0].response) {
|
||
files[0].response.businessType = 'section_file';
|
||
}
|
||
}
|
||
},
|
||
|
||
handleDelFile(file, index) {
|
||
// 核心修改:统一从 response.fileRes.filePath 获取路径
|
||
const filePath = file.response?.fileRes?.filePath || file.filePath || '';
|
||
if (filePath) {
|
||
const delFileList = this.fileTabs[index].formData.delFileList;
|
||
if (!delFileList.includes(filePath)) {
|
||
delFileList.push(filePath)
|
||
}
|
||
}
|
||
},
|
||
|
||
handleFileNameChange(index) {
|
||
const currentTab = this.fileTabs[index];
|
||
const fileList = currentTab.formData.fileList;
|
||
if (fileList.length > 0) {
|
||
fileList[0].compositionName = currentTab.formData.fileName;
|
||
}
|
||
},
|
||
|
||
handleTypeChange(index) {
|
||
const currentTab = this.fileTabs[index];
|
||
const fileList = currentTab.formData.fileList;
|
||
if (fileList.length > 0) {
|
||
fileList[0].compositionFileType = currentTab.formData.compositionFileType;
|
||
}
|
||
},
|
||
|
||
// 切换Tab时不再执行任何操作
|
||
handleTabChange(tab, event) {
|
||
// 可以保留日志用于调试
|
||
const index = tab.index;
|
||
console.log(`切换到了第 ${index + 1} 个 tab`);
|
||
},
|
||
|
||
setFormData(sectionFiles) {
|
||
// 直接清空并重建 fileTabs
|
||
this.fileTabs = [];
|
||
|
||
if (!sectionFiles || sectionFiles.length === 0) {
|
||
this.fileTabs.push({
|
||
formData: {
|
||
compositionType: '2',
|
||
fileName: '',
|
||
compositionFileType: '',
|
||
fileList: [],
|
||
delFileList: []
|
||
}
|
||
})
|
||
this.activeTabIndex = '0';
|
||
return;
|
||
}
|
||
|
||
sectionFiles.forEach((file) => {
|
||
const formattedFile = {
|
||
name: file.fileName || file.name,
|
||
size: file.size,
|
||
status: 'success',
|
||
percentage: 100,
|
||
filePath: file.filePath,
|
||
lsFilePath: file.lsFilePath,
|
||
url: file.lsFilePath,
|
||
compositionName: file.compositionName || file.fileName || '',
|
||
compositionFileType: file.compositionFileType || '',
|
||
compositionType: '2',
|
||
businessType: 'section_file',
|
||
};
|
||
|
||
this.fileTabs.push({
|
||
formData: {
|
||
compositionType: '2',
|
||
fileName: formattedFile.compositionName,
|
||
compositionFileType: formattedFile.compositionFileType,
|
||
fileList: [formattedFile],
|
||
delFileList: []
|
||
}
|
||
})
|
||
})
|
||
|
||
this.activeTabIndex = sectionFiles.length > 0 ? '0' : '0';
|
||
},
|
||
|
||
validate() {
|
||
return new Promise(async (resolve, reject) => {
|
||
try {
|
||
const allFormData = []
|
||
for (let i = 0; i < this.fileTabs.length; i++) {
|
||
const formRef = this.$refs[`sectionFileForm_${i}`]?.[0]
|
||
if (!formRef) continue
|
||
|
||
await new Promise((validResolve, validReject) => {
|
||
formRef.validate((valid) => {
|
||
if (valid) {
|
||
validResolve()
|
||
} else {
|
||
this.activeTabIndex = i.toString()
|
||
validReject(new Error(`文件 ${i + 1} 信息填写不完整`))
|
||
}
|
||
})
|
||
})
|
||
allFormData.push({ ...this.fileTabs[i].formData })
|
||
}
|
||
resolve(allFormData)
|
||
} catch (error) {
|
||
reject(error)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
/* 样式部分保持不变 */
|
||
.file-container {
|
||
width: 100%;
|
||
}
|
||
|
||
.basic-info-title {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin: 10px 0 20px;
|
||
|
||
.title-content {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
span {
|
||
margin-left: 8px;
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
color: #1d2129;
|
||
}
|
||
|
||
img {
|
||
width: 20px;
|
||
height: 20px;
|
||
object-fit: contain;
|
||
}
|
||
}
|
||
|
||
.file-tabs {
|
||
margin-bottom: 20px;
|
||
width: 100%;
|
||
}
|
||
|
||
.add-tab-btn {
|
||
color: #409EFF;
|
||
border: 1px dashed #409EFF;
|
||
padding: 6px 12px;
|
||
border-radius: 4px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.file-form {
|
||
padding: 20px 10px 0;
|
||
}
|
||
|
||
::v-deep .el-tabs__item {
|
||
padding: 0 20px;
|
||
}
|
||
|
||
::v-deep .el-tabs__close-btn {
|
||
margin-left: 8px;
|
||
}
|
||
</style>
|