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

542 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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.tenderer || '--' }}</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.bidOpeningTime || '--' }}</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.bidOpeningMethod || '--' }}</div>
</div>
</el-col>
</el-row>
<!-- 项目简介 - 占满宽度 -->
<div class="detail-field full-width">
<div class="field-label">项目简介</div>
<div class="field-value description-value">{{ detailData.proIntroduction || '--' }}</div>
</div>
<div class="detail-field full-width" v-for="item in detailData.compositions" :key="item.id">
<div class="field-label">{{ item.compositionFileName || '--' }}</div>
<div class="file-value" @click="viewFile(item)">{{ getFirstFileName(item) }}</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="getBidListAPI"
:sendParams="sendParams" :handleColWidth="180" :isRadioShow="true"
@radio-change="handleRadioChange" :indexNumShow="false" :isShowtableCardStyle="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>
<ViewFile v-if="dialogVisible" :file="fileData" @closeDialog="handleCloseDialog" />
</div>
</template>
<script>
import { decryptWithSM4 } from '@/utils/sm'
import { getProDetailAPI, getBidListAPI } from '@/api/analysis/analysis'
import { formLabel, detailColumnsList } from '../config'
import TableModel from '@/components/TableModel2'
import BidForm from './BidForm.vue'
import UploadMoreFile from '@/views/common/UploadMoreFile.vue'
import ViewFile from '@/views/common/ViewFile.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,
ViewFile
},
data() {
return {
formLabel,
detailColumnsList,
getBidListAPI,
proId: '',
detailData: {},
sendParams: {
proId: decryptWithSM4(this.$route.query.proId),
},
defaultParams,
selectedRow: {},
uploadType: ['标段文件', '标段文件2'],
form: {
delFileList: []
},
rules: {},
dialogVisible:false,
fileData:{}
}
},
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 {
const res = await getProDetailAPI({ proId: this.proId, queryType: 2 })
console.log('res:', res);
if (res.code === 200) {
this.detailData = res.data || {}
} else {
this.$message.error(res.msg || '获取详情失败')
}
} catch (error) {
console.error('获取详情失败:', error)
}
},
// 处理文件名
getFirstFileName(item) {
return item?.fileVoList?.[0]?.fileName || '--';
},
// 预览文件
viewFile(item) {
this.fileData = item.fileVoList[0]
console.log(this.fileData);
this.dialogVisible = true
},
handleCloseDialog() {
this.dialogVisible = false
this.fileData = {}
},
// 返回
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;
cursor: pointer;
}
}
}
.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>