yuanxingsheji/项目管理/市场商务管理/市场商务管理-修.html

2236 lines
90 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: 800px;
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;
}
}
</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-menu-item index="marketing" @click="switchModule('marketing')">市场营销</el-menu-item>
<!-- 自建项目 - 合并为一个菜单项 -->
<el-menu-item index="self-project" @click="switchModule('self-project')">自建项目</el-menu-item>
</el-submenu>
<!-- 项目合同管理 -->
<el-submenu index="contract">
<template slot="title">项目合同管理</template>
<el-menu-item index="income-contract" @click="switchModule('income-contract')">收入合同</el-menu-item>
<el-menu-item index="expense-contract" @click="switchModule('expense-contract')">支出合同</el-menu-item>
<el-menu-item index="cost-management" @click="switchModule('cost-management')">成本费用</el-menu-item>
</el-submenu>
</el-submenu>
<!-- 施工立项 -->
<el-menu-item index="construction" @click="switchModule('construction')">
<i class="el-icon-document-add"></i>
<span slot="title">施工立项</span>
</el-menu-item>
<!-- 组织实施管理 -->
<el-menu-item index="implementation" @click="switchModule('implementation')">
<i class="el-icon-s-management"></i>
<span slot="title">组织实施管理</span>
</el-menu-item>
</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-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="source" label="来源平台" width="120"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '进行中' ? 'success' : 'info'">
{{ 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="markAsInterested(scope.row)">关注</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.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.docUrl" target="_blank">下载招标文件</el-link>
</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"></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="150"></el-table-column>
<el-table-column prop="budget" label="预算金额" width="120" align="right"></el-table-column>
<el-table-column prop="deadline" label="截止时间" width="150"></el-table-column>
<el-table-column prop="status" label="投标状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '已中标' ? 'success' :
scope.row.status === '已投标' ? 'primary' :
scope.row.status === '准备中' ? 'warning' : 'info'">
{{ scope.row.status }}
</el-tag>
</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="200">
<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>
</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-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"></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="250"></el-table-column>
<el-table-column prop="project" label="关联项目" width="200"></el-table-column>
<el-table-column prop="type" label="标书类型" width="120">
<template slot-scope="scope">
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="version" label="版本" width="100"></el-table-column>
<el-table-column prop="updateTime" label="更新时间" width="180"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<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="200">
<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="success" @click="generateDocument(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-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"></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="200"></el-table-column>
<el-table-column prop="totalPrice" label="总报价" width="120" align="right"></el-table-column>
<el-table-column prop="profitRate" label="利润率" width="120">
<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="updateTime" label="更新时间" width="180"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<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="200">
<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="success" @click="confirmQuotation(scope.row)"
:disabled="scope.row.status === '已确认'">确认</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-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"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-download" @click="exportNotices">导出数据</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="150"></el-table-column>
<el-table-column prop="winningPrice" label="中标金额" width="120" align="right"></el-table-column>
<el-table-column prop="noticeDate" label="通知日期" width="150"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<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="200">
<template slot-scope="scope">
<el-button size="mini" @click="viewNotice(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="processNotice(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-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="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="docUrl">
<el-upload
class="upload-demo"
action=""
:on-change="handleBiddingFileChange"
:auto-upload="false"
:file-list="biddingFileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">请上传招标文件</div>
</el-upload>
</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 v-if="activeModule === 'marketing'">
<h2 class="module-title">市场营销</h2>
<div class="card-container">
<el-tabs v-model="marketingTab" type="card">
<el-tab-pane label="营销推广" name="promotion">
<el-tabs v-model="marketingSubTab">
<el-tab-pane label="营销活动" name="activities">
<div class="table-actions">
<el-input
placeholder="搜索营销活动"
v-model="activitySearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewActivityDialog">新建活动</el-button>
<el-button icon="el-icon-refresh" @click="refreshActivities">刷新</el-button>
</div>
</div>
<el-table :data="activityData" border style="width: 100%">
<el-table-column prop="name" label="活动名称" width="250"></el-table-column>
<el-table-column prop="type" label="活动类型" width="120">
<template slot-scope="scope">
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="startDate" label="开始时间" width="180"></el-table-column>
<el-table-column prop="endDate" label="结束时间" width="180"></el-table-column>
<el-table-column prop="budget" label="预算" width="120" align="right"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '进行中' ? 'success' :
scope.row.status === '已结束' ? 'info' : 'warning'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewActivity(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editActivity(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleActivitySizeChange"
@current-change="handleActivityCurrentChange"
:current-page="activitySearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="activitySearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="activityTotal">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="客户管理" name="clients">
<div class="table-actions">
<el-input
placeholder="搜索客户"
v-model="clientSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewClientDialog">新增客户</el-button>
<el-button icon="el-icon-refresh" @click="refreshClients">刷新</el-button>
</div>
</div>
<el-table :data="clientData" border style="width: 100%">
<el-table-column prop="name" label="客户名称" width="200"></el-table-column>
<el-table-column prop="industry" label="所属行业" width="150"></el-table-column>
<el-table-column prop="contact" label="联系人" width="120"></el-table-column>
<el-table-column prop="phone" label="联系电话" width="150"></el-table-column>
<el-table-column prop="level" label="客户等级" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.level === 'A' ? 'success' :
scope.row.level === 'B' ? 'primary' : 'warning'">
{{ scope.row.level }}级
</el-tag>
</template>
</el-table-column>
<el-table-column prop="lastContact" label="最后联系" width="180"></el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewClient(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editClient(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleClientSizeChange"
@current-change="handleClientCurrentChange"
:current-page="clientSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="clientSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="clientTotal">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="营销效果" name="effect">
<div style="margin-bottom: 20px;">
<el-date-picker
v-model="effectDateRange"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
style="width: 300px;"
>
</el-date-picker>
<el-button type="primary" @click="loadEffectData" style="margin-left: 20px;">查询</el-button>
</div>
<div style="display: flex; margin-bottom: 20px;">
<div style="flex: 1; margin-right: 20px;">
<el-card shadow="hover">
<div slot="header">新增客户</div>
<div style="font-size: 24px; text-align: center;">{{ effectData.newClients }}</div>
</el-card>
</div>
<div style="flex: 1; margin-right: 20px;">
<el-card shadow="hover">
<div slot="header">商机数量</div>
<div style="font-size: 24px; text-align: center;">{{ effectData.opportunities }}</div>
</el-card>
</div>
<div style="flex: 1;">
<el-card shadow="hover">
<div slot="header">转化率</div>
<div style="font-size: 24px; text-align: center;">{{ effectData.conversionRate }}%</div>
</el-card>
</div>
</div>
<div style="height: 400px; margin-bottom: 20px;">
<div id="effectChart" style="width: 100%; height: 100%;"></div>
</div>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
<el-tab-pane label="框架协议" name="agreement">
<div class="table-actions">
<el-input
placeholder="搜索框架协议"
v-model="agreementSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewAgreementDialog">新建协议</el-button>
<el-button icon="el-icon-refresh" @click="refreshAgreements">刷新</el-button>
</div>
</div>
<el-table :data="agreementData" border style="width: 100%">
<el-table-column prop="name" label="协议名称" width="250"></el-table-column>
<el-table-column prop="client" label="客户名称" width="200"></el-table-column>
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="结束日期" width="150"></el-table-column>
<el-table-column prop="amount" label="预估金额" width="120" align="right"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '有效' ? 'success' : 'info'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button size="mini" @click="viewAgreement(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editAgreement(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="terminateAgreement(scope.row)"
:disabled="scope.row.status !== '有效'">终止</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleAgreementSizeChange"
@current-change="handleAgreementCurrentChange"
:current-page="agreementSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="agreementSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="agreementTotal">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="同行业务" name="industry">
<el-tabs v-model="industryTab">
<el-tab-pane label="竞争对手" name="competitors">
<div class="table-actions">
<el-input
placeholder="搜索竞争对手"
v-model="competitorSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewCompetitorDialog">新增对手</el-button>
<el-button icon="el-icon-refresh" @click="refreshCompetitors">刷新</el-button>
</div>
</div>
<el-table :data="competitorData" border style="width: 100%">
<el-table-column prop="name" label="公司名称" width="250"></el-table-column>
<el-table-column prop="strength" label="优势领域" width="200"></el-table-column>
<el-table-column prop="marketShare" label="市场份额" width="120">
<template slot-scope="scope">
{{ scope.row.marketShare }}%
</template>
</el-table-column>
<el-table-column prop="recentProjects" label="近期项目" width="150"></el-table-column>
<el-table-column label="竞争等级" width="120">
<template slot-scope="scope">
<el-rate
v-model="scope.row.level"
disabled
show-text
:texts="['弱', '一般', '较强', '强', '很强']"
></el-rate>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewCompetitor(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editCompetitor(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleCompetitorSizeChange"
@current-change="handleCompetitorCurrentChange"
:current-page="competitorSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="competitorSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="competitorTotal">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="市场分析" name="analysis">
<div style="margin-bottom: 20px;">
<el-date-picker
v-model="analysisYear"
type="year"
placeholder="选择年份"
value-format="yyyy"
style="width: 200px;"
>
</el-date-picker>
<el-button type="primary" @click="loadAnalysisData" style="margin-left: 20px;">查询</el-button>
</div>
<div style="height: 400px; margin-bottom: 20px;">
<div id="marketShareChart" style="width: 100%; height: 100%;"></div>
</div>
<div style="height: 400px;">
<div id="priceTrendChart" style="width: 100%; height: 100%;"></div>
</div>
</el-tab-pane>
<el-tab-pane label="议价策略" name="strategy">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="showNewStrategyDialog">新建策略</el-button>
</div>
<el-table :data="strategyData" border style="width: 100%">
<el-table-column prop="name" label="策略名称" width="250"></el-table-column>
<el-table-column prop="target" label="目标客户" width="200"></el-table-column>
<el-table-column prop="discount" label="折扣范围" width="120">
<template slot-scope="scope">
{{ scope.row.discount }}%
</template>
</el-table-column>
<el-table-column prop="conditions" label="适用条件" width="200"></el-table-column>
<el-table-column prop="effectiveness" label="有效性" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.effectiveness === '' ? 'success' :
scope.row.effectiveness === '中' ? 'warning' : 'danger'">
{{ scope.row.effectiveness }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewStrategy(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editStrategy(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleStrategySizeChange"
@current-change="handleStrategyCurrentChange"
:current-page="strategySearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="strategySearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="strategyTotal">
</el-pagination>
</div>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
</el-tabs>
</div>
</div>
<!-- 自建项目 -->
<div v-if="activeModule === 'self-project'">
<h2 class="module-title">自建项目</h2>
<div class="card-container">
<el-tabs v-model="selfProjectTab" type="card">
<el-tab-pane label="软件开发" name="software">
<div class="table-actions">
<el-input
placeholder="搜索软件项目"
v-model="softwareSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewSoftwareDialog">新建项目</el-button>
<el-button icon="el-icon-refresh" @click="refreshSoftwareProjects">刷新</el-button>
</div>
</div>
<el-table :data="softwareData" border style="width: 100%">
<el-table-column prop="name" label="项目名称" width="250"></el-table-column>
<el-table-column prop="type" label="项目类型" width="150">
<template slot-scope="scope">
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="manager" label="项目经理" width="120"></el-table-column>
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="计划完成" width="150"></el-table-column>
<el-table-column prop="progress" label="进度" width="150">
<template slot-scope="scope">
<el-progress :percentage="scope.row.progress" :status="scope.row.progress === 100 ? 'success' : ''"></el-progress>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewSoftwareProject(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editSoftwareProject(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleSoftwareSizeChange"
@current-change="handleSoftwareCurrentChange"
:current-page="softwareSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="softwareSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="softwareTotal">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="硬件开发" name="hardware">
<div class="table-actions">
<el-input
placeholder="搜索硬件项目"
v-model="hardwareSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewHardwareDialog">新建项目</el-button>
<el-button icon="el-icon-refresh" @click="refreshHardwareProjects">刷新</el-button>
</div>
</div>
<el-table :data="hardwareData" border style="width: 100%">
<el-table-column prop="name" label="项目名称" width="250"></el-table-column>
<el-table-column prop="type" label="项目类型" width="150">
<template slot-scope="scope">
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="manager" label="项目经理" width="120"></el-table-column>
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="计划完成" width="150"></el-table-column>
<el-table-column prop="progress" label="进度" width="150">
<template slot-scope="scope">
<el-progress :percentage="scope.row.progress" :status="scope.row.progress === 100 ? 'success' : ''"></el-progress>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewHardwareProject(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editHardwareProject(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleHardwareSizeChange"
@current-change="handleHardwareCurrentChange"
:current-page="hardwareSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="hardwareSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="hardwareTotal">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="AI模型/算法工具" name="ai">
<div class="table-actions">
<el-input
placeholder="搜索AI项目"
v-model="aiSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewAIDialog">新建项目</el-button>
<el-button icon="el-icon-refresh" @click="refreshAIProjects">刷新</el-button>
</div>
</div>
<el-table :data="aiData" border style="width: 100%">
<el-table-column prop="name" label="项目名称" width="250"></el-table-column>
<el-table-column prop="type" label="项目类型" width="150">
<template slot-scope="scope">
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="manager" label="项目经理" width="120"></el-table-column>
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="计划完成" width="150"></el-table-column>
<el-table-column prop="progress" label="进度" width="150">
<template slot-scope="scope">
<el-progress :percentage="scope.row.progress" :status="scope.row.progress === 100 ? 'success' : ''"></el-progress>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewAIProject(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editAIProject(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleAISizeChange"
@current-change="handleAICurrentChange"
:current-page="aiSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="aiSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="aiTotal">
</el-pagination>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
<!-- 项目合同管理 - 收入合同 -->
<div v-if="activeModule === 'income-contract'">
<h2 class="module-title">项目合同管理 - 收入合同</h2>
<div class="card-container">
<div class="table-actions">
<el-input
placeholder="搜索收入合同"
v-model="incomeContractSearch.keyword"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<div>
<el-button type="primary" icon="el-icon-plus" @click="showNewIncomeContractDialog">新建合同</el-button>
<el-button icon="el-icon-refresh" @click="refreshIncomeContracts">刷新</el-button>
</div>
</div>
<el-table :data="incomeContractData" border style="width: 100%">
<el-table-column prop="code" label="合同编号" width="150"></el-table-column>
<el-table-column prop="name" label="合同名称" width="250"></el-table-column>
<el-table-column prop="client" label="客户名称" width="200"></el-table-column>
<el-table-column prop="amount" label="合同金额" width="150" align="right"></el-table-column>
<el-table-column prop="signDate" label="签订日期" width="150"></el-table-column>
<el-table-column prop="status" label="合同状态" width="150">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '履行中' ? 'success' :
scope.row.status === '已完成' ? 'info' : 'warning'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewIncomeContract(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editIncomeContract(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleIncomeContractSizeChange"
@current-change="handleIncomeContractCurrentChange"
:current-page="incomeContractSearch.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="incomeContractSearch.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="incomeContractTotal">
</el-pagination>
</div>
</div>
</div>
<!-- 施工立项 -->
<div v-if="activeModule === 'construction'">
<h2 class="module-title">施工立项</h2>
<div class="card-container">
<el-alert
title="此功能已实现,请从导航菜单进入具体功能"
type="info"
show-icon
:closable="false"
>
</el-alert>
</div>
</div>
<!-- 组织实施管理 -->
<div v-if="activeModule === 'implementation'">
<h2 class="module-title">组织实施管理</h2>
<div class="card-container">
<el-alert
title="此功能已实现,请从导航菜单进入具体功能"
type="info"
show-icon
:closable="false"
>
</el-alert>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.2/dist/echarts.min.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
isCollapse: false,
activeModule: 'bidding',
// 招采业务
biddingTab: 'search',
biddingSearch: {
keyword: '',
page: 1,
limit: 10
},
biddingData: [
{
title: '某风电场2023年叶片检查服务采购项目',
publisher: '某风电有限公司',
budget: '120.00万元',
deadline: '2023-12-15 17:00',
source: '中国招标投标网',
status: '进行中',
location: '内蒙古自治区',
duration: '6个月',
scope: '风电场50台机组叶片检查服务',
qualification: '具备风电叶片检查资质',
docUrl: 'https://example.com/bidding-docs/123',
content: '<p>本项目为某风电场2023年叶片检查服务采购包括但不限于</p><ul><li>叶片外观检查</li><li>无损检测</li><li>缺陷记录与评估</li><li>检查报告编制</li></ul>'
},
// 更多数据...
],
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: [
{
projectName: '某风电场2023年叶片检查服务采购项目',
publisher: '某风电有限公司',
budget: '120.00万元',
deadline: '2023-12-15 17:00',
status: '准备中',
progress: 30
},
// 更多数据...
],
biddingResponseTotal: 15,
documentSearch: {
keyword: '',
page: 1,
limit: 10
},
documentData: [
{
name: '某风电场叶片检查技术标书',
project: '某风电场2023年叶片检查',
type: '技术标',
version: '1.0',
updateTime: '2023-11-10 14:30',
status: '进行中'
},
// 更多数据...
],
documentTotal: 8,
quotationSearch: {
keyword: '',
page: 1,
limit: 10
},
quotationData: [
{
name: '某风电场叶片检查报价单',
project: '某风电场2023年叶片检查',
totalPrice: '1,080,000.00',
profitRate: 15,
updateTime: '2023-11-08 10:15',
status: '草稿'
},
// 更多数据...
],
quotationTotal: 5,
noticeSearch: {
keyword: '',
page: 1,
limit: 10
},
noticeData: [
{
projectName: '某风电场2022年叶片检查服务',
publisher: '某风电有限公司',
winningPrice: '980,000.00',
noticeDate: '2023-01-15',
status: '已签约'
},
// 更多数据...
],
noticeTotal: 3,
newBiddingDialogVisible: false,
newBiddingForm: {
projectName: '',
publisher: '',
budget: '',
deadline: '',
location: [],
projectType: '',
docUrl: '',
description: ''
},
biddingRules: {
projectName: [
{ required: true, message: '请输入项目名称', trigger: 'blur' }
],
publisher: [
{ required: true, message: '请输入招标单位', trigger: 'blur' }
]
},
locationOptions: [
{
value: '北京市',
label: '北京市',
children: [
{ value: '东城区', label: '东城区' },
// 更多区县...
]
},
// 更多省市...
],
biddingFileList: [],
// 市场营销
marketingTab: 'promotion',
marketingSubTab: 'activities',
activitySearch: {
keyword: '',
page: 1,
limit: 10
},
activityData: [
{
name: '2023年风电运维技术研讨会',
type: '会议',
startDate: '2023-10-15 09:00',
endDate: '2023-10-16 17:00',
budget: '50,000.00',
status: '已结束'
},
// 更多数据...
],
activityTotal: 12,
clientSearch: {
keyword: '',
page: 1,
limit: 10
},
clientData: [
{
name: '某风电集团有限公司',
industry: '风电',
contact: '张经理',
phone: '13800138000',
level: 'A',
lastContact: '2023-11-08'
},
// 更多数据...
],
clientTotal: 25,
effectDateRange: ['2023-01-01', '2023-12-31'],
effectData: {
newClients: 15,
opportunities: 8,
conversionRate: 35
},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
agreementSearch: {
keyword: '',
page: 1,
limit: 10
},
agreementData: [
{
name: '某风电集团2023-2025年叶片检查框架协议',
client: '某风电集团有限公司',
startDate: '2023-01-01',
endDate: '2025-12-31',
amount: '5,000,000.00',
status: '有效'
},
// 更多数据...
],
agreementTotal: 5,
industryTab: 'competitors',
competitorSearch: {
keyword: '',
page: 1,
limit: 10
},
competitorData: [
{
name: '某风电技术服务有限公司',
strength: '叶片维修',
marketShare: 25,
recentProjects: '多个风场运维',
level: 4
},
// 更多数据...
],
competitorTotal: 8,
analysisYear: '2023',
strategySearch: {
keyword: '',
page: 1,
limit: 10
},
strategyData: [
{
name: '长期合作客户优惠',
target: '框架协议客户',
discount: '5-10',
conditions: '合同金额大于100万',
effectiveness: '高'
},
// 更多数据...
],
strategyTotal: 6,
// 自建项目
selfProjectTab: 'software',
softwareSearch: {
keyword: '',
page: 1,
limit: 10
},
softwareData: [
{
name: '风电叶片智能检查系统',
type: '管理平台',
manager: '张工程师',
startDate: '2023-01-15',
endDate: '2023-12-31',
progress: 75
},
// 更多数据...
],
softwareTotal: 3,
hardwareSearch: {
keyword: '',
page: 1,
limit: 10
},
hardwareData: [
{
name: '风电叶片检测设备',
type: '硬件设备',
manager: '李工程师',
startDate: '2023-03-01',
endDate: '2023-09-30',
progress: 90
},
// 更多数据...
],
hardwareTotal: 2,
aiSearch: {
keyword: '',
page: 1,
limit: 10
},
aiData: [
{
name: '叶片缺陷AI识别算法',
type: 'AI模型',
manager: '王工程师',
startDate: '2023-02-15',
endDate: '2023-08-31',
progress: 60
},
// 更多数据...
],
aiTotal: 4,
// 项目合同管理 - 收入合同
incomeContractSearch: {
keyword: '',
page: 1,
limit: 10
},
incomeContractData: [
{
code: 'HT2023001',
name: '某风电场叶片检查服务合同',
client: '某风电有限公司',
amount: '980,000.00',
signDate: '2023-01-20',
status: '履行中'
},
// 更多数据...
],
incomeContractTotal: 5
}
},
mounted() {
// 初始化图表
this.initCharts();
},
methods: {
toggleSidebar() {
this.isCollapse = !this.isCollapse;
},
switchModule(module) {
this.activeModule = module;
},
// 招采业务 - 信息检索
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;
},
markAsInterested(row) {
this.$confirm(`确定要关注招标项目 "${row.title}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '已关注该项目!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
goToBiddingResponse() {
this.biddingTab = 'response';
},
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();
},
// 招采业务 - 投标响应
showNewBiddingDialog() {
this.newBiddingDialogVisible = true;
},
refreshBiddingResponse() {
this.$message('刷新投标响应数据');
// 实际应用中这里应该调用API获取数据
},
viewBiddingResponse(row) {
this.$message(`查看投标响应: ${row.projectName}`);
},
editBiddingResponse(row) {
this.$message(`编辑投标响应: ${row.projectName}`);
},
refreshDocuments() {
this.$message('刷新标书数据');
// 实际应用中这里应该调用API获取数据
},
viewDocument(row) {
this.$message(`查看标书: ${row.name}`);
},
editDocument(row) {
this.$message(`编辑标书: ${row.name}`);
},
generateDocument(row) {
this.$message(`生成标书: ${row.name}`);
},
refreshQuotations() {
this.$message('刷新报价数据');
// 实际应用中这里应该调用API获取数据
},
viewQuotation(row) {
this.$message(`查看报价: ${row.name}`);
},
editQuotation(row) {
this.$message(`编辑报价: ${row.name}`);
},
confirmQuotation(row) {
this.$confirm(`确定要确认报价 "${row.name}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
row.status = '已确认';
this.$message({
type: 'success',
message: '报价已确认!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
refreshNotices() {
this.$message('刷新中标通知数据');
// 实际应用中这里应该调用API获取数据
},
exportNotices() {
this.$message('导出中标通知数据');
},
viewNotice(row) {
this.$message(`查看中标通知: ${row.projectName}`);
},
processNotice(row) {
this.$message(`处理中标通知: ${row.projectName}`);
},
handleBiddingFileChange(file, fileList) {
this.biddingFileList = fileList;
},
submitNewBidding() {
this.$refs.newBiddingForm.validate(valid => {
if (valid) {
this.$message.success('新建投标项目成功');
this.newBiddingDialogVisible = false;
this.refreshBiddingResponse();
} else {
return false;
}
});
},
handleBiddingResponseSizeChange(val) {
this.biddingResponseSearch.limit = val;
this.refreshBiddingResponse();
},
handleBiddingResponseCurrentChange(val) {
this.biddingResponseSearch.page = val;
this.refreshBiddingResponse();
},
handleDocumentSizeChange(val) {
this.documentSearch.limit = val;
this.refreshDocuments();
},
handleDocumentCurrentChange(val) {
this.documentSearch.page = val;
this.refreshDocuments();
},
handleQuotationSizeChange(val) {
this.quotationSearch.limit = val;
this.refreshQuotations();
},
handleQuotationCurrentChange(val) {
this.quotationSearch.page = val;
this.refreshQuotations();
},
handleNoticeSizeChange(val) {
this.noticeSearch.limit = val;
this.refreshNotices();
},
handleNoticeCurrentChange(val) {
this.noticeSearch.page = val;
this.refreshNotices();
},
// 市场营销 - 营销推广
showNewActivityDialog() {
this.$message('显示新建营销活动对话框');
},
refreshActivities() {
this.$message('刷新营销活动数据');
// 实际应用中这里应该调用API获取数据
},
viewActivity(row) {
this.$message(`查看营销活动: ${row.name}`);
},
editActivity(row) {
this.$message(`编辑营销活动: ${row.name}`);
},
showNewClientDialog() {
this.$message('显示新增客户对话框');
},
refreshClients() {
this.$message('刷新客户数据');
// 实际应用中这里应该调用API获取数据
},
viewClient(row) {
this.$message(`查看客户: ${row.name}`);
},
editClient(row) {
this.$message(`编辑客户: ${row.name}`);
},
loadEffectData() {
this.$message('加载营销效果数据');
// 实际应用中这里应该调用API获取数据
this.initEffectChart();
},
handleActivitySizeChange(val) {
this.activitySearch.limit = val;
this.refreshActivities();
},
handleActivityCurrentChange(val) {
this.activitySearch.page = val;
this.refreshActivities();
},
handleClientSizeChange(val) {
this.clientSearch.limit = val;
this.refreshClients();
},
handleClientCurrentChange(val) {
this.clientSearch.page = val;
this.refreshClients();
},
// 市场营销 - 框架协议
showNewAgreementDialog() {
this.$message('显示新建框架协议对话框');
},
refreshAgreements() {
this.$message('刷新框架协议数据');
// 实际应用中这里应该调用API获取数据
},
viewAgreement(row) {
this.$message(`查看框架协议: ${row.name}`);
},
editAgreement(row) {
this.$message(`编辑框架协议: ${row.name}`);
},
terminateAgreement(row) {
this.$confirm(`确定要终止框架协议 "${row.name}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
row.status = '已终止';
this.$message({
type: 'success',
message: '协议已终止!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
handleAgreementSizeChange(val) {
this.agreementSearch.limit = val;
this.refreshAgreements();
},
handleAgreementCurrentChange(val) {
this.agreementSearch.page = val;
this.refreshAgreements();
},
// 市场营销 - 同行业务
showNewCompetitorDialog() {
this.$message('显示新增竞争对手对话框');
},
refreshCompetitors() {
this.$message('刷新竞争对手数据');
// 实际应用中这里应该调用API获取数据
},
viewCompetitor(row) {
this.$message(`查看竞争对手: ${row.name}`);
},
editCompetitor(row) {
this.$message(`编辑竞争对手: ${row.name}`);
},
loadAnalysisData() {
this.$message('加载市场分析数据');
// 实际应用中这里应该调用API获取数据
this.initMarketShareChart();
this.initPriceTrendChart();
},
showNewStrategyDialog() {
this.$message('显示新建议价策略对话框');
},
viewStrategy(row) {
this.$message(`查看议价策略: ${row.name}`);
},
editStrategy(row) {
this.$message(`编辑议价策略: ${row.name}`);
},
handleCompetitorSizeChange(val) {
this.competitorSearch.limit = val;
this.refreshCompetitors();
},
handleCompetitorCurrentChange(val) {
this.competitorSearch.page = val;
this.refreshCompetitors();
},
handleStrategySizeChange(val) {
this.strategySearch.limit = val;
// 实际应用中这里应该调用API获取数据
},
handleStrategyCurrentChange(val) {
this.strategySearch.page = val;
// 实际应用中这里应该调用API获取数据
},
// 自建项目 - 软件开发
showNewSoftwareDialog() {
this.$message('显示新建软件项目对话框');
},
refreshSoftwareProjects() {
this.$message('刷新软件项目数据');
// 实际应用中这里应该调用API获取数据
},
viewSoftwareProject(row) {
this.$message(`查看软件项目: ${row.name}`);
},
editSoftwareProject(row) {
this.$message(`编辑软件项目: ${row.name}`);
},
handleSoftwareSizeChange(val) {
this.softwareSearch.limit = val;
this.refreshSoftwareProjects();
},
handleSoftwareCurrentChange(val) {
this.softwareSearch.page = val;
this.refreshSoftwareProjects();
},
// 自建项目 - 硬件开发
showNewHardwareDialog() {
this.$message('显示新建硬件项目对话框');
},
refreshHardwareProjects() {
this.$message('刷新硬件项目数据');
// 实际应用中这里应该调用API获取数据
},
viewHardwareProject(row) {
this.$message(`查看硬件项目: ${row.name}`);
},
editHardwareProject(row) {
this.$message(`编辑硬件项目: ${row.name}`);
},
handleHardwareSizeChange(val) {
this.hardwareSearch.limit = val;
this.refreshHardwareProjects();
},
handleHardwareCurrentChange(val) {
this.hardwareSearch.page = val;
this.refreshHardwareProjects();
},
// 自建项目 - AI模型/算法工具
showNewAIDialog() {
this.$message('显示新建AI项目对话框');
},
refreshAIProjects() {
this.$message('刷新AI项目数据');
// 实际应用中这里应该调用API获取数据
},
viewAIProject(row) {
this.$message(`查看AI项目: ${row.name}`);
},
editAIProject(row) {
this.$message(`编辑AI项目: ${row.name}`);
},
handleAISizeChange(val) {
this.aiSearch.limit = val;
this.refreshAIProjects();
},
handleAICurrentChange(val) {
this.aiSearch.page = val;
this.refreshAIProjects();
},
// 项目合同管理 - 收入合同
showNewIncomeContractDialog() {
this.$message('显示新建收入合同对话框');
},
refreshIncomeContracts() {
this.$message('刷新收入合同数据');
// 实际应用中这里应该调用API获取数据
},
viewIncomeContract(row) {
this.$message(`查看收入合同: ${row.name}`);
},
editIncomeContract(row) {
this.$message(`编辑收入合同: ${row.name}`);
},
handleIncomeContractSizeChange(val) {
this.incomeContractSearch.limit = val;
this.refreshIncomeContracts();
},
handleIncomeContractCurrentChange(val) {
this.incomeContractSearch.page = val;
this.refreshIncomeContracts();
},
// 图表初始化
initCharts() {
// 初始化营销效果图表
this.initEffectChart();
// 初始化市场份额图表
this.initMarketShareChart();
// 初始化价格趋势图表
this.initPriceTrendChart();
},
initEffectChart() {
const chart = echarts.init(document.getElementById('effectChart'));
chart.setOption({
title: {
text: '营销活动效果趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['新增客户', '商机数量', '转化率'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: [
{
type: 'value',
name: '数量',
position: 'left'
},
{
type: 'value',
name: '转化率(%)',
position: 'right',
max: 100
}
],
series: [
{
name: '新增客户',
type: 'line',
data: [5, 8, 10, 12, 15, 18, 20, 22, 25, 28, 30, 32],
smooth: true,
itemStyle: {
color: '#409EFF'
}
},
{
name: '商机数量',
type: 'line',
data: [2, 3, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22],
smooth: true,
itemStyle: {
color: '#67C23A'
}
},
{
name: '转化率',
type: 'line',
yAxisIndex: 1,
data: [40, 38, 50, 50, 53, 56, 60, 64, 64, 64, 67, 69],
smooth: true,
itemStyle: {
color: '#E6A23C'
}
}
]
});
},
initMarketShareChart() {
const chart = echarts.init(document.getElementById('marketShareChart'));
chart.setOption({
title: {
text: '2023年风电叶片检查市场份额',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: ['我公司', '竞争对手A', '竞争对手B', '竞争对手C', '其他']
},
series: [
{
name: '市场份额',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 35, name: '我公司', itemStyle: { color: '#409EFF' } },
{ value: 25, name: '竞争对手A', itemStyle: { color: '#67C23A' } },
{ value: 20, name: '竞争对手B', itemStyle: { color: '#E6A23C' } },
{ value: 15, name: '竞争对手C', itemStyle: { color: '#F56C6C' } },
{ value: 5, name: '其他', itemStyle: { color: '#909399' } }
]
}
]
});
},
initPriceTrendChart() {
const chart = echarts.init(document.getElementById('priceTrendChart'));
chart.setOption({
title: {
text: '2023年风电叶片检查服务价格趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['我公司报价', '行业平均价', '最低价'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value',
name: '价格(万元/台)'
},
series: [
{
name: '我公司报价',
type: 'line',
data: [2.5, 2.6, 2.55, 2.4, 2.3, 2.2, 2.1, 2.0, 1.95, 1.9, 1.85, 1.8],
smooth: true,
itemStyle: {
color: '#409EFF'
}
},
{
name: '行业平均价',
type: 'line',
data: [2.8, 2.7, 2.65, 2.6, 2.5, 2.4, 2.3, 2.2, 2.1, 2.0, 1.95, 1.9],
smooth: true,
itemStyle: {
color: '#67C23A'
}
},
{
name: '最低价',
type: 'line',
data: [2.2, 2.1, 2.0, 1.9, 1.8, 1.7, 1.6, 1.55, 1.5, 1.45, 1.4, 1.35],
smooth: true,
itemStyle: {
color: '#F56C6C'
}
}
]
});
}
}
});
</script>
</body>
</html>