招标解析
This commit is contained in:
parent
16c61cdd1b
commit
ed078f2dc9
|
|
@ -1,64 +1,401 @@
|
|||
<!-- 标段解析 -->
|
||||
<template>
|
||||
<el-card class="analysis-container">
|
||||
<div class="back-container">
|
||||
<el-button type="default" size="small" @click="handleBack" class="back-btn">
|
||||
返回
|
||||
</el-button>
|
||||
<div class="analysis-bid-view">
|
||||
<!-- 头部导航 -->
|
||||
<AnalysisHeader @back="handleBack" />
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<div class="main-content">
|
||||
<!-- 左侧:解析结果面板 -->
|
||||
<div class="left-panel">
|
||||
<AnalysisResultPanel ref="resultPanel" :main-tabs="mainTabs" :default-main-tab="defaultMainTab"
|
||||
:default-sub-tab="defaultSubTab" @main-tab-change="handleMainTabChange"
|
||||
@sub-tab-change="handleSubTabChange">
|
||||
<!-- 项目信息 - 招标人 -->
|
||||
<template #project-tenderer>
|
||||
<div class="content-section">
|
||||
<div class="section-title">招标人信息</div>
|
||||
<div class="section-content">{{ analysisData.tenderer || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 基础信息 -->
|
||||
<template #project-basic>
|
||||
<div class="content-section">
|
||||
<div class="section-title">基础信息</div>
|
||||
<div class="section-content">{{ analysisData.basicInfo || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 代理机构 -->
|
||||
<template #project-agency>
|
||||
<div class="content-section">
|
||||
<div class="section-title">代理机构</div>
|
||||
<div class="section-content">{{ analysisData.agency || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 关键时间 -->
|
||||
<template #project-keyTime>
|
||||
<div class="content-section">
|
||||
<div class="section-title">关键时间</div>
|
||||
<div class="section-content">{{ analysisData.keyTime || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 联合体要求 -->
|
||||
<template #project-consortium>
|
||||
<div class="content-section">
|
||||
<div class="section-title">联合体要求</div>
|
||||
<div class="section-content">{{ analysisData.consortium || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 分包要求 -->
|
||||
<template #project-subcontract>
|
||||
<div class="content-section">
|
||||
<div class="section-title">分包要求</div>
|
||||
<div class="section-content">{{ analysisData.subcontract || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 最高限价 -->
|
||||
<template #project-maxPrice>
|
||||
<div class="content-section">
|
||||
<div class="section-title">最高限价</div>
|
||||
<div class="section-content">{{ analysisData.maxPrice || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 项目信息 - 响应和偏差 -->
|
||||
<template #project-response>
|
||||
<div class="content-section">
|
||||
<div class="section-title">响应和偏差要求</div>
|
||||
<div class="section-content">{{ analysisData.response || '招标文件无此内容' }}</div>
|
||||
</div>
|
||||
<div class="content-section">
|
||||
<div class="section-title">澄清要求</div>
|
||||
<div class="section-content">{{ analysisData.clarification || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 标段信息 -->
|
||||
<template #bid>
|
||||
<div class="content-section">
|
||||
<div class="section-title">标段信息</div>
|
||||
<div class="section-content">{{ analysisData.bidInfo || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 保证金 - 投标保证金 -->
|
||||
<template #deposit-bidDeposit>
|
||||
<div class="content-section">
|
||||
<div class="section-title">投标保证金金额</div>
|
||||
<div class="section-content">{{ analysisData.bidDepositAmount || '--' }}</div>
|
||||
</div>
|
||||
<div class="content-section">
|
||||
<div class="section-title">投标保证金形式</div>
|
||||
<div class="section-content">{{ analysisData.bidDepositForm || '--' }}</div>
|
||||
</div>
|
||||
<div class="content-section">
|
||||
<div class="section-title">投标保证金提交期限</div>
|
||||
<div class="section-content">{{ analysisData.bidDepositSubmitDeadline || '--' }}</div>
|
||||
</div>
|
||||
<div class="content-section">
|
||||
<div class="section-title">投标保证金退还期限</div>
|
||||
<div class="section-content">{{ analysisData.bidDepositRefundDeadline || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 保证金 - 履约保证金 -->
|
||||
<template #deposit-performanceDeposit>
|
||||
<div class="content-section">
|
||||
<div class="section-title">履约保证金</div>
|
||||
<div class="section-content">{{ analysisData.performanceDeposit || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 其他标签页内容 -->
|
||||
<template #qualification>
|
||||
<div class="content-section">
|
||||
<div class="section-title">资格要求</div>
|
||||
<div class="section-content">{{ analysisData.qualification || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #performance>
|
||||
<div class="content-section">
|
||||
<div class="section-title">业绩要求</div>
|
||||
<div class="section-content">{{ analysisData.performance || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #finance>
|
||||
<div class="content-section">
|
||||
<div class="section-title">财务要求</div>
|
||||
<div class="section-content">{{ analysisData.finance || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #personnel>
|
||||
<div class="content-section">
|
||||
<div class="section-title">人员要求</div>
|
||||
<div class="section-content">{{ analysisData.personnel || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #evaluation>
|
||||
<div class="content-section">
|
||||
<div class="section-title">开评定标要求</div>
|
||||
<div class="section-content">{{ analysisData.evaluation || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #rejection>
|
||||
<div class="content-section">
|
||||
<div class="section-title">废标项</div>
|
||||
<div class="section-content">{{ analysisData.rejection || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #document>
|
||||
<div class="content-section">
|
||||
<div class="section-title">投标文件要求</div>
|
||||
<div class="section-content">{{ analysisData.document || '--' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</AnalysisResultPanel>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:文档预览面板 -->
|
||||
<div class="right-panel">
|
||||
<DocumentPreviewPanel :tender-document-url="tenderDocumentUrl"
|
||||
:tender-document-title="tenderDocumentTitle" :tender-document-key="tenderDocumentKey"
|
||||
:bid-document-url="bidDocumentUrl" :bid-document-title="bidDocumentTitle"
|
||||
:bid-document-key="bidDocumentKey" @document-switch="handleDocumentSwitch" @search="handleSearch" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { decryptWithSM4, encryptWithSM4 } from '@/utils/sm'
|
||||
import AnalysisHeader from './child/AnalysisHeader.vue'
|
||||
import AnalysisResultPanel from './child/AnalysisResultPanel.vue'
|
||||
import DocumentPreviewPanel from './child/DocumentPreviewPanel.vue'
|
||||
// import { getBidDetailAPI } from '@/api/analysis/analysis'
|
||||
|
||||
export default {
|
||||
name: 'AnalysisBidView',
|
||||
components: {
|
||||
AnalysisHeader,
|
||||
AnalysisResultPanel,
|
||||
DocumentPreviewPanel
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
proId: decryptWithSM4(this.$route.query.proId),
|
||||
bidId: decryptWithSM4(this.$route.query.bidId),
|
||||
defaultMainTab: 'project',
|
||||
defaultSubTab: 'response',
|
||||
analysisData: {},
|
||||
tenderDocumentUrl: '',
|
||||
tenderDocumentTitle: '',
|
||||
tenderDocumentKey: '',
|
||||
bidDocumentUrl: '',
|
||||
bidDocumentTitle: '',
|
||||
bidDocumentKey: '',
|
||||
mainTabs: [
|
||||
{
|
||||
name: 'project',
|
||||
label: '项目信息',
|
||||
subTabs: [
|
||||
{ name: 'tenderer', label: '招标人' },
|
||||
{ name: 'basic', label: '基础信息' },
|
||||
{ name: 'agency', label: '代理机构' },
|
||||
{ name: 'keyTime', label: '关键时间' },
|
||||
{ name: 'consortium', label: '联合体要求' },
|
||||
{ name: 'subcontract', label: '分包要求' },
|
||||
{ name: 'maxPrice', label: '最高限价' },
|
||||
{ name: 'response', label: '响应和偏差' },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'bid',
|
||||
label: '标段信息',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'deposit',
|
||||
label: '保证金',
|
||||
subTabs: [
|
||||
{ name: 'bidDeposit', label: '投标保证金' },
|
||||
{ name: 'performanceDeposit', label: '履约保证金' },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'qualification',
|
||||
label: '资格要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'performance',
|
||||
label: '业绩要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'finance',
|
||||
label: '财务要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'personnel',
|
||||
label: '人员要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'evaluation',
|
||||
label: '开评定标要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'rejection',
|
||||
label: '废标项',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'document',
|
||||
label: '投标文件要求',
|
||||
subTabs: []
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getBidDetail()
|
||||
},
|
||||
methods: {
|
||||
handleBack() {
|
||||
const obj = { name: "AnalysisBidIndex", query: { proId: encryptWithSM4(this.proId) } }
|
||||
this.$tab.closeOpenPage(obj)
|
||||
},
|
||||
// 获取标段详情
|
||||
async getBidDetail() {
|
||||
try {
|
||||
// TODO: 根据实际API调整,获取标段解析结果数据
|
||||
// const res = await getBidDetailAPI({ proId: this.proId, bidId: this.bidId })
|
||||
// if (res.code === 200) {
|
||||
// this.analysisData = res.data || {}
|
||||
// this.initDocumentData(res.data)
|
||||
// }
|
||||
|
||||
// 临时模拟数据
|
||||
this.analysisData = {
|
||||
tenderer: '中国南方电网有限责任公司',
|
||||
basic: '基础信息内容',
|
||||
agency: '代理机构信息',
|
||||
keyTime: '关键时间信息',
|
||||
consortium: '联合体要求内容',
|
||||
subcontract: '分包要求内容',
|
||||
maxPrice: '最高限价信息',
|
||||
response: '招标文件无此内容',
|
||||
clarification: '澄清要求内容...',
|
||||
bidInfo: '标段信息内容',
|
||||
bidDepositAmount: '投标保证金金额',
|
||||
bidDepositForm: '投标保证金形式',
|
||||
bidDepositSubmitDeadline: '投标截止日前一天',
|
||||
bidDepositRefundDeadline: '退还期限信息',
|
||||
performanceDeposit: '履约保证金信息',
|
||||
qualification: '资格要求内容',
|
||||
performance: '业绩要求内容',
|
||||
finance: '财务要求内容',
|
||||
personnel: '人员要求内容',
|
||||
evaluation: '开评定标要求内容',
|
||||
rejection: '废标项内容',
|
||||
document: '投标文件要求内容'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取标段详情失败:', error)
|
||||
}
|
||||
},
|
||||
// 初始化文档数据
|
||||
initDocumentData(data) {
|
||||
if (data) {
|
||||
this.tenderDocumentUrl = data.tenderDocumentUrl || ''
|
||||
this.tenderDocumentTitle = data.tenderDocumentTitle || ''
|
||||
this.tenderDocumentKey = data.tenderDocumentKey || ''
|
||||
this.bidDocumentUrl = data.bidDocumentUrl || ''
|
||||
this.bidDocumentTitle = data.bidDocumentTitle || ''
|
||||
this.bidDocumentKey = data.bidDocumentKey || ''
|
||||
}
|
||||
},
|
||||
handleMainTabChange(mainTab, subTab) {
|
||||
console.log('主标签切换:', mainTab, subTab)
|
||||
},
|
||||
handleSubTabChange(mainTab, subTab) {
|
||||
console.log('子标签切换:', mainTab, subTab)
|
||||
},
|
||||
handleDocumentSwitch(type) {
|
||||
console.log('文档切换:', type)
|
||||
},
|
||||
handleSearch() {
|
||||
console.log('搜索')
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.analysis-container {
|
||||
.analysis-bid-view {
|
||||
height: calc(100vh - 84px);
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, #F1F6FF 20%, #E5EFFF 100%);
|
||||
}
|
||||
|
||||
.back-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 20px;
|
||||
flex-direction: column;
|
||||
background: linear-gradient(180deg, #F1F6FF 20%, #E5EFFF 100%);
|
||||
overflow: hidden;
|
||||
|
||||
.back-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: #666;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 0 20px 20px 20px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
color: #409EFF;
|
||||
box-shadow: 0px 6px 12px 0px rgba(76, 76, 76, 0.3);
|
||||
.left-panel {
|
||||
flex: 0 0 50%;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 0 0 50%;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 1.8;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
background: #F5F7FA;
|
||||
padding: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<!-- 分析页面头部 -->
|
||||
<template>
|
||||
<div class="analysis-header">
|
||||
<div class="header-right">
|
||||
<el-button
|
||||
class="back-btn"
|
||||
@click="handleBack"
|
||||
>
|
||||
返回
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AnalysisHeader',
|
||||
methods: {
|
||||
handleBack() {
|
||||
this.$emit('back')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.analysis-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
background: transparent;
|
||||
border-bottom: none;
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.back-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-size: 14px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f5f7fa;
|
||||
color: #409EFF;
|
||||
box-shadow: 0px 6px 12px 0px rgba(76, 76, 76, 0.3);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0px 2px 4px 0px rgba(76, 76, 76, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
<!-- 解析结果面板 -->
|
||||
<template>
|
||||
<div class="analysis-result-panel">
|
||||
<div class="panel-header">
|
||||
<h3>解析结果</h3>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<!-- 主标签页 -->
|
||||
<el-tabs v-model="activeMainTab" @tab-click="handleMainTabClick" class="main-tabs">
|
||||
<el-tab-pane
|
||||
v-for="tab in mainTabs"
|
||||
:key="tab.name"
|
||||
:label="tab.label"
|
||||
:name="tab.name"
|
||||
>
|
||||
<!-- 子标签页 -->
|
||||
<el-tabs
|
||||
v-if="tab.subTabs && tab.subTabs.length > 0"
|
||||
v-model="activeSubTab"
|
||||
@tab-click="handleSubTabClick"
|
||||
class="sub-tabs"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="subTab in tab.subTabs"
|
||||
:key="subTab.name"
|
||||
:label="subTab.label"
|
||||
:name="subTab.name"
|
||||
>
|
||||
<div class="tab-content">
|
||||
<slot :name="`${tab.name}-${subTab.name}`">
|
||||
<div class="empty-content">{{ subTab.label }}内容</div>
|
||||
</slot>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- 没有子标签页的内容 -->
|
||||
<div v-else class="tab-content">
|
||||
<slot :name="tab.name">
|
||||
<div class="empty-content">{{ tab.label }}内容</div>
|
||||
</slot>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AnalysisResultPanel',
|
||||
props: {
|
||||
// 主标签页数据
|
||||
mainTabs: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{
|
||||
name: 'project',
|
||||
label: '项目信息',
|
||||
subTabs: [
|
||||
{ name: 'tenderer', label: '招标人' },
|
||||
{ name: 'basic', label: '基础信息' },
|
||||
{ name: 'agency', label: '代理机构' },
|
||||
{ name: 'keyTime', label: '关键时间' },
|
||||
{ name: 'consortium', label: '联合体要求' },
|
||||
{ name: 'subcontract', label: '分包要求' },
|
||||
{ name: 'maxPrice', label: '最高限价' },
|
||||
{ name: 'response', label: '响应和偏差' },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'bid',
|
||||
label: '标段信息',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'deposit',
|
||||
label: '保证金',
|
||||
subTabs: [
|
||||
{ name: 'bidDeposit', label: '投标保证金' },
|
||||
{ name: 'performanceDeposit', label: '履约保证金' },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'qualification',
|
||||
label: '资格要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'performance',
|
||||
label: '业绩要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'finance',
|
||||
label: '财务要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'personnel',
|
||||
label: '人员要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'evaluation',
|
||||
label: '开评定标要求',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'rejection',
|
||||
label: '废标项',
|
||||
subTabs: []
|
||||
},
|
||||
{
|
||||
name: 'document',
|
||||
label: '投标文件要求',
|
||||
subTabs: []
|
||||
},
|
||||
]
|
||||
},
|
||||
// 默认激活的主标签
|
||||
defaultMainTab: {
|
||||
type: String,
|
||||
default: 'project'
|
||||
},
|
||||
// 默认激活的子标签
|
||||
defaultSubTab: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeMainTab: this.defaultMainTab,
|
||||
activeSubTab: this.defaultSubTab || this.getDefaultSubTab(this.defaultMainTab),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultMainTab(newVal) {
|
||||
this.activeMainTab = newVal
|
||||
this.activeSubTab = this.getDefaultSubTab(newVal)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMainTabClick(tab) {
|
||||
// 切换主标签时,重置子标签为第一个
|
||||
const currentMainTab = this.mainTabs.find(t => t.name === tab.name)
|
||||
if (currentMainTab && currentMainTab.subTabs && currentMainTab.subTabs.length > 0) {
|
||||
this.activeSubTab = currentMainTab.subTabs[0].name
|
||||
} else {
|
||||
this.activeSubTab = ''
|
||||
}
|
||||
this.$emit('main-tab-change', tab.name, this.activeSubTab)
|
||||
},
|
||||
handleSubTabClick(tab) {
|
||||
this.$emit('sub-tab-change', this.activeMainTab, tab.name)
|
||||
},
|
||||
getDefaultSubTab(mainTabName) {
|
||||
const mainTab = this.mainTabs.find(t => t.name === mainTabName)
|
||||
if (mainTab && mainTab.subTabs && mainTab.subTabs.length > 0) {
|
||||
return mainTab.subTabs[0].name
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.analysis-result-panel {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
background: #fff;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
::v-deep .main-tabs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-tabs__header {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
background: #FAFAFA;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&.is-active {
|
||||
color: #409EFF;
|
||||
border-bottom-color: #409EFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .sub-tabs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-tabs__header {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
background: #F5F7FA;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 16px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&.is-active {
|
||||
color: #409EFF;
|
||||
border-bottom-color: #409EFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 20px;
|
||||
|
||||
.empty-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
padding: 40px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
<!-- 文档预览面板 -->
|
||||
<template>
|
||||
<div class="document-preview-panel">
|
||||
<div class="panel-header">
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-search"
|
||||
class="search-btn"
|
||||
@click="handleSearch"
|
||||
></el-button>
|
||||
</div>
|
||||
<div class="document-switch">
|
||||
<el-button
|
||||
:type="activeDocType === 'tender' ? 'primary' : 'default'"
|
||||
class="doc-btn"
|
||||
@click="switchDocument('tender')"
|
||||
>
|
||||
招标文件
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="activeDocType === 'bid' ? 'primary' : 'default'"
|
||||
class="doc-btn"
|
||||
@click="switchDocument('bid')"
|
||||
>
|
||||
标段文件
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<div v-if="documentUrl" class="document-viewer">
|
||||
<OnlyOfficeViewer
|
||||
v-if="showOnlyOffice"
|
||||
:document-url="documentUrl"
|
||||
:document-title="documentTitle"
|
||||
:document-key="documentKey"
|
||||
:mode="viewMode"
|
||||
:type="viewType"
|
||||
@document-ready="handleDocumentReady"
|
||||
@app-ready="handleAppReady"
|
||||
@error="handleError"
|
||||
/>
|
||||
<div v-else class="document-placeholder">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-document placeholder-icon"></i>
|
||||
<p class="placeholder-text">文档预览</p>
|
||||
<p class="placeholder-desc">{{ documentTitle || '暂无文档' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-state">
|
||||
<i class="el-icon-document-empty empty-icon"></i>
|
||||
<p class="empty-text">暂无文档</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OnlyOfficeViewer from '@/views/common/OnlyOfficeViewer.vue'
|
||||
|
||||
export default {
|
||||
name: 'DocumentPreviewPanel',
|
||||
components: {
|
||||
OnlyOfficeViewer
|
||||
},
|
||||
props: {
|
||||
// 招标文件URL
|
||||
tenderDocumentUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 招标文件标题
|
||||
tenderDocumentTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 招标文件Key
|
||||
tenderDocumentKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标段文件URL
|
||||
bidDocumentUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标段文件标题
|
||||
bidDocumentTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标段文件Key
|
||||
bidDocumentKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 查看模式
|
||||
viewMode: {
|
||||
type: String,
|
||||
default: 'view'
|
||||
},
|
||||
// 查看类型
|
||||
viewType: {
|
||||
type: String,
|
||||
default: 'desktop'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeDocType: 'tender', // 'tender' 或 'bid'
|
||||
showOnlyOffice: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
documentUrl() {
|
||||
return this.activeDocType === 'tender' ? this.tenderDocumentUrl : this.bidDocumentUrl
|
||||
},
|
||||
documentTitle() {
|
||||
return this.activeDocType === 'tender' ? this.tenderDocumentTitle : this.bidDocumentTitle
|
||||
},
|
||||
documentKey() {
|
||||
return this.activeDocType === 'tender' ? this.tenderDocumentKey : this.bidDocumentKey
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
documentUrl(newVal) {
|
||||
if (newVal) {
|
||||
this.showOnlyOffice = true
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
switchDocument(type) {
|
||||
this.activeDocType = type
|
||||
this.showOnlyOffice = false
|
||||
this.$nextTick(() => {
|
||||
if (this.documentUrl) {
|
||||
this.showOnlyOffice = true
|
||||
}
|
||||
})
|
||||
this.$emit('document-switch', type)
|
||||
},
|
||||
handleSearch() {
|
||||
this.$emit('search')
|
||||
},
|
||||
handleDocumentReady() {
|
||||
this.$emit('document-ready')
|
||||
},
|
||||
handleAppReady() {
|
||||
this.$emit('app-ready')
|
||||
},
|
||||
handleError(error) {
|
||||
this.$emit('error', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.document-preview-panel {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
background: #fff;
|
||||
|
||||
.header-actions {
|
||||
.search-btn {
|
||||
padding: 8px;
|
||||
font-size: 18px;
|
||||
color: #606266;
|
||||
|
||||
&:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.document-switch {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.doc-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.el-button--primary {
|
||||
background: #409EFF;
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
&.el-button--default {
|
||||
background: #F5F7FA;
|
||||
border-color: #DCDFE6;
|
||||
color: #606266;
|
||||
|
||||
&:hover {
|
||||
background: #ECF5FF;
|
||||
border-color: #B3D8FF;
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.document-viewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
::v-deep .onlyoffice-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.document-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #FAFAFA;
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 64px;
|
||||
color: #C0C4CC;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.placeholder-desc {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #FAFAFA;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64px;
|
||||
color: #C0C4CC;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue