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

2722 lines
115 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!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>