smart-bid-web/src/views/analysis/components/AnalysisBidDetail.vue

548 lines
19 KiB
Vue
Raw Normal View History

2025-11-05 16:42:09 +08:00
<template>
<div class="analysis-detail-container">
<div class="content-header">
<el-button class="reset-btn" @click="handleClose">返回</el-button>
<el-button class="upload-btn" @click="handleUpload('ruleForm')">提交解析</el-button>
</div>
<div class="content-scrollable">
<el-card class="analysis-detail-card">
<template slot="header">
<div class="card-header">
<img src="@/assets/enterpriseLibrary/basic-info.png" alt="项目信息">
<h3>项目信息</h3>
</div>
</template>
<div class="analysis-detail-content">
<!-- 两列布局的字段 -->
<el-row :gutter="24" class="detail-row">
<el-col :span="12" class="detail-col">
<div class="detail-field">
<div class="field-label">项目名称</div>
<div class="field-value">{{ detailData.proName || '--' }}</div>
</div>
</el-col>
<el-col :span="12" class="detail-col">
<div class="detail-field">
<div class="field-label">项目编号</div>
<div class="field-value">{{ detailData.proCode || '--' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="24" class="detail-row">
<el-col :span="12" class="detail-col">
<div class="detail-field">
<div class="field-label">招标人</div>
<div class="field-value">{{ detailData.bidder || '--' }}</div>
</div>
</el-col>
<el-col :span="12" class="detail-col">
<div class="detail-field">
<div class="field-label">代理机构</div>
<div class="field-value">{{ detailData.agency || '--' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="24" class="detail-row">
<el-col :span="12" class="detail-col">
<div class="detail-field">
<div class="field-label">开标时间</div>
<div class="field-value">{{ detailData.openTime || '--' }}</div>
</div>
</el-col>
<el-col :span="12" class="detail-col">
<div class="detail-field">
<div class="field-label">开标方式</div>
<div class="field-value">{{ detailData.openMethod || '--' }}</div>
</div>
</el-col>
</el-row>
<!-- 项目简介 - 占满宽度 -->
<div class="detail-field full-width">
<div class="field-label">项目简介</div>
<div class="field-value description-value">{{ detailData.proDescription || '--' }}</div>
</div>
<!-- 招标文件 -->
<div class="detail-field full-width">
<div class="field-label">招标文件</div>
<div class="file-value">{{ detailData.proDescription || '--' }}</div>
</div>
</div>
</el-card>
<div class="table-container">
<el-row :gutter="12">
<el-col :span="17">
<TableModel :showSearch="false" :showOperation="false" :showRightTools="false"
ref="detailTableRef" :columnsList="detailColumnsList" :request-api="listAPI"
:sendParams="sendParams" :handleColWidth="180" :isRadioShow="true"
@radio-change="handleRadioChange" :indexNumShow="false">
<template slot="tableTitle">
<div class="card-header">
<img src="@/assets/enterpriseLibrary/basic-info.png" alt="标的信息">
<h3>标的信息</h3>
</div>
</template>
<template slot="handle" slot-scope="{ data }">
<el-button type="text" v-hasPermi="['enterpriseLibrary:analysis:edit']"
class="action-btn" style="#FE9400" @click="handleUpdate(data)">
修改
</el-button>
</template>
</TableModel>
</el-col>
<el-col :span="7">
<el-card class="upload-container">
<div class="card-header">
<img src="@/assets/enterpriseLibrary/basic-info.png" alt="附件上传">
<h3>附件上传</h3>
</div>
<div class="upload-title">
<span>请先选择需要解析的标的再上传标的及其相关附件附件进行解析</span>
</div>
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="110px"
label-position="top">
<el-form-item v-for="(item, index) in uploadType" :key="index" :label="item"
:prop="`fileList${index + 1}`">
<UploadMoreFile :fileList="form[`fileList${index + 1}`]"
@file-change="handleFileChange" @del-file="handleDelFile"
:type="`fileList${index + 1}`" :uploadType="defaultParams.uploadType"
:maxFileTips="defaultParams.maxFileTips"
:fileUploadRule="defaultParams.fileUploadRule"
:limitUploadNum="defaultParams.limitUploadNum" />
</el-form-item>
</el-form>
</el-card>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<script>
import { decryptWithSM4 } from '@/utils/sm'
import { listAPI, delDataAPI } from '@/api/analysis/analysis'
import { formLabel, detailColumnsList } from '../config'
import TableModel from '@/components/TableModel2'
import request from '@/utils/request'
import BidForm from './BidForm.vue'
import UploadMoreFile from '@/views/common/UploadMoreFile.vue'
// 默认参数
const defaultParams = {
fileUploadRule: {
fileUploadType: 'bidding',
fields_json: '',
suffix: 'analysis_database',
},
uploadType: 'pdf、doc、docx',
maxFileTips: '500MB',
limitUploadNum: 1,
}
export default {
name: 'AnalysisBidDetail',
components: {
TableModel,
BidForm,
UploadMoreFile
},
data() {
return {
formLabel,
detailColumnsList,
listAPI,
proId: '',
detailData: {},
sendParams: {
enterpriseId: 2
},
defaultParams,
selectedRow: {},
uploadType: ['标段文件', '标段文件2'],
form: {
delFileList: []
},
rules: {},
}
},
watch: {
uploadType: {
handler(newVal, oldVal) {
if (newVal && newVal.length > 0) {
// 立即清除验证
this.$refs.ruleForm && this.$refs.ruleForm.clearValidate()
if (oldVal && oldVal.length > 0) {
oldVal.forEach((item, index) => {
this.$delete(this.form, `fileList${index + 1}`)
this.$delete(this.rules, `fileList${index + 1}`)
})
}
newVal.forEach((item, index) => {
this.$set(this.form, `fileList${index + 1}`, [])
this.$set(this.rules, `fileList${index + 1}`, [
{
required: true,
message: `请上传${item}`,
trigger: ['change'],
},
])
})
this.$nextTick(() => {
this.$refs.ruleForm &&
this.$refs.ruleForm.clearValidate()
})
}
},
immediate: true,
},
},
created() {
this.proId = decryptWithSM4(this.$route.query.proId)
this.getDetail()
},
methods: {
// 获取详情数据
async getDetail() {
try {
// TODO: 根据实际API接口调整
const res = await request({
url: '/smartBid/analysis/detail',
method: 'GET',
params: { proId: this.proId }
})
// 或者使用 getDetailDataAPI需要根据实际接口调整
// const res = await getDetailDataAPI({ proId: this.proId })
if (res.code === 200) {
this.detailData = res.data || {}
} else {
this.$message.error(res.msg || '获取详情失败')
// 如果接口未实现,使用模拟数据
this.detailData = {
proName: '南方电网公司2025年电网基建工程第二批次施工公开招标项目',
proCode: 'CG2700022002003411',
bidder: '中建宏业建筑工程有限公司',
agency: '南方电网供应链集团有限公司',
openTime: '2025/06/02 09:00',
openMethod: '线上开标',
proDescription: '本项目为南方电网公司2025年电网基建工程第四批次施工公开招标涵盖云南、广东及深圳地区的多个标段工期从141至487日历天不等计划总投资额约...'
}
}
} catch (error) {
console.error('获取详情失败:', error)
// 如果接口未实现,使用模拟数据
this.detailData = {
proName: '南方电网公司2025年电网基建工程第二批次施工公开招标项目',
proCode: 'CG2700022002003411',
bidder: '中建宏业建筑工程有限公司',
agency: '南方电网供应链集团有限公司',
openTime: '2025/06/02 09:00',
openMethod: '线上开标',
proDescription: '本项目为南方电网公司2025年电网基建工程第四批次施工公开招标涵盖云南、广东及深圳地区的多个标段工期从141至487日历天不等计划总投资额约...'
}
}
},
// 返回
handleClose() {
const obj = { path: "/analysis" }
this.$tab.closeOpenPage(obj)
},
// 处理单选框变化
handleRadioChange(row, index) {
this.selectedRow = row
console.log('选中的行数据:', row)
},
// 文件变化
handleFileChange(file, fileName) {
console.log(file)
this.form[fileName] = file
this.$refs.ruleForm && this.$refs.ruleForm.clearValidate([fileName])
},
// 文件删除时触发
handleDelFile(file) {
console.log(file)
const delPath =
file?.response?.fileRes?.filePath || file?.filePath || null
if (delPath) {
this.form.delFileList.push(delPath)
}
},
validate(formName) {
return new Promise((resolve, reject) => {
this.$refs[formName].validate((valid) => {
if (valid) {
resolve(this.form) // 校验成功返回表单数据
} else {
reject(new Error('数据未填写完整'))
}
})
})
},
/**验证 */
async handleUpload(formName) {
try {
if (Object.keys(this.selectedRow).length === 0) {
this.$message.error('请选择要提交的标段')
return
}
const data = await this.validate(formName)
// 所有校验通过,组装完整数据
let formData = {
...data,
allFiles: [
...data.fileList.map((file) =>
JSON.parse(JSON.stringify(file)),
),
],
delFiles: [...data.delFileList],
}
let allFiles = formData.allFiles
.map((file) => {
return file?.response?.fileRes
? {
...file.response.fileRes,
}
: null
})
.filter((item) => item !== null)
formData.files = allFiles
delete formData.fileList
delete formData.delFileList
delete formData.allFiles
// 显示遮罩层
this.loading = this.$loading({
lock: true,
text: '数据提交中,请稍候...',
background: 'rgba(0,0,0,0.5)',
target:
this.$el.querySelector('.el-dialog') || document.body,
})
console.log('所有表单校验通过,完整数据:', formData)
/* const res = await this.saveData(formData)
if (res.code === 200) {
this.handleReuslt(res)
} else {
this.$modal.msgError(res.msg)
} */
} catch (error) {
} finally {
if (this.loading) {
this.loading.close()
}
}
},
// 保存接口
async saveData(formData) {
return new Promise((resolve, reject) => {
if (this.isAdd === 'add') {
// 新增
addDataAPI(formData)
.then((res) => {
resolve(res)
})
.catch((error) => {
reject(error)
})
} else {
// 修改
editDataAPI(formData)
.then((res) => {
resolve(res)
})
.catch((error) => {
reject(error)
})
}
})
},
}
}
</script>
<style scoped lang="scss">
.analysis-detail-container {
padding: 24px;
background: linear-gradient(180deg, #F1F6FF 20%, #E5EFFF 100%);
height: calc(100vh - 84px);
display: flex;
flex-direction: column;
overflow: hidden;
}
.content-scrollable {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding-right: 6px; // 为滚动条留出空间
// 自定义滚动条样式
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
&:hover {
background: rgba(0, 0, 0, 0.3);
}
}
}
.content-header {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 20px;
gap: 12px;
}
.reset-btn {
width: 98px;
height: 36px;
background: #FFFFFF;
box-shadow: 0px 4px 8px 0px rgba(76, 76, 76, 0.2);
border-radius: 4px;
border: none;
color: #606266;
font-weight: 600;
font-size: 14px;
transition: all 0.3s ease;
cursor: pointer;
&:hover {
background: #f5f7fa;
color: #409EFF;
box-shadow: 0px 6px 12px 0px rgba(76, 76, 76, 0.3);
}
}
.analysis-detail-card {
background: #fff;
border-radius: 8px;
box-shadow: 0px 4px 20px 0px rgba(31, 35, 55, 0.1);
border: none;
margin-top: 5px;
margin-bottom: 10px;
::v-deep .el-card__header {
padding: 20px 24px;
border-bottom: 1px solid #EBEEF5;
}
::v-deep .el-card__body {
padding: 24px;
}
}
.card-header {
display: flex;
align-items: center;
gap: 8px;
.header-icon {
font-size: 20px;
color: #409EFF;
}
h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #303133;
}
}
.analysis-detail-content {
.detail-row {
margin-bottom: 10px;
&:last-of-type {
margin-bottom: 0;
}
}
.detail-col {
margin-bottom: 10px;
}
.detail-field {
.field-label {
color: #424242;
font-size: 18px;
font-weight: 500;
margin-bottom: 8px;
line-height: 1.5;
}
.field-value {
background: #f5f7fa;
border-radius: 4px;
padding: 12px 16px;
color: #303133;
font-size: 14px;
line-height: 1.5;
min-height: 20px;
word-break: break-word;
}
&.full-width {
margin-bottom: 10px;
.description-value {
min-height: 80px;
white-space: pre-wrap;
word-wrap: break-word;
}
}
.file-value {
font-size: 14px;
line-height: 1.5;
color: #1f72ea;
}
}
}
.upload-container {
margin-top: 5px;
}
.upload-btn {
width: 98px;
height: 36px;
background: #1f72ea;
box-shadow: 0px 4px 8px 0px rgba(51, 135, 255, 0.5);
border-radius: 4px 4px 4px 4px;
color: #fff;
border: none;
font-size: 14px;
transition: all 0.3s;
&:hover {
background: #4a8bff;
box-shadow: 0px 6px 12px 0px rgba(51, 135, 255, 0.6);
}
}
.upload-title {
span {
font-size: 14px !important;
color: red;
}
}
</style>