yuanxingsheji/项目管理/市场商务管理/招采业务.html

2722 lines
115 KiB
HTML
Raw Normal View History

2025-07-25 17:57:25 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目管理 - 招采业务</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<style>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
color: #333;
background-color: #f0f2f5;
}
.app-container {
display: flex;
min-height: 100vh;
overflow: hidden;
}
/* 侧边栏样式 */
.sidebar {
width: 220px;
background-color: #304156;
color: #fff;
transition: width 0.3s;
flex-shrink: 0;
overflow-y: auto;
}
.sidebar.collapsed {
width: 64px;
}
.main-content {
flex: 1;
padding: 20px;
overflow-y: auto;
transition: margin-left 0.3s;
}
.sidebar.collapsed + .main-content {
margin-left: -156px;
}
.logo {
padding: 10px 0;
text-align: center;
position: relative;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.logo h2 {
font-size: 18px;
margin: 5px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 10px;
}
.toggle-sidebar {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
color: #fff;
}
/* 菜单项样式 */
.el-menu {
border-right: none;
background-color: transparent;
}
.el-menu-item, .el-submenu__title {
color: #fff;
height: 46px;
line-height: 46px;
}
.el-menu-item:hover, .el-submenu__title:hover {
background-color: rgba(0, 0, 0, 0.1) !important;
}
.el-menu-item.is-active {
background-color: rgba(0, 0, 0, 0.2) !important;
color: #409EFF !important;
}
.el-submenu .el-menu-item {
min-width: 0 !important;
padding-left: 50px !important;
background-color: #1f2d3d !important;
}
.el-submenu .el-menu-item:hover {
background-color: rgba(0, 0, 0, 0.1) !important;
}
.el-submenu__title {
padding-left: 20px !important;
}
.el-menu-item [class^=el-icon-], .el-submenu [class^=el-icon-] {
margin-right: 10px;
}
/* 主内容区样式 */
.module-title {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.card-container {
background: #fff;
padding: 20px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
margin-bottom: 20px;
}
/* 表格操作按钮 */
.table-actions {
margin-bottom: 15px;
display: flex;
justify-content: space-between;
}
/* 分页样式 */
.pagination-container {
margin-top: 20px;
text-align: right;
}
/* 表单样式 */
.form-container {
max-width: 1100px;
margin: 0 auto;
}
/* 响应式调整 */
@media (max-width: 768px) {
.sidebar {
position: fixed;
z-index: 1000;
height: 100vh;
}
.sidebar.collapsed + .main-content {
margin-left: 0;
}
.main-content {
margin-left: 220px;
}
.sidebar.collapsed {
width: 0;
overflow: hidden;
}
}
/* 新增样式 */
.file-link {
display: flex;
align-items: center;
}
.file-link i {
margin-right: 5px;
}
.status-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
background-color: #f0f0f0;
}
.status-badge.preparing {
background-color: #fdf6ec;
color: #e6a23c;
}
.status-badge.bidding {
background-color: #ecf5ff;
color: #409eff;
}
.status-badge.won {
background-color: #f0f9eb;
color: #67c23a;
}
.status-badge.lost {
background-color: #fef0f0;
color: #f56c6c;
}
.file-actions {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 进度详情样式 */
.progress-timeline {
margin-top: 20px;
}
.progress-step {
margin-bottom: 30px;
position: relative;
padding-left: 30px;
}
.progress-step:last-child {
margin-bottom: 0;
}
.progress-step::before {
content: '';
position: absolute;
left: 6px;
top: 0;
height: 100%;
width: 2px;
background-color: #e4e7ed;
}
.progress-step.active::before {
background-color: #409eff;
}
.progress-step.completed::before {
background-color: #67c23a;
}
.progress-step-icon {
position: absolute;
left: 0;
width: 14px;
height: 14px;
border-radius: 50%;
background-color: #e4e7ed;
z-index: 1;
}
.progress-step.active .progress-step-icon {
background-color: #409eff;
}
.progress-step.completed .progress-step-icon {
background-color: #67c23a;
}
.progress-step-title {
font-weight: bold;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.progress-step-date {
color: #909399;
font-size: 12px;
margin-left: 10px;
}
.progress-step-content {
margin-left: 20px;
}
.progress-step-files {
margin-top: 10px;
}
.progress-step-file {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.progress-step-file i {
margin-right: 5px;
color: #409eff;
}
</style>
</head>
<body>
<div id="app" class="app-container">
<!-- 侧边栏导航 -->
<div class="sidebar" :class="{collapsed: isCollapse}">
<div class="logo">
<h2>项目管理</h2>
<div class="toggle-sidebar" @click="toggleSidebar">
<i :class="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"></i>
</div>
</div>
<el-menu
:default-active="activeModule"
class="el-menu-vertical"
:collapse="isCollapse"
:collapse-transition="false"
background-color="#304156"
text-color="#fff"
active-text-color="#409EFF"
>
<!-- 市场商务管理 -->
<el-submenu index="market">
<template slot="title">
<i class="el-icon-shopping-bag-1"></i>
<span>市场商务管理</span>
</template>
<!-- 项目来源 -->
<el-submenu index="project-source">
<template slot="title">项目来源</template>
<!-- 招采业务 -->
<el-menu-item index="bidding" @click="switchModule('bidding')">招采业务</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 招采业务 -->
<div v-if="activeModule === 'bidding'">
<h2 class="module-title">招采业务</h2>
<div class="card-container">
<el-tabs v-model="biddingTab" type="card">
<el-tab-pane label="信息检索" name="search">
<div class="table-actions">
<el-input
placeholder="搜索招标项目"
v-model="biddingSearch.keyword"
clearable
style="width: 300px;"
@keyup.enter.native="searchBiddingInfo"
>
<el-button slot="append" icon="el-icon-search" @click="searchBiddingInfo"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-video-play" @click="startCrawler">开始爬虫</el-button>
<el-button type="primary" icon="el-icon-refresh" @click="refreshBiddingData">刷新数据</el-button>
<el-button type="success" icon="el-icon-download" @click="exportBiddingData">导出数据</el-button>
<el-button type="warning" icon="el-icon-setting" @click="showCrawlerSettings">爬虫设置</el-button>
</div>
</div>
<el-table :data="biddingData" border style="width: 100%">
<el-table-column prop="title" label="招标项目" width="300"></el-table-column>
<el-table-column prop="publisher" label="招标单位" width="180"></el-table-column>
<el-table-column prop="budget" label="预算金额" width="120" align="right"></el-table-column>
<el-table-column prop="deadline" label="截止时间" width="180"></el-table-column>
<el-table-column prop="crawlTime" label="爬取时间" width="180"></el-table-column>
<el-table-column prop="source" label="来源平台" width="120">
<template slot-scope="scope">
<el-link type="primary" :href="scope.row.sourceUrl" target="_blank" v-if="scope.row.sourceUrl">{{ scope.row.source }}</el-link>
<span v-else>{{ scope.row.source }}</span>
</template>
</el-table-column>
<el-table-column prop="documents" label="招标文件" width="150">
<template slot-scope="scope">
<div v-if="scope.row.documents && scope.row.documents.length > 0" class="file-actions">
<el-tag v-for="doc in scope.row.documents" :key="doc.name" size="mini" style="margin-right: 5px;">
<el-link :href="doc.url" target="_blank">{{ doc.name }}</el-link>
</el-tag>
</div>
<el-upload
class="upload-demo"
action=""
:on-change="(file, fileList) => handleBiddingFileUpload(file, fileList, scope.row)"
:auto-upload="false"
:show-file-list="false"
>
<el-button size="mini" type="text">上传文件</el-button>
</el-upload>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '待报名' ? 'warning' : 'success'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewBiddingDetail(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="markAsRegistered(scope.row)"
:disabled="scope.row.status === '已报名'">{{ scope.row.status === '已报名' ? '已报名' : '报名' }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleBiddingSizeChange"
@current-change="handleBiddingCurrentChange"
:current-page="biddingSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="biddingSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="biddingTotal">
</el-pagination>
</div>
<!-- 招标详情对话框 -->
<el-dialog title="招标详情" :visible.sync="biddingDetailVisible" width="70%">
<div v-if="currentBidding">
<el-descriptions :column="2" border>
<el-descriptions-item label="项目名称">{{ currentBidding.title }}</el-descriptions-item>
<el-descriptions-item label="招标单位">{{ currentBidding.publisher }}</el-descriptions-item>
<el-descriptions-item label="预算金额">{{ currentBidding.budget }}</el-descriptions-item>
<el-descriptions-item label="截止时间">{{ currentBidding.deadline }}</el-descriptions-item>
<el-descriptions-item label="爬取时间">{{ currentBidding.crawlTime }}</el-descriptions-item>
<el-descriptions-item label="项目地点">{{ currentBidding.location }}</el-descriptions-item>
<el-descriptions-item label="项目周期">{{ currentBidding.duration }}</el-descriptions-item>
<el-descriptions-item label="招标范围" :span="2">{{ currentBidding.scope }}</el-descriptions-item>
<el-descriptions-item label="资质要求" :span="2">{{ currentBidding.qualification }}</el-descriptions-item>
<el-descriptions-item label="来源平台" :span="2">
<el-link type="primary" :href="currentBidding.sourceUrl" target="_blank">{{ currentBidding.source }}</el-link>
</el-descriptions-item>
<el-descriptions-item label="招标文件" :span="2">
<div v-if="currentBidding.documents && currentBidding.documents.length > 0" class="file-actions">
<div v-for="doc in currentBidding.documents" :key="doc.name" class="file-link">
<i class="el-icon-document"></i>
<el-link :href="doc.url" target="_blank">{{ doc.name }}</el-link>
</div>
</div>
<el-upload
class="upload-demo"
action=""
:on-change="(file, fileList) => handleBiddingFileUpload(file, fileList, currentBidding)"
:auto-upload="false"
:show-file-list="false"
>
<el-button size="mini" type="primary">上传招标文件</el-button>
</el-upload>
</el-descriptions-item>
</el-descriptions>
<div style="margin-top: 20px;">
<h3>招标内容</h3>
<div v-html="currentBidding.content" style="border: 1px solid #ebeef5; padding: 10px; border-radius: 4px;"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="biddingDetailVisible = false">关闭</el-button>
<el-button type="primary" @click="goToBiddingResponse">投标响应</el-button>
</span>
</el-dialog>
<!-- 爬虫设置对话框 -->
<el-dialog title="爬虫设置" :visible.sync="crawlerSettingsVisible" width="50%">
<el-form :model="crawlerSettings" label-width="120px">
<el-form-item label="爬取频率">
<el-select v-model="crawlerSettings.frequency" style="width: 200px;">
<el-option label="每小时" value="hourly"></el-option>
<el-option label="每天" value="daily"></el-option>
<el-option label="每周" value="weekly"></el-option>
</el-select>
</el-form-item>
<el-form-item label="关键词过滤">
<el-tag
v-for="tag in crawlerSettings.keywords"
:key="tag"
closable
@close="removeKeyword(tag)"
style="margin-right: 10px;"
>
{{ tag }}
</el-tag>
<el-input
v-model="newKeyword"
size="small"
style="width: 150px;"
@keyup.enter.native="addKeyword"
>
<el-button slot="append" icon="el-icon-plus" @click="addKeyword"></el-button>
</el-input>
</el-form-item>
<el-form-item label="来源平台">
<el-checkbox-group v-model="crawlerSettings.platforms">
<el-checkbox label="中国招标投标公共服务平台"></el-checkbox>
<el-checkbox label="各省市公共资源交易中心"></el-checkbox>
<el-checkbox label="企业自有招标平台"></el-checkbox>
<el-checkbox label="其他第三方平台"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="自动通知">
<el-switch v-model="crawlerSettings.autoNotify"></el-switch>
<span style="margin-left: 10px;">{{ crawlerSettings.autoNotify ? '已开启' : '已关闭' }}</span>
</el-form-item>
<el-form-item label="通知方式" v-if="crawlerSettings.autoNotify">
<el-checkbox-group v-model="crawlerSettings.notifyMethods">
<el-checkbox label="站内消息"></el-checkbox>
<el-checkbox label="电子邮件"></el-checkbox>
<el-checkbox label="手机短信"></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="crawlerSettingsVisible = false">取消</el-button>
<el-button type="primary" @click="saveCrawlerSettings">保存设置</el-button>
</span>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="投标响应" name="response">
<el-tabs v-model="biddingResponseTab">
<el-tab-pane label="投标项目" name="projects">
<div class="table-actions">
<el-input
placeholder="搜索投标项目"
v-model="biddingResponseSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search" @click="searchBiddingResponse"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewBiddingDialog">新建投标</el-button>
<el-button icon="el-icon-refresh" @click="refreshBiddingResponse">刷新</el-button>
</div>
</div>
<el-table :data="biddingResponseData" border style="width: 100%">
<el-table-column prop="projectName" label="项目名称" width="250"></el-table-column>
<el-table-column prop="publisher" label="招标单位" width="200"></el-table-column>
<el-table-column prop="budget" label="预算金额" width="200" align="right"></el-table-column>
<el-table-column prop="deadline" label="截止时间" width="200"></el-table-column>
<el-table-column prop="status" label="投标状态" width="200">
<template slot-scope="scope">
<span :class="['status-badge',
scope.row.status === '准备中' ? 'preparing' :
scope.row.status === '已投标' ? 'bidding' :
scope.row.status === '已中标' ? 'won' : 'lost']">
{{ scope.row.status }}
</span>
</template>
</el-table-column>
<el-table-column prop="progress" label="进度" width="150">
<template slot-scope="scope">
<el-progress :percentage="scope.row.progress" :status="scope.row.status === '已中标' ? 'success' : ''"></el-progress>
</template>
</el-table-column>
<el-table-column label="操作" width="370">
<template slot-scope="scope">
<el-button size="mini" @click="viewBiddingResponse(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editBiddingResponse(scope.row)"
:disabled="scope.row.status === '已中标' || scope.row.status === '已投标'">编辑</el-button>
<el-button size="mini" type="success" @click="uploadBiddingDocument(scope.row)">上传文件</el-button>
<el-button size="mini" type="warning" @click="updateBiddingStatus(scope.row)"
v-if="scope.row.status === '准备中'">提交投标</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleBiddingResponseSizeChange"
@current-change="handleBiddingResponseCurrentChange"
:current-page="biddingResponseSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="biddingResponseSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="biddingResponseTotal">
</el-pagination>
</div>
<!-- 投标项目详情对话框 -->
<el-dialog :title="`投标项目详情 - ${currentBiddingResponse.projectName}`" :visible.sync="biddingResponseDetailVisible" width="60%">
<el-descriptions :column="2" border>
<el-descriptions-item label="项目名称">{{ currentBiddingResponse.projectName }}</el-descriptions-item>
<el-descriptions-item label="招标单位">{{ currentBiddingResponse.publisher }}</el-descriptions-item>
<el-descriptions-item label="预算金额">{{ currentBiddingResponse.budget }}</el-descriptions-item>
<el-descriptions-item label="截止时间">{{ currentBiddingResponse.deadline }}</el-descriptions-item>
<el-descriptions-item label="项目地点">{{ currentBiddingResponse.location }}</el-descriptions-item>
<el-descriptions-item label="项目类型">{{ currentBiddingResponse.projectType }}</el-descriptions-item>
<el-descriptions-item label="投标状态">{{ currentBiddingResponse.status }}</el-descriptions-item>
<el-descriptions-item label="投标进度">
<el-progress :percentage="currentBiddingResponse.progress" :status="currentBiddingResponse.status === '已中标' ? 'success' : ''"></el-progress>
</el-descriptions-item>
<el-descriptions-item label="投标负责人">{{ currentBiddingResponse.manager }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ currentBiddingResponse.phone }}</el-descriptions-item>
<el-descriptions-item label="项目描述" :span="2">{{ currentBiddingResponse.description }}</el-descriptions-item>
<el-descriptions-item label="招标文件" :span="2">
<div v-if="currentBiddingResponse.biddingDocuments && currentBiddingResponse.biddingDocuments.length > 0">
<div v-for="doc in currentBiddingResponse.biddingDocuments" :key="doc.name" class="file-actions">
<div class="file-link">
<i class="el-icon-document"></i>
<el-link :href="doc.url" target="_blank">{{ doc.name }}</el-link>
</div>
<el-button size="mini" type="danger" icon="el-icon-delete" circle @click="deleteBiddingResponseFile(currentBiddingResponse, doc)"></el-button>
</div>
</div>
<div v-else style="color: #909399;">暂无招标文件</div>
</el-descriptions-item>
</el-descriptions>
<span slot="footer" class="dialog-footer">
<el-button @click="biddingResponseDetailVisible = false">关闭</el-button>
</span>
</el-dialog>
<!-- 上传投标文件对话框 -->
<el-dialog title="上传投标文件" :visible.sync="uploadDocumentDialogVisible" width="50%">
<el-form :model="uploadDocumentForm" label-width="100px">
<el-form-item label="文件类型">
<el-radio-group v-model="uploadDocumentForm.type">
<el-radio label="商务标书">商务标书</el-radio>
<el-radio label="报价文件">报价文件</el-radio>
<el-radio label="其他文件">其他文件</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="上传文件">
<el-upload
class="upload-demo"
action=""
:on-change="handleDocumentUpload"
:auto-upload="false"
:file-list="documentFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传投标相关文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="uploadDocumentDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitDocumentUpload">确定</el-button>
</span>
</el-dialog>
<!-- 编辑投标项目对话框 -->
<el-dialog title="编辑投标项目" :visible.sync="editBiddingDialogVisible" width="50%">
<el-form :model="editBiddingForm" :rules="biddingRules" ref="editBiddingForm" label-width="120px">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="editBiddingForm.projectName"></el-input>
</el-form-item>
<el-form-item label="招标单位" prop="publisher">
<el-input v-model="editBiddingForm.publisher"></el-input>
</el-form-item>
<el-form-item label="预算金额" prop="budget">
<el-input v-model="editBiddingForm.budget" style="width: 200px;">
<template slot="append">万元</template>
</el-input>
</el-form-item>
<el-form-item label="截止时间" prop="deadline">
<el-date-picker
v-model="editBiddingForm.deadline"
type="datetime"
placeholder="选择日期时间"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item label="项目地点" prop="location">
<el-cascader
v-model="editBiddingForm.location"
:options="locationOptions"
style="width: 100%;"
></el-cascader>
</el-form-item>
<el-form-item label="项目类型" prop="projectType">
<el-select v-model="editBiddingForm.projectType" style="width: 100%;">
<el-option label="风电叶片检查" value="wind-power"></el-option>
<el-option label="风电运维" value="maintenance"></el-option>
<el-option label="风电安装" value="installation"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
</el-form-item>
<el-form-item label="投标负责人" prop="manager">
<el-input v-model="editBiddingForm.manager"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input v-model="editBiddingForm.phone"></el-input>
</el-form-item>
<el-form-item label="项目描述" prop="description">
<el-input type="textarea" v-model="editBiddingForm.description" :rows="3"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editBiddingDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditBidding">保存</el-button>
</span>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="商务标书" name="documents">
<div class="table-actions">
<el-input
placeholder="搜索标书"
v-model="documentSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search" @click="searchDocuments"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewDocumentDialog">新建标书</el-button>
<el-button icon="el-icon-refresh" @click="refreshDocuments">刷新</el-button>
</div>
</div>
<el-table :data="documentData" border style="width: 100%">
<el-table-column prop="name" label="标书名称" width="280"></el-table-column>
<el-table-column prop="project" label="关联项目" width="280"></el-table-column>
<el-table-column prop="type" label="标书类型" width="300">
<template slot-scope="scope">
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="300">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '已完成' ? 'success' : 'warning'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="400">
<template slot-scope="scope">
<el-button size="mini" @click="viewDocument(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editDocument(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteDocument(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleDocumentSizeChange"
@current-change="handleDocumentCurrentChange"
:current-page="documentSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="documentSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="documentTotal">
</el-pagination>
</div>
<!-- 新建商务标书对话框 -->
<el-dialog title="新建商务标书" :visible.sync="newDocumentDialogVisible" width="50%">
<el-form :model="newDocumentForm" :rules="documentRules" ref="newDocumentForm" label-width="100px">
<el-form-item label="标书名称" prop="name">
<el-input v-model="newDocumentForm.name"></el-input>
</el-form-item>
<el-form-item label="关联项目" prop="project">
<el-select v-model="newDocumentForm.project" placeholder="请选择关联项目" style="width: 100%;">
<el-option
v-for="item in biddingResponseData"
:key="item.id"
:label="item.projectName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="标书类型" prop="type">
<el-select v-model="newDocumentForm.type" placeholder="请选择标书类型" style="width: 100%;">
<el-option label="商务标书" value="商务标书"></el-option>
<el-option label="技术标书" value="技术标书"></el-option>
<el-option label="其他" value="其他"></el-option>
</el-select>
</el-form-item>
<el-form-item label="上传文件" prop="files">
<el-upload
class="upload-demo"
action=""
:on-change="handleNewDocumentUpload"
:auto-upload="false"
:file-list="newDocumentFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传标书文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="newDocumentDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitNewDocument">确定</el-button>
</span>
</el-dialog>
<!-- 编辑商务标书对话框 -->
<el-dialog title="编辑商务标书" :visible.sync="editDocumentDialogVisible" width="50%">
<el-form :model="editDocumentForm" :rules="documentRules" ref="editDocumentForm" label-width="100px">
<el-form-item label="标书名称" prop="name">
<el-input v-model="editDocumentForm.name"></el-input>
</el-form-item>
<el-form-item label="关联项目" prop="project">
<el-select v-model="editDocumentForm.project" placeholder="请选择关联项目" style="width: 100%;">
<el-option
v-for="item in biddingResponseData"
:key="item.id"
:label="item.projectName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="标书类型" prop="type">
<el-select v-model="editDocumentForm.type" placeholder="请选择标书类型" style="width: 100%;">
<el-option label="商务标书" value="商务标书"></el-option>
<el-option label="技术标书" value="技术标书"></el-option>
<el-option label="其他" value="其他"></el-option>
</el-select>
</el-form-item>
<el-form-item label="上传文件" prop="files">
<el-upload
class="upload-demo"
action=""
:on-change="handleEditDocumentUpload"
:auto-upload="false"
:file-list="editDocumentFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传标书文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDocumentDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditDocument">保存</el-button>
</span>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="报价文件" name="quotations">
<div class="table-actions">
<el-input
placeholder="搜索报价文件"
v-model="quotationSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search" @click="searchQuotations"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewQuotationDialog">新建报价</el-button>
<el-button icon="el-icon-refresh" @click="refreshQuotations">刷新</el-button>
</div>
</div>
<el-table :data="quotationData" border style="width: 100%">
<el-table-column prop="name" label="报价文件" width="250"></el-table-column>
<el-table-column prop="project" label="关联项目" width="250"></el-table-column>
<el-table-column prop="totalPrice" label="总报价" width="240" align="right"></el-table-column>
<el-table-column prop="profitRate" label="利润率" width="240">
<template slot-scope="scope">
<el-tag :type="scope.row.profitRate >= 20 ? 'success' : scope.row.profitRate >= 10 ? 'warning' : 'danger'">
{{ scope.row.profitRate }}%
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="240">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '已完成' ? 'success' : 'warning'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="360">
<template slot-scope="scope">
<el-button size="mini" @click="viewQuotation(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editQuotation(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteQuotation(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleQuotationSizeChange"
@current-change="handleQuotationCurrentChange"
:current-page="quotationSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="quotationSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="quotationTotal">
</el-pagination>
</div>
<!-- 新建报价对话框 -->
<el-dialog title="新建报价文件" :visible.sync="newQuotationDialogVisible" width="50%">
<el-form :model="newQuotationForm" :rules="quotationRules" ref="newQuotationForm" label-width="100px">
<el-form-item label="报价名称" prop="name">
<el-input v-model="newQuotationForm.name"></el-input>
</el-form-item>
<el-form-item label="关联项目" prop="project">
<el-select v-model="newQuotationForm.project" placeholder="请选择关联项目" style="width: 100%;">
<el-option
v-for="item in biddingResponseData"
:key="item.id"
:label="item.projectName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="总报价" prop="totalPrice">
<el-input v-model="newQuotationForm.totalPrice" style="width: 200px;">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="利润率" prop="profitRate">
<el-input v-model="newQuotationForm.profitRate" style="width: 200px;">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item label="报价说明" prop="description">
<el-input type="textarea" v-model="newQuotationForm.description" :rows="3"></el-input>
</el-form-item>
<el-form-item label="上传文件" prop="files">
<el-upload
class="upload-demo"
action=""
:on-change="handleNewQuotationUpload"
:auto-upload="false"
:file-list="newQuotationFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传报价相关文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="newQuotationDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitNewQuotation">确定</el-button>
</span>
</el-dialog>
<!-- 编辑报价对话框 -->
<el-dialog title="编辑报价文件" :visible.sync="editQuotationDialogVisible" width="50%">
<el-form :model="editQuotationForm" :rules="quotationRules" ref="editQuotationForm" label-width="100px">
<el-form-item label="报价名称" prop="name">
<el-input v-model="editQuotationForm.name"></el-input>
</el-form-item>
<el-form-item label="关联项目" prop="project">
<el-select v-model="editQuotationForm.project" placeholder="请选择关联项目" style="width: 100%;">
<el-option
v-for="item in biddingResponseData"
:key="item.id"
:label="item.projectName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="总报价" prop="totalPrice">
<el-input v-model="editQuotationForm.totalPrice" style="width: 200px;">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="利润率" prop="profitRate">
<el-input v-model="editQuotationForm.profitRate" style="width: 200px;">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item label="报价说明" prop="description">
<el-input type="textarea" v-model="editQuotationForm.description" :rows="3"></el-input>
</el-form-item>
<el-form-item label="上传文件" prop="files">
<el-upload
class="upload-demo"
action=""
:on-change="handleEditQuotationUpload"
:auto-upload="false"
:file-list="editQuotationFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传报价相关文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editQuotationDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditQuotation">保存</el-button>
</span>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="中标通知" name="notices">
<div class="table-actions">
<el-input
placeholder="搜索中标通知"
v-model="noticeSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search" @click="searchNotices"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewNoticeDialog">新建通知</el-button>
<el-button icon="el-icon-refresh" @click="refreshNotices">刷新</el-button>
</div>
</div>
<el-table :data="noticeData" border style="width: 100%">
<el-table-column prop="projectName" label="项目名称" width="250"></el-table-column>
<el-table-column prop="publisher" label="招标单位" width="250"></el-table-column>
<el-table-column prop="winningPrice" label="中标金额" width="240" align="right"></el-table-column>
<el-table-column prop="noticeDate" label="通知日期" width="240"></el-table-column>
<el-table-column prop="status" label="状态" width="240">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '已签约' ? 'success' :
scope.row.status === '已确认' ? 'primary' : 'warning'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="350">
<template slot-scope="scope">
<el-button size="mini" @click="viewNotice(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editNotice(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteNotice(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleNoticeSizeChange"
@current-change="handleNoticeCurrentChange"
:current-page="noticeSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="noticeSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="noticeTotal">
</el-pagination>
</div>
<!-- 新建中标通知对话框 -->
<el-dialog title="新建中标通知" :visible.sync="newNoticeDialogVisible" width="50%">
<el-form :model="newNoticeForm" :rules="noticeRules" ref="newNoticeForm" label-width="100px">
<el-form-item label="关联项目" prop="project">
<el-select v-model="newNoticeForm.project" placeholder="请选择关联项目" style="width: 100%;">
<el-option
v-for="item in biddingResponseData"
:key="item.id"
:label="item.projectName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="中标金额" prop="winningPrice">
<el-input v-model="newNoticeForm.winningPrice" style="width: 200px;">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="通知日期" prop="noticeDate">
<el-date-picker
v-model="newNoticeForm.noticeDate"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width: 100%;">
</el-date-picker>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="newNoticeForm.status" placeholder="请选择状态" style="width: 100%;">
<el-option label="已确认" value="已确认"></el-option>
<el-option label="已签约" value="已签约"></el-option>
</el-select>
</el-form-item>
<el-form-item label="上传文件" prop="files">
<el-upload
class="upload-demo"
action=""
:on-change="handleNewNoticeUpload"
:auto-upload="false"
:file-list="newNoticeFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传中标通知书等相关文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="newNoticeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitNewNotice">确定</el-button>
</span>
</el-dialog>
<!-- 编辑中标通知对话框 -->
<el-dialog title="编辑中标通知" :visible.sync="editNoticeDialogVisible" width="50%">
<el-form :model="editNoticeForm" :rules="noticeRules" ref="editNoticeForm" label-width="100px">
<el-form-item label="关联项目" prop="project">
<el-select v-model="editNoticeForm.project" placeholder="请选择关联项目" style="width: 100%;">
<el-option
v-for="item in biddingResponseData"
:key="item.id"
:label="item.projectName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="中标金额" prop="winningPrice">
<el-input v-model="editNoticeForm.winningPrice" style="width: 200px;">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="通知日期" prop="noticeDate">
<el-date-picker
v-model="editNoticeForm.noticeDate"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width: 100%;">
</el-date-picker>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="editNoticeForm.status" placeholder="请选择状态" style="width: 100%;">
<el-option label="已确认" value="已确认"></el-option>
<el-option label="已签约" value="已签约"></el-option>
</el-select>
</el-form-item>
<el-form-item label="上传文件" prop="files">
<el-upload
class="upload-demo"
action=""
:on-change="handleEditNoticeUpload"
:auto-upload="false"
:file-list="editNoticeFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传中标通知书等相关文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editNoticeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditNotice">保存</el-button>
</span>
</el-dialog>
</el-tab-pane>
</el-tabs>
<!-- 新建投标对话框 -->
<el-dialog title="新建投标项目" :visible.sync="newBiddingDialogVisible" width="50%">
<el-form :model="newBiddingForm" :rules="biddingRules" ref="newBiddingForm" label-width="120px">
<el-form-item label="关联招标项目" prop="biddingProject">
<el-select
v-model="newBiddingForm.biddingProject"
filterable
placeholder="请选择关注的招标项目"
style="width: 100%;"
@change="handleBiddingProjectChange"
>
<el-option
v-for="item in interestedBiddingProjects"
:key="item.id"
:label="`${item.title} - ${item.publisher}`"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="newBiddingForm.projectName"></el-input>
</el-form-item>
<el-form-item label="招标单位" prop="publisher">
<el-input v-model="newBiddingForm.publisher"></el-input>
</el-form-item>
<el-form-item label="预算金额" prop="budget">
<el-input v-model="newBiddingForm.budget" style="width: 200px;">
<template slot="append">万元</template>
</el-input>
</el-form-item>
<el-form-item label="截止时间" prop="deadline">
<el-date-picker
v-model="newBiddingForm.deadline"
type="datetime"
placeholder="选择日期时间"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item label="项目地点" prop="location">
<el-cascader
v-model="newBiddingForm.location"
:options="locationOptions"
style="width: 100%;"
></el-cascader>
</el-form-item>
<el-form-item label="项目类型" prop="projectType">
<el-select v-model="newBiddingForm.projectType" style="width: 100%;">
<el-option label="风电叶片检查" value="wind-power"></el-option>
<el-option label="风电运维" value="maintenance"></el-option>
<el-option label="风电安装" value="installation"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
</el-form-item>
<el-form-item label="招标文件" prop="documents">
<div v-if="newBiddingForm.documents && newBiddingForm.documents.length > 0">
<div v-for="doc in newBiddingForm.documents" :key="doc.name" class="file-link">
<i class="el-icon-document"></i>
<el-link :href="doc.url" target="_blank">{{ doc.name }}</el-link>
</div>
</div>
<div v-else style="color: #909399;">暂无招标文件</div>
</el-form-item>
<el-form-item label="投标负责人" prop="manager">
<el-input v-model="newBiddingForm.manager"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input v-model="newBiddingForm.phone"></el-input>
</el-form-item>
<el-form-item label="项目描述" prop="description">
<el-input type="textarea" v-model="newBiddingForm.description" :rows="3"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="newBiddingDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitNewBidding">确定</el-button>
</span>
</el-dialog>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
isCollapse: false,
activeModule: 'bidding',
// 招采业务
biddingTab: 'search',
biddingSearch: {
keyword: '',
page: 1,
limit: 10
},
biddingData: [
{
id: 1,
title: '某风电场2023年叶片检查服务采购项目',
publisher: '某风电有限公司',
budget: '120.00万元',
deadline: '2023-12-15 17:00',
crawlTime: '2023-11-01 09:30',
source: '中国招标投标网',
sourceUrl: 'https://www.cebpubservice.com/',
status: '待报名',
location: '内蒙古自治区',
duration: '6个月',
scope: '风电场50台机组叶片检查服务',
qualification: '具备风电叶片检查资质',
documents: [
{ name: '招标文件.pdf', url: 'https://example.com/bidding-docs/123' },
{ name: '技术规范.docx', url: 'https://example.com/bidding-docs/124' }
],
content: '<p>本项目为某风电场2023年叶片检查服务采购包括但不限于</p><ul><li>叶片外观检查</li><li>无损检测</li><li>缺陷记录与评估</li><li>检查报告编制</li></ul>',
interested: false
},
{
id: 2,
title: '某风电场2023年运维服务采购项目',
publisher: '某新能源集团',
budget: '200.00万元',
deadline: '2023-12-20 17:00',
crawlTime: '2023-11-02 10:15',
source: '某省公共资源交易中心',
sourceUrl: 'https://www.example-province.gov.cn/',
status: '已报名',
location: '河北省',
duration: '1年',
scope: '风电场全年运维服务',
qualification: '具备风电运维资质',
documents: [],
content: '<p>本项目为某风电场2023年全年运维服务采购包括但不限于</p><ul><li>日常巡检</li><li>定期维护</li><li>故障处理</li><li>数据记录</li></ul>',
interested: true
}
],
biddingTotal: 20,
currentBidding: null,
biddingDetailVisible: false,
crawlerSettingsVisible: false,
crawlerSettings: {
frequency: 'daily',
keywords: ['风电', '叶片', '检查', '运维'],
platforms: ['中国招标投标公共服务平台', '各省市公共资源交易中心'],
autoNotify: true,
notifyMethods: ['站内消息', '电子邮件']
},
newKeyword: '',
// 投标响应
biddingResponseTab: 'projects',
biddingResponseSearch: {
keyword: '',
page: 1,
limit: 10
},
biddingResponseData: [
{
id: 1,
biddingId: 1,
projectName: '某风电场2023年叶片检查服务采购项目',
publisher: '某风电有限公司',
budget: '120.00万元',
deadline: '2023-12-15 17:00',
location: '内蒙古自治区',
projectType: '风电叶片检查',
manager: '张经理',
phone: '13800138000',
description: '为某风电场提供叶片检查服务',
status: '准备中',
progress: 30,
biddingDocuments: [
{ name: '招标文件.pdf', url: 'https://example.com/bidding-docs/123' },
{ name: '技术规范.docx', url: 'https://example.com/bidding-docs/124' }
],
documents: [
{ name: '技术标书.docx', type: '商务标书', status: '已完成', url: 'https://example.com/documents/123' },
{ name: '商务标书.docx', type: '商务标书', status: '已完成', url: 'https://example.com/documents/124' }
],
quotation: {
name: '某风电场叶片检查报价单',
totalPrice: '1,080,000.00',
profitRate: 15,
status: '已完成',
description: '基于市场价格和公司成本核算的报价',
files: [
{ name: '报价单.xlsx', url: 'https://example.com/quotations/123' }
]
},
notice: null,
progressSteps: [
{
title: '项目立项',
description: '项目立项并分配负责人',
date: '2023-11-01',
status: 'completed',
files: []
},
{
title: '招标文件分析',
description: '完成招标文件分析和技术要求评估',
date: '2023-11-05',
status: 'completed',
files: [
{ name: '招标分析报告.docx', url: 'https://example.com/progress/123' }
]
},
{
title: '技术标书编制',
description: '技术标书编制完成',
date: '2023-11-10',
status: 'completed',
files: [
{ name: '技术标书.docx', url: 'https://example.com/documents/123' }
]
},
{
title: '商务标书编制',
description: '商务标书编制完成',
date: '2023-11-15',
status: 'completed',
files: [
{ name: '商务标书.docx', url: 'https://example.com/documents/124' }
]
},
{
title: '报价文件准备',
description: '正在准备报价文件',
date: '',
status: 'active',
files: []
},
{
title: '投标文件审核',
description: '等待投标文件审核',
date: '',
status: '',
files: []
},
{
title: '提交投标',
description: '提交投标文件',
date: '',
status: '',
files: []
}
]
},
{
id: 2,
biddingId: 3,
projectName: '某风电场2022年运维服务项目',
publisher: '某新能源公司',
budget: '180.00万元',
deadline: '2022-11-30 17:00',
location: '甘肃省',
projectType: '风电运维',
manager: '李经理',
phone: '13900139000',
description: '为某风电场提供全年运维服务',
status: '已中标',
progress: 100,
biddingDocuments: [
{ name: '招标文件.pdf', url: 'https://example.com/bidding-docs/125' }
],
documents: [
{ name: '技术标书.docx', type: '商务标书', status: '已完成', url: 'https://example.com/documents/125' },
{ name: '商务标书.docx', type: '商务标书', status: '已完成', url: 'https://example.com/documents/126' }
],
quotation: {
name: '某风电场运维服务报价单',
totalPrice: '1,620,000.00',
profitRate: 12,
status: '已完成',
description: '综合考虑运维成本和市场竞争的报价',
files: [
{ name: '报价单.xlsx', url: 'https://example.com/quotations/124' }
]
},
notice: {
name: '某风电场2022年运维服务中标通知',
winningPrice: '1,620,000.00',
noticeDate: '2022-12-05',
status: '已签约',
files: [
{ name: '中标通知书.pdf', url: 'https://example.com/notices/123' }
]
},
progressSteps: [
{
title: '项目立项',
description: '项目立项并分配负责人',
date: '2022-10-01',
status: 'completed',
files: []
},
{
title: '招标文件分析',
description: '完成招标文件分析和技术要求评估',
date: '2022-10-05',
status: 'completed',
files: [
{ name: '招标分析报告.docx', url: 'https://example.com/progress/125' }
]
},
{
title: '技术标书编制',
description: '技术标书编制完成',
date: '2022-10-15',
status: 'completed',
files: [
{ name: '技术标书.docx', url: 'https://example.com/documents/125' }
]
},
{
title: '商务标书编制',
description: '商务标书编制完成',
date: '2022-10-20',
status: 'completed',
files: [
{ name: '商务标书.docx', url: 'https://example.com/documents/126' }
]
},
{
title: '报价文件准备',
description: '报价文件准备完成',
date: '2022-10-25',
status: 'completed',
files: [
{ name: '报价单.xlsx', url: 'https://example.com/quotations/124' }
]
},
{
title: '投标文件审核',
description: '投标文件审核通过',
date: '2022-10-28',
status: 'completed',
files: []
},
{
title: '提交投标',
description: '投标文件已提交',
date: '2022-10-30',
status: 'completed',
files: []
},
{
title: '中标通知',
description: '收到中标通知书',
date: '2022-12-05',
status: 'completed',
files: [
{ name: '中标通知书.pdf', url: 'https://example.com/notices/123' }
]
},
{
title: '合同签订',
description: '合同已签订',
date: '2022-12-15',
status: 'completed',
files: [
{ name: '合同文件.pdf', url: 'https://example.com/contracts/123' }
]
}
]
}
],
biddingResponseTotal: 15,
currentBiddingResponse: {},
biddingResponseDetailVisible: false,
uploadDocumentDialogVisible: false,
uploadDocumentForm: {
type: '商务标书',
files: []
},
documentFileList: [],
editBiddingDialogVisible: false,
editBiddingForm: {
projectName: '',
publisher: '',
budget: '',
deadline: '',
location: [],
projectType: '',
manager: '',
phone: '',
description: ''
},
// 商务标书
documentSearch: {
keyword: '',
page: 1,
limit: 10
},
documentData: [
{
id: 1,
name: '某风电场叶片检查技术标书',
project: '某风电场2023年叶片检查',
projectId: 1,
type: '商务标书',
status: '已完成',
url: 'https://example.com/documents/123'
},
{
id: 2,
name: '某风电场叶片检查商务标书',
project: '某风电场2023年叶片检查',
projectId: 1,
type: '商务标书',
status: '已完成',
url: 'https://example.com/documents/124'
}
],
documentTotal: 8,
newDocumentDialogVisible: false,
newDocumentForm: {
name: '',
project: '',
type: '商务标书',
files: []
},
newDocumentFileList: [],
editDocumentDialogVisible: false,
editDocumentForm: {
id: '',
name: '',
project: '',
type: '商务标书',
files: []
},
editDocumentFileList: [],
documentRules: {
name: [
{ required: true, message: '请输入标书名称', trigger: 'blur' }
],
project: [
{ required: true, message: '请选择关联项目', trigger: 'change' }
],
type: [
{ required: true, message: '请选择标书类型', trigger: 'change' }
]
},
// 报价文件
quotationSearch: {
keyword: '',
page: 1,
limit: 10
},
quotationData: [
{
id: 1,
name: '某风电场叶片检查报价单',
project: '某风电场2023年叶片检查',
projectId: 1,
totalPrice: '1,080,000.00',
profitRate: 15,
status: '已完成',
description: '基于市场价格和公司成本核算的报价',
files: [
{ name: '报价单.xlsx', url: 'https://example.com/quotations/123' }
]
},
{
id: 2,
name: '某风电场运维服务报价单',
project: '某风电场2022年运维服务',
projectId: 2,
totalPrice: '1,620,000.00',
profitRate: 12,
status: '已完成',
description: '综合考虑运维成本和市场竞争的报价',
files: [
{ name: '报价单.xlsx', url: 'https://example.com/quotations/124' }
]
}
],
quotationTotal: 5,
newQuotationDialogVisible: false,
newQuotationForm: {
name: '',
project: '',
totalPrice: '',
profitRate: '',
description: '',
files: []
},
newQuotationFileList: [],
editQuotationDialogVisible: false,
editQuotationForm: {
id: '',
name: '',
project: '',
totalPrice: '',
profitRate: '',
description: '',
files: []
},
editQuotationFileList: [],
quotationRules: {
name: [
{ required: true, message: '请输入报价名称', trigger: 'blur' }
],
project: [
{ required: true, message: '请选择关联项目', trigger: 'change' }
],
totalPrice: [
{ required: true, message: '请输入总报价', trigger: 'blur' }
],
profitRate: [
{ required: true, message: '请输入利润率', trigger: 'blur' }
]
},
// 中标通知
noticeSearch: {
keyword: '',
page: 1,
limit: 10
},
noticeData: [
{
id: 1,
projectName: '某风电场2022年运维服务',
projectId: 2,
publisher: '某新能源公司',
winningPrice: '1,620,000.00',
noticeDate: '2022-12-05',
status: '已签约',
files: [
{ name: '中标通知书.pdf', url: 'https://example.com/notices/123' }
]
}
],
noticeTotal: 1,
newNoticeDialogVisible: false,
newNoticeForm: {
project: '',
winningPrice: '',
noticeDate: '',
status: '已确认',
files: []
},
newNoticeFileList: [],
editNoticeDialogVisible: false,
editNoticeForm: {
id: '',
project: '',
winningPrice: '',
noticeDate: '',
status: '已确认',
files: []
},
editNoticeFileList: [],
noticeRules: {
project: [
{ required: true, message: '请选择关联项目', trigger: 'change' }
],
winningPrice: [
{ required: true, message: '请输入中标金额', trigger: 'blur' }
],
noticeDate: [
{ required: true, message: '请选择通知日期', trigger: 'change' }
],
status: [
{ required: true, message: '请选择状态', trigger: 'change' }
]
},
// 新建投标项目
newBiddingDialogVisible: false,
newBiddingForm: {
biddingProject: '',
projectName: '',
publisher: '',
budget: '',
deadline: '',
location: [],
projectType: '',
documents: [],
manager: '',
phone: '',
description: ''
},
biddingRules: {
biddingProject: [
{ required: true, message: '请选择关联的招标项目', trigger: 'change' }
],
projectName: [
{ required: true, message: '请输入项目名称', trigger: 'blur' }
],
publisher: [
{ required: true, message: '请输入招标单位', trigger: 'blur' }
],
deadline: [
{ required: true, message: '请选择截止时间', trigger: 'change' }
]
},
locationOptions: [
{
value: '北京市',
label: '北京市',
children: [
{ value: '东城区', label: '东城区' },
{ value: '西城区', label: '西城区' }
]
},
{
value: '内蒙古自治区',
label: '内蒙古自治区',
children: [
{ value: '呼和浩特市', label: '呼和浩特市' },
{ value: '包头市', label: '包头市' }
]
}
],
biddingFileList: []
}
},
computed: {
interestedBiddingProjects() {
return this.biddingData.filter(item => item.status === '已报名');
}
},
mounted() {
// 初始化数据
},
methods: {
toggleSidebar() {
this.isCollapse = !this.isCollapse;
},
switchModule(module) {
this.activeModule = module;
},
// 招采业务 - 信息检索
startCrawler() {
this.$message.success('爬虫任务已启动');
// 实际应用中这里应该调用API启动爬虫
},
searchBiddingInfo() {
this.$message(`搜索招标信息: ${this.biddingSearch.keyword}`);
// 实际应用中这里应该调用API获取数据
},
refreshBiddingData() {
this.$message('刷新招标数据');
// 实际应用中这里应该调用API获取数据
},
exportBiddingData() {
this.$message('导出招标数据');
},
showCrawlerSettings() {
this.crawlerSettingsVisible = true;
},
viewBiddingDetail(row) {
this.currentBidding = row;
this.biddingDetailVisible = true;
},
markAsRegistered(row) {
this.$confirm(`确定要报名招标项目 "${row.title}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
row.status = '已报名';
this.$message({
type: 'success',
message: '报名成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
goToBiddingResponse() {
this.biddingTab = 'response';
this.biddingDetailVisible = false;
},
addKeyword() {
if (this.newKeyword && !this.crawlerSettings.keywords.includes(this.newKeyword)) {
this.crawlerSettings.keywords.push(this.newKeyword);
this.newKeyword = '';
}
},
removeKeyword(tag) {
this.crawlerSettings.keywords = this.crawlerSettings.keywords.filter(item => item !== tag);
},
saveCrawlerSettings() {
this.$message.success('爬虫设置已保存');
this.crawlerSettingsVisible = false;
},
handleBiddingSizeChange(val) {
this.biddingSearch.limit = val;
this.refreshBiddingData();
},
handleBiddingCurrentChange(val) {
this.biddingSearch.page = val;
this.refreshBiddingData();
},
handleBiddingFileUpload(file, fileList, row) {
// 模拟上传文件
const newDoc = {
name: file.name,
url: URL.createObjectURL(file.raw)
};
if (!row.documents) {
row.documents = [];
}
row.documents.push(newDoc);
this.$message.success(`文件 ${file.name} 上传成功`);
},
// 招采业务 - 投标响应
searchBiddingResponse() {
this.$message(`搜索投标项目: ${this.biddingResponseSearch.keyword}`);
// 实际应用中这里应该调用API获取数据
},
showNewBiddingDialog() {
if (this.interestedBiddingProjects.length === 0) {
this.$message.warning('请先在"信息检索"中报名招标项目');
return;
}
this.newBiddingForm = {
biddingProject: '',
projectName: '',
publisher: '',
budget: '',
deadline: '',
location: [],
projectType: '',
documents: [],
manager: '',
phone: '',
description: ''
};
this.newBiddingDialogVisible = true;
},
handleBiddingProjectChange(biddingId) {
const project = this.biddingData.find(item => item.id === biddingId);
if (project) {
this.newBiddingForm.projectName = project.title;
this.newBiddingForm.publisher = project.publisher;
this.newBiddingForm.budget = project.budget;
this.newBiddingForm.deadline = project.deadline;
this.newBiddingForm.location = project.location.split(' '); // 假设location是空格分隔的
this.newBiddingForm.documents = project.documents || [];
}
},
submitNewBidding() {
this.$refs.newBiddingForm.validate(valid => {
if (valid) {
// 生成新的投标项目
const newProject = {
id: Date.now(),
biddingId: this.newBiddingForm.biddingProject,
projectName: this.newBiddingForm.projectName,
publisher: this.newBiddingForm.publisher,
budget: this.newBiddingForm.budget,
deadline: this.newBiddingForm.deadline,
location: this.newBiddingForm.location.join(' '),
projectType: this.newBiddingForm.projectType,
manager: this.newBiddingForm.manager,
phone: this.newBiddingForm.phone,
description: this.newBiddingForm.description,
status: '准备中',
progress: 0,
biddingDocuments: this.newBiddingForm.documents || [],
documents: [],
quotation: null,
notice: null,
progressSteps: [
{
title: '项目立项',
description: '项目立项并分配负责人',
date: this.formatDate(new Date()),
status: 'completed',
files: []
},
{
title: '招标文件分析',
description: '分析招标文件和技术要求',
date: '',
status: '',
files: []
},
{
title: '技术标书编制',
description: '编制技术标书',
date: '',
status: '',
files: []
},
{
title: '商务标书编制',
description: '编制商务标书',
date: '',
status: '',
files: []
},
{
title: '报价文件准备',
description: '准备报价文件',
date: '',
status: '',
files: []
},
{
title: '投标文件审核',
description: '审核投标文件',
date: '',
status: '',
files: []
},
{
title: '提交投标',
description: '提交投标文件',
date: '',
status: '',
files: []
}
]
};
this.biddingResponseData.unshift(newProject);
this.biddingResponseTotal += 1;
this.$message.success('新建投标项目成功');
this.newBiddingDialogVisible = false;
} else {
return false;
}
});
},
refreshBiddingResponse() {
this.$message('刷新投标响应数据');
// 实际应用中这里应该调用API获取数据
},
viewBiddingResponse(row) {
this.currentBiddingResponse = JSON.parse(JSON.stringify(row));
this.biddingResponseDetailVisible = true;
},
editBiddingResponse(row) {
this.editBiddingForm = {
projectName: row.projectName,
publisher: row.publisher,
budget: row.budget,
deadline: row.deadline,
location: row.location.split(' '),
projectType: row.projectType,
manager: row.manager,
phone: row.phone,
description: row.description
};
this.currentBiddingResponse = row;
this.editBiddingDialogVisible = true;
},
submitEditBidding() {
this.$refs.editBiddingForm.validate(valid => {
if (valid) {
this.currentBiddingResponse.projectName = this.editBiddingForm.projectName;
this.currentBiddingResponse.publisher = this.editBiddingForm.publisher;
this.currentBiddingResponse.budget = this.editBiddingForm.budget;
this.currentBiddingResponse.deadline = this.editBiddingForm.deadline;
this.currentBiddingResponse.location = this.editBiddingForm.location.join(' ');
this.currentBiddingResponse.projectType = this.editBiddingForm.projectType;
this.currentBiddingResponse.manager = this.editBiddingForm.manager;
this.currentBiddingResponse.phone = this.editBiddingForm.phone;
this.currentBiddingResponse.description = this.editBiddingForm.description;
this.$message.success('修改投标项目成功');
this.editBiddingDialogVisible = false;
} else {
return false;
}
});
},
uploadBiddingDocument(row) {
this.currentBiddingResponse = row;
this.uploadDocumentForm = {
type: '商务标书',
files: []
};
this.documentFileList = [];
this.uploadDocumentDialogVisible = true;
},
handleDocumentUpload(file, fileList) {
this.documentFileList = fileList;
},
submitDocumentUpload() {
if (this.documentFileList.length === 0) {
this.$message.warning('请先上传文件');
return;
}
const newDoc = {
name: this.documentFileList[0].name,
type: this.uploadDocumentForm.type,
url: URL.createObjectURL(this.documentFileList[0].raw),
status: '已完成'
};
// 根据文件类型添加到不同位置
if (this.uploadDocumentForm.type === '商务标书') {
if (!this.currentBiddingResponse.documents) {
this.currentBiddingResponse.documents = [];
}
this.currentBiddingResponse.documents.push(newDoc);
// 同时添加到商务标书列表
this.documentData.unshift({
id: Date.now(),
name: newDoc.name,
project: this.currentBiddingResponse.projectName,
projectId: this.currentBiddingResponse.id,
type: newDoc.type,
status: newDoc.status,
url: newDoc.url
});
this.documentTotal += 1;
// 更新进度步骤
const stepIndex = this.currentBiddingResponse.progressSteps.findIndex(
step => step.title.includes('商务标书')
);
if (stepIndex !== -1) {
this.currentBiddingResponse.progressSteps[stepIndex].status = 'completed';
this.currentBiddingResponse.progressSteps[stepIndex].date = this.formatDate(new Date());
this.currentBiddingResponse.progressSteps[stepIndex].files = [newDoc];
this.updateProgressPercentage(this.currentBiddingResponse);
}
} else if (this.uploadDocumentForm.type === '报价文件') {
if (!this.currentBiddingResponse.quotation) {
this.currentBiddingResponse.quotation = {
name: `${this.currentBiddingResponse.projectName}报价单`,
totalPrice: '',
profitRate: '',
status: '已完成',
description: '',
files: []
};
}
this.currentBiddingResponse.quotation.files.push(newDoc);
// 同时添加到报价文件列表
this.quotationData.unshift({
id: Date.now(),
name: `${this.currentBiddingResponse.projectName}报价单`,
project: this.currentBiddingResponse.projectName,
projectId: this.currentBiddingResponse.id,
totalPrice: this.currentBiddingResponse.quotation.totalPrice,
profitRate: this.currentBiddingResponse.quotation.profitRate,
status: '已完成',
description: this.currentBiddingResponse.quotation.description,
files: [newDoc]
});
this.quotationTotal += 1;
// 更新进度步骤
const stepIndex = this.currentBiddingResponse.progressSteps.findIndex(
step => step.title.includes('报价文件')
);
if (stepIndex !== -1) {
this.currentBiddingResponse.progressSteps[stepIndex].status = 'completed';
this.currentBiddingResponse.progressSteps[stepIndex].date = this.formatDate(new Date());
this.currentBiddingResponse.progressSteps[stepIndex].files = [newDoc];
this.updateProgressPercentage(this.currentBiddingResponse);
}
}
this.$message.success('文件上传成功');
this.uploadDocumentDialogVisible = false;
},
deleteBiddingResponseFile(row, doc) {
this.$confirm('确定要删除这个文件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (row.biddingDocuments) {
row.biddingDocuments = row.biddingDocuments.filter(item => item.name !== doc.name);
}
this.$message.success('文件已删除');
}).catch(() => {
this.$message.info('已取消删除');
});
},
updateBiddingStatus(row) {
this.$confirm(`确定要提交投标项目 "${row.projectName}" 吗?提交后将不能修改`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
row.status = '已投标';
row.progress = 60;
// 更新进度步骤
const stepIndex = row.progressSteps.findIndex(
step => step.title.includes('提交投标')
);
if (stepIndex !== -1) {
row.progressSteps[stepIndex].status = 'completed';
row.progressSteps[stepIndex].date = this.formatDate(new Date());
this.updateProgressPercentage(row);
}
this.$message({
type: 'success',
message: '投标已提交!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
updateProgressPercentage(row) {
const completedSteps = row.progressSteps.filter(step => step.status === 'completed').length;
const totalSteps = row.progressSteps.length;
row.progress = Math.round((completedSteps / totalSteps) * 100);
},
formatDate(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}`;
},
handleBiddingResponseSizeChange(val) {
this.biddingResponseSearch.limit = val;
this.refreshBiddingResponse();
},
handleBiddingResponseCurrentChange(val) {
this.biddingResponseSearch.page = val;
this.refreshBiddingResponse();
},
// 商务标书
searchDocuments() {
this.$message(`搜索标书: ${this.documentSearch.keyword}`);
// 实际应用中这里应该调用API获取数据
},
refreshDocuments() {
this.$message('刷新标书数据');
// 实际应用中这里应该调用API获取数据
},
showNewDocumentDialog() {
this.newDocumentForm = {
name: '',
project: '',
type: '商务标书',
files: []
};
this.newDocumentFileList = [];
this.newDocumentDialogVisible = true;
},
handleNewDocumentUpload(file, fileList) {
this.newDocumentFileList = fileList;
},
submitNewDocument() {
this.$refs.newDocumentForm.validate(valid => {
if (valid) {
if (this.newDocumentFileList.length === 0) {
this.$message.warning('请先上传文件');
return;
}
const project = this.biddingResponseData.find(item => item.id === this.newDocumentForm.project);
const newDoc = {
id: Date.now(),
name: this.newDocumentForm.name,
project: project ? project.projectName : '',
projectId: this.newDocumentForm.project,
type: this.newDocumentForm.type,
status: '已完成',
url: URL.createObjectURL(this.newDocumentFileList[0].raw)
};
this.documentData.unshift(newDoc);
this.documentTotal += 1;
// 同时添加到投标项目的文档列表
if (project) {
if (!project.documents) {
project.documents = [];
}
project.documents.push({
name: newDoc.name,
type: newDoc.type,
status: newDoc.status,
url: newDoc.url
});
// 更新进度步骤
const stepTitle = newDoc.type === '商务标书' ? '商务标书编制' : '技术标书编制';
const stepIndex = project.progressSteps.findIndex(
step => step.title.includes(stepTitle)
);
if (stepIndex !== -1) {
project.progressSteps[stepIndex].status = 'completed';
project.progressSteps[stepIndex].date = this.formatDate(new Date());
project.progressSteps[stepIndex].files = [{
name: newDoc.name,
url: newDoc.url
}];
this.updateProgressPercentage(project);
}
}
this.$message.success('新建标书成功');
this.newDocumentDialogVisible = false;
} else {
return false;
}
});
},
viewDocument(row) {
window.open(row.url, '_blank');
},
editDocument(row) {
this.editDocumentForm = {
id: row.id,
name: row.name,
project: row.projectId,
type: row.type,
files: []
};
this.editDocumentFileList = [];
this.currentDocument = row;
this.editDocumentDialogVisible = true;
},
handleEditDocumentUpload(file, fileList) {
this.editDocumentFileList = fileList;
},
submitEditDocument() {
this.$refs.editDocumentForm.validate(valid => {
if (valid) {
this.currentDocument.name = this.editDocumentForm.name;
this.currentDocument.project = this.biddingResponseData.find(item => item.id === this.editDocumentForm.project)?.projectName || '';
this.currentDocument.projectId = this.editDocumentForm.project;
this.currentDocument.type = this.editDocumentForm.type;
if (this.editDocumentFileList.length > 0) {
this.currentDocument.url = URL.createObjectURL(this.editDocumentFileList[0].raw);
}
this.$message.success('修改标书成功');
this.editDocumentDialogVisible = false;
} else {
return false;
}
});
},
deleteDocument(row) {
this.$confirm('确定要删除这个标书吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.documentData = this.documentData.filter(item => item.id !== row.id);
this.documentTotal -= 1;
// 同时从投标项目的文档列表中删除
const project = this.biddingResponseData.find(item => item.id === row.projectId);
if (project && project.documents) {
project.documents = project.documents.filter(doc => doc.name !== row.name);
}
this.$message.success('标书已删除');
}).catch(() => {
this.$message.info('已取消删除');
});
},
handleDocumentSizeChange(val) {
this.documentSearch.limit = val;
this.refreshDocuments();
},
handleDocumentCurrentChange(val) {
this.documentSearch.page = val;
this.refreshDocuments();
},
// 报价文件
searchQuotations() {
this.$message(`搜索报价文件: ${this.quotationSearch.keyword}`);
// 实际应用中这里应该调用API获取数据
},
refreshQuotations() {
this.$message('刷新报价数据');
// 实际应用中这里应该调用API获取数据
},
showNewQuotationDialog() {
this.newQuotationForm = {
name: '',
project: '',
totalPrice: '',
profitRate: '',
description: '',
files: []
};
this.newQuotationFileList = [];
this.newQuotationDialogVisible = true;
},
handleNewQuotationUpload(file, fileList) {
this.newQuotationFileList = fileList;
},
submitNewQuotation() {
this.$refs.newQuotationForm.validate(valid => {
if (valid) {
if (this.newQuotationFileList.length === 0) {
this.$message.warning('请先上传文件');
return;
}
const project = this.biddingResponseData.find(item => item.id === this.newQuotationForm.project);
const newQuotation = {
id: Date.now(),
name: this.newQuotationForm.name || `${project ? project.projectName : ''}报价单`,
project: project ? project.projectName : '',
projectId: this.newQuotationForm.project,
totalPrice: this.newQuotationForm.totalPrice,
profitRate: this.newQuotationForm.profitRate,
status: '已完成',
description: this.newQuotationForm.description,
files: [
{
name: this.newQuotationFileList[0].name,
url: URL.createObjectURL(this.newQuotationFileList[0].raw)
}
]
};
this.quotationData.unshift(newQuotation);
this.quotationTotal += 1;
// 同时添加到投标项目的报价信息
if (project) {
project.quotation = {
name: newQuotation.name,
totalPrice: newQuotation.totalPrice,
profitRate: newQuotation.profitRate,
status: '已完成',
description: newQuotation.description,
files: newQuotation.files
};
// 更新进度步骤
const stepIndex = project.progressSteps.findIndex(
step => step.title.includes('报价文件')
);
if (stepIndex !== -1) {
project.progressSteps[stepIndex].status = 'completed';
project.progressSteps[stepIndex].date = this.formatDate(new Date());
project.progressSteps[stepIndex].files = newQuotation.files;
this.updateProgressPercentage(project);
}
}
this.$message.success('新建报价成功');
this.newQuotationDialogVisible = false;
} else {
return false;
}
});
},
viewQuotation(row) {
if (row.files && row.files.length > 0) {
window.open(row.files[0].url, '_blank');
}
},
editQuotation(row) {
this.editQuotationForm = {
id: row.id,
name: row.name,
project: row.projectId,
totalPrice: row.totalPrice,
profitRate: row.profitRate,
description: row.description,
files: []
};
this.editQuotationFileList = [];
this.currentQuotation = row;
this.editQuotationDialogVisible = true;
},
handleEditQuotationUpload(file, fileList) {
this.editQuotationFileList = fileList;
},
submitEditQuotation() {
this.$refs.editQuotationForm.validate(valid => {
if (valid) {
this.currentQuotation.name = this.editQuotationForm.name;
this.currentQuotation.project = this.biddingResponseData.find(item => item.id === this.editQuotationForm.project)?.projectName || '';
this.currentQuotation.projectId = this.editQuotationForm.project;
this.currentQuotation.totalPrice = this.editQuotationForm.totalPrice;
this.currentQuotation.profitRate = this.editQuotationForm.profitRate;
this.currentQuotation.description = this.editQuotationForm.description;
if (this.editQuotationFileList.length > 0) {
this.currentQuotation.files = [{
name: this.editQuotationFileList[0].name,
url: URL.createObjectURL(this.editQuotationFileList[0].raw)
}];
}
// 更新投标项目中的报价信息
const project = this.biddingResponseData.find(item => item.id === this.editQuotationForm.project);
if (project && project.quotation) {
project.quotation.name = this.editQuotationForm.name;
project.quotation.totalPrice = this.editQuotationForm.totalPrice;
project.quotation.profitRate = this.editQuotationForm.profitRate;
project.quotation.description = this.editQuotationForm.description;
if (this.editQuotationFileList.length > 0) {
project.quotation.files = [{
name: this.editQuotationFileList[0].name,
url: URL.createObjectURL(this.editQuotationFileList[0].raw)
}];
}
}
this.$message.success('修改报价成功');
this.editQuotationDialogVisible = false;
} else {
return false;
}
});
},
deleteQuotation(row) {
this.$confirm('确定要删除这个报价吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.quotationData = this.quotationData.filter(item => item.id !== row.id);
this.quotationTotal -= 1;
// 同时从投标项目的报价信息中删除
const project = this.biddingResponseData.find(item => item.id === row.projectId);
if (project) {
project.quotation = null;
// 更新进度步骤
const stepIndex = project.progressSteps.findIndex(
step => step.title.includes('报价文件')
);
if (stepIndex !== -1) {
project.progressSteps[stepIndex].status = '';
project.progressSteps[stepIndex].date = '';
project.progressSteps[stepIndex].files = [];
this.updateProgressPercentage(project);
}
}
this.$message.success('报价已删除');
}).catch(() => {
this.$message.info('已取消删除');
});
},
handleQuotationSizeChange(val) {
this.quotationSearch.limit = val;
this.refreshQuotations();
},
handleQuotationCurrentChange(val) {
this.quotationSearch.page = val;
this.refreshQuotations();
},
// 中标通知
searchNotices() {
this.$message(`搜索中标通知: ${this.noticeSearch.keyword}`);
// 实际应用中这里应该调用API获取数据
},
refreshNotices() {
this.$message('刷新中标通知数据');
// 实际应用中这里应该调用API获取数据
},
showNewNoticeDialog() {
this.newNoticeForm = {
project: '',
winningPrice: '',
noticeDate: '',
status: '已确认',
files: []
};
this.newNoticeFileList = [];
this.newNoticeDialogVisible = true;
},
handleNewNoticeUpload(file, fileList) {
this.newNoticeFileList = fileList;
},
submitNewNotice() {
this.$refs.newNoticeForm.validate(valid => {
if (valid) {
if (this.newNoticeFileList.length === 0) {
this.$message.warning('请先上传文件');
return;
}
const project = this.biddingResponseData.find(item => item.id === this.newNoticeForm.project);
const newNotice = {
id: Date.now(),
projectName: project ? project.projectName : '',
projectId: this.newNoticeForm.project,
publisher: project ? project.publisher : '',
winningPrice: this.newNoticeForm.winningPrice,
noticeDate: this.newNoticeForm.noticeDate,
status: this.newNoticeForm.status,
files: [
{
name: this.newNoticeFileList[0].name,
url: URL.createObjectURL(this.newNoticeFileList[0].raw)
}
]
};
this.noticeData.unshift(newNotice);
this.noticeTotal += 1;
// 同时更新投标项目的状态和通知信息
if (project) {
project.status = '已中标';
project.progress = 100;
project.notice = {
name: `${project.projectName}中标通知`,
winningPrice: newNotice.winningPrice,
noticeDate: newNotice.noticeDate,
status: newNotice.status,
files: newNotice.files
};
// 更新进度步骤
const stepIndex = project.progressSteps.findIndex(
step => step.title.includes('中标通知')
);
if (stepIndex !== -1) {
project.progressSteps[stepIndex].status = 'completed';
project.progressSteps[stepIndex].date = newNotice.noticeDate;
project.progressSteps[stepIndex].files = newNotice.files;
this.updateProgressPercentage(project);
}
}
this.$message.success('新建中标通知成功');
this.newNoticeDialogVisible = false;
} else {
return false;
}
});
},
viewNotice(row) {
if (row.files && row.files.length > 0) {
window.open(row.files[0].url, '_blank');
}
},
editNotice(row) {
this.editNoticeForm = {
id: row.id,
project: row.projectId,
winningPrice: row.winningPrice,
noticeDate: row.noticeDate,
status: row.status,
files: []
};
this.editNoticeFileList = [];
this.currentNotice = row;
this.editNoticeDialogVisible = true;
},
handleEditNoticeUpload(file, fileList) {
this.editNoticeFileList = fileList;
},
submitEditNotice() {
this.$refs.editNoticeForm.validate(valid => {
if (valid) {
this.currentNotice.project = this.editNoticeForm.project;
this.currentNotice.projectName = this.biddingResponseData.find(item => item.id === this.editNoticeForm.project)?.projectName || '';
this.currentNotice.winningPrice = this.editNoticeForm.winningPrice;
this.currentNotice.noticeDate = this.editNoticeForm.noticeDate;
this.currentNotice.status = this.editNoticeForm.status;
if (this.editNoticeFileList.length > 0) {
this.currentNotice.files = [{
name: this.editNoticeFileList[0].name,
url: URL.createObjectURL(this.editNoticeFileList[0].raw)
}];
}
// 更新投标项目中的通知信息
const project = this.biddingResponseData.find(item => item.id === this.editNoticeForm.project);
if (project && project.notice) {
project.notice.winningPrice = this.editNoticeForm.winningPrice;
project.notice.noticeDate = this.editNoticeForm.noticeDate;
project.notice.status = this.editNoticeForm.status;
if (this.editNoticeFileList.length > 0) {
project.notice.files = [{
name: this.editNoticeFileList[0].name,
url: URL.createObjectURL(this.editNoticeFileList[0].raw)
}];
}
}
this.$message.success('修改中标通知成功');
this.editNoticeDialogVisible = false;
} else {
return false;
}
});
},
deleteNotice(row) {
this.$confirm('确定要删除这个中标通知吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.noticeData = this.noticeData.filter(item => item.id !== row.id);
this.noticeTotal -= 1;
// 同时从投标项目的通知信息中删除
const project = this.biddingResponseData.find(item => item.id === row.projectId);
if (project) {
project.notice = null;
// 更新进度步骤
const stepIndex = project.progressSteps.findIndex(
step => step.title.includes('中标通知')
);
if (stepIndex !== -1) {
project.progressSteps[stepIndex].status = '';
project.progressSteps[stepIndex].date = '';
project.progressSteps[stepIndex].files = [];
this.updateProgressPercentage(project);
}
}
this.$message.success('通知已删除');
}).catch(() => {
this.$message.info('已取消删除');
});
},
handleNoticeSizeChange(val) {
this.noticeSearch.limit = val;
this.refreshNotices();
},
handleNoticeCurrentChange(val) {
this.noticeSearch.page = val;
this.refreshNotices();
}
}
});
</script>
</body>
</html>