yuanxingsheji/项目管理/项目管理新增(人员分布、安全、质量).html

1453 lines
73 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://cdn.jsdelivr.net/npm/element-ui@2.15.13/lib/theme-chalk/index.css">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.13/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
<style>
.app-container {
padding: 20px;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
}
.header {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.card-container {
margin-bottom: 20px;
}
.tab-content {
padding: 15px 0;
}
.personnel-table {
margin-top: 15px;
}
.chart-container {
width: 100%;
height: 400px;
}
.filter-container {
margin-bottom: 15px;
}
.role-tag {
margin-right: 5px;
}
.project-card {
margin-bottom: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.project-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
</head>
<body>
<div id="app" class="app-container">
<div class="header">
<h2>项目管理</h2>
<el-breadcrumb separator="/">
<el-breadcrumb-item>项目管理</el-breadcrumb-item>
<el-breadcrumb-item>{{ activeTab }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<!-- 优化后的人员分布模块 -->
<el-tab-pane label="人员分布" name="personnel">
<div class="tab-content">
<div class="filter-container">
<el-form :inline="true" :model="personnelQuery" class="demo-form-inline">
<el-form-item label="项目选择">
<el-select v-model="personnelQuery.project" placeholder="请选择项目" clearable @change="handleProjectChange">
<el-option
v-for="item in projectOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="角色筛选">
<el-select v-model="personnelQuery.role" placeholder="请选择角色" clearable multiple>
<el-option
v-for="item in roleOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="时间范围">
<el-date-picker
v-model="personnelQuery.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchPersonnelData">查询</el-button>
<el-button @click="resetPersonnelQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 项目人员分布卡片 -->
<div v-if="selectedProject">
<el-card class="project-card">
<div slot="header" class="project-header">
<span>{{ selectedProject.name }} 人员分布</span>
<div>
<el-radio-group v-model="viewType" size="small">
<el-radio-button label="chart">分布图</el-radio-button>
<el-radio-button label="table">分布表</el-radio-button>
</el-radio-group>
<el-button type="primary" size="small" @click="showPersonnelDialog" style="margin-left: 10px;">新增安排</el-button>
</div>
</div>
<!-- 角色分布饼图 -->
<div v-if="viewType === 'chart'" class="chart-container" ref="personnelChart"></div>
<!-- 人员分布表格 -->
<div v-if="viewType === 'table'">
<el-table
:data="filteredPersonnelTableData"
border
style="width: 100%">
<el-table-column
prop="name"
label="姓名"
width="120">
</el-table-column>
<el-table-column
label="角色"
width="180">
<template slot-scope="scope">
<el-tag
v-for="role in scope.row.roles"
:key="role"
:type="getRoleTagType(role)"
class="role-tag">
{{ role }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="department"
label="部门">
</el-table-column>
<el-table-column
prop="startDate"
label="开始时间"
width="120">
</el-table-column>
<el-table-column
prop="endDate"
label="结束时间"
width="120">
</el-table-column>
<el-table-column
prop="progress"
label="进度">
<template slot-scope="scope">
<el-progress :percentage="scope.row.progress" :status="getProgressStatus(scope.row.progress)"></el-progress>
</template>
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="editPersonnel(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="viewPersonnelDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</div>
<div v-else style="text-align: center; padding: 50px 0;">
<el-empty description="请选择一个项目查看人员分布"></el-empty>
</div>
</div>
</el-tab-pane>
<!-- 安全管理模块 (保持不变) -->
<el-tab-pane label="安全管理" name="safety">
<!-- 原有安全管理代码保持不变 -->
<div class="tab-content">
<el-collapse v-model="activeSafetyCollapse">
<el-collapse-item title="安全会议管理" name="meeting">
<div>
<el-button type="primary" @click="showSafetyMeetingDialog" style="margin-bottom: 15px;">新增安全会议</el-button>
<el-table
:data="safetyMeetings"
border
style="width: 100%">
<el-table-column
prop="title"
label="会议主题"
width="200">
</el-table-column>
<el-table-column
prop="date"
label="会议时间"
width="180">
</el-table-column>
<el-table-column
prop="organizer"
label="组织者">
</el-table-column>
<el-table-column
prop="participants"
label="参与人数">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewSafetyMeeting(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editSafetyMeeting(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item title="风险点管控" name="risk">
<div>
<el-button type="primary" @click="showRiskDialog" style="margin-bottom: 15px;">新增风险点</el-button>
<el-table
:data="riskPoints"
border
style="width: 100%">
<el-table-column
prop="name"
label="风险点名称"
width="200">
</el-table-column>
<el-table-column
prop="project"
label="所属项目">
</el-table-column>
<el-table-column
prop="level"
label="风险等级">
<template slot-scope="scope">
<el-tag :type="getRiskTagType(scope.row.level)">
{{ scope.row.level }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="manager"
label="责任人">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewRiskDetail(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editRiskPoint(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item title="安全培训与考试" name="training">
<div>
<el-button type="primary" @click="showTrainingDialog" style="margin-bottom: 15px;">新增培训计划</el-button>
<el-table
:data="trainings"
border
style="width: 100%">
<el-table-column
prop="title"
label="培训主题"
width="200">
</el-table-column>
<el-table-column
prop="type"
label="培训类型">
</el-table-column>
<el-table-column
prop="date"
label="培训时间"
width="180">
</el-table-column>
<el-table-column
prop="participants"
label="参与人数">
</el-table-column>
<el-table-column
label="状态">
<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="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewTrainingDetail(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editTraining(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item title="工艺流程安全控制" name="process">
<div>
<el-button type="primary" @click="showProcessDialog" style="margin-bottom: 15px;">新增工艺流程</el-button>
<el-table
:data="processControls"
border
style="width: 100%">
<el-table-column
prop="name"
label="流程名称"
width="200">
</el-table-column>
<el-table-column
prop="project"
label="所属项目">
</el-table-column>
<el-table-column
prop="riskPoints"
label="风险点数量">
</el-table-column>
<el-table-column
prop="manager"
label="责任人">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewProcessDetail(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editProcessControl(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
</el-collapse>
</div>
</el-tab-pane>
<!-- 质量管理模块 (保持不变) -->
<el-tab-pane label="质量管理" name="quality">
<!-- 原有质量管理代码保持不变 -->
<div class="tab-content">
<el-collapse v-model="activeQualityCollapse">
<el-collapse-item title="质量会议管理" name="meeting">
<div>
<el-button type="primary" @click="showQualityMeetingDialog" style="margin-bottom: 15px;">新增质量会议</el-button>
<el-table
:data="qualityMeetings"
border
style="width: 100%">
<el-table-column
prop="title"
label="会议主题"
width="200">
</el-table-column>
<el-table-column
prop="date"
label="会议时间"
width="180">
</el-table-column>
<el-table-column
prop="organizer"
label="组织者">
</el-table-column>
<el-table-column
prop="participants"
label="参与人数">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewQualityMeeting(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editQualityMeeting(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item title="数据质量管理" name="data">
<div>
<el-button type="primary" @click="showDataQualityDialog" style="margin-bottom: 15px;">新增数据质量检查</el-button>
<el-table
:data="dataQualityChecks"
border
style="width: 100%">
<el-table-column
prop="project"
label="项目名称"
width="200">
</el-table-column>
<el-table-column
prop="dataType"
label="数据类型">
</el-table-column>
<el-table-column
prop="checker"
label="检查人">
</el-table-column>
<el-table-column
prop="checkDate"
label="检查时间"
width="180">
</el-table-column>
<el-table-column
prop="result"
label="检查结果">
<template slot-scope="scope">
<el-tag :type="scope.row.result === '合格' ? 'success' : 'danger'">
{{ scope.row.result }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewDataQualityDetail(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editDataQualityCheck(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item title="质量标准管理" name="standard">
<div>
<el-button type="primary" @click="showQualityStandardDialog" style="margin-bottom: 15px;">新增质量标准</el-button>
<el-table
:data="qualityStandards"
border
style="width: 100%">
<el-table-column
prop="name"
label="标准名称"
width="200">
</el-table-column>
<el-table-column
prop="type"
label="标准类型">
</el-table-column>
<el-table-column
prop="version"
label="版本号">
</el-table-column>
<el-table-column
prop="publishDate"
label="发布时间"
width="180">
</el-table-column>
<el-table-column
prop="manager"
label="负责人">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewQualityStandard(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editQualityStandard(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item title="报告质量管理" name="report">
<div>
<el-button type="primary" @click="showReportQualityDialog" style="margin-bottom: 15px;">新增报告检查</el-button>
<el-table
:data="reportQualityChecks"
border
style="width: 100%">
<el-table-column
prop="project"
label="项目名称"
width="200">
</el-table-column>
<el-table-column
prop="reportType"
label="报告类型">
</el-table-column>
<el-table-column
prop="checker"
label="检查人">
</el-table-column>
<el-table-column
prop="checkDate"
label="检查时间"
width="180">
</el-table-column>
<el-table-column
prop="result"
label="检查结果">
<template slot-scope="scope">
<el-tag :type="scope.row.result === '合格' ? 'success' : 'danger'">
{{ scope.row.result }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewReportQualityDetail(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="editReportQualityCheck(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
</el-collapse>
</div>
</el-tab-pane>
</el-tabs>
<!-- 人员分布图对话框 -->
<el-dialog :title="personnelDialogTitle" :visible.sync="personnelDialogVisible" width="50%">
<el-form :model="personnelForm" label-width="100px">
<el-form-item label="人员姓名">
<el-select v-model="personnelForm.name" placeholder="请选择人员" filterable>
<el-option
v-for="item in employeeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="所属项目">
<el-select v-model="personnelForm.project" placeholder="请选择项目" disabled>
<el-option
v-for="item in projectOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="角色">
<el-select v-model="personnelForm.roles" multiple placeholder="请选择角色">
<el-option
v-for="item in roleOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="部门">
<el-input v-model="personnelForm.department"></el-input>
</el-form-item>
<el-form-item label="工作时间">
<el-date-picker
v-model="personnelForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item label="工作进度">
<el-input-number v-model="personnelForm.progress" :min="0" :max="100"></el-input-number> %
</el-form-item>
<el-form-item label="工作内容">
<el-input type="textarea" v-model="personnelForm.description"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="personnelDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="savePersonnel">确 定</el-button>
</span>
</el-dialog>
<!-- 人员详情对话框 -->
<el-dialog title="人员安排详情" :visible.sync="personnelDetailVisible" width="50%">
<el-descriptions :column="1" border>
<el-descriptions-item label="姓名">{{ currentPersonnel.name }}</el-descriptions-item>
<el-descriptions-item label="项目">{{ currentPersonnel.project }}</el-descriptions-item>
<el-descriptions-item label="角色">
<el-tag
v-for="role in currentPersonnel.roles"
:key="role"
:type="getRoleTagType(role)"
class="role-tag">
{{ role }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="部门">{{ currentPersonnel.department }}</el-descriptions-item>
<el-descriptions-item label="工作时间">{{ currentPersonnel.startDate }} 至 {{ currentPersonnel.endDate }}</el-descriptions-item>
<el-descriptions-item label="工作进度">
<el-progress :percentage="currentPersonnel.progress" :status="getProgressStatus(currentPersonnel.progress)"></el-progress>
</el-descriptions-item>
<el-descriptions-item label="工作内容">{{ currentPersonnel.description || '无' }}</el-descriptions-item>
</el-descriptions>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="personnelDetailVisible = false">关 闭</el-button>
</span>
</el-dialog>
<!-- 安全会议对话框 (保持不变) -->
<el-dialog :title="safetyMeetingDialogTitle" :visible.sync="safetyMeetingDialogVisible" width="50%">
<el-form :model="safetyMeetingForm" label-width="100px">
<el-form-item label="会议主题">
<el-input v-model="safetyMeetingForm.title"></el-input>
</el-form-item>
<el-form-item label="会议时间">
<el-date-picker
v-model="safetyMeetingForm.date"
type="datetime"
placeholder="选择日期时间">
</el-date-picker>
</el-form-item>
<el-form-item label="组织者">
<el-input v-model="safetyMeetingForm.organizer"></el-input>
</el-form-item>
<el-form-item label="参与人员">
<el-select
v-model="safetyMeetingForm.participants"
multiple
placeholder="请选择参与人员">
<el-option
v-for="item in employeeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="会议内容">
<el-input type="textarea" v-model="safetyMeetingForm.content"></el-input>
</el-form-item>
<el-form-item label="会议记录">
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
multiple
:limit="3"
:on-exceed="handleExceed"
:file-list="safetyMeetingForm.files">
<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="safetyMeetingDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="saveSafetyMeeting">确 定</el-button>
</span>
</el-dialog>
<!-- 质量会议对话框 (保持不变) -->
<el-dialog :title="qualityMeetingDialogTitle" :visible.sync="qualityMeetingDialogVisible" width="50%">
<el-form :model="qualityMeetingForm" label-width="100px">
<el-form-item label="会议主题">
<el-input v-model="qualityMeetingForm.title"></el-input>
</el-form-item>
<el-form-item label="会议时间">
<el-date-picker
v-model="qualityMeetingForm.date"
type="datetime"
placeholder="选择日期时间">
</el-date-picker>
</el-form-item>
<el-form-item label="组织者">
<el-input v-model="qualityMeetingForm.organizer"></el-input>
</el-form-item>
<el-form-item label="参与人员">
<el-select
v-model="qualityMeetingForm.participants"
multiple
placeholder="请选择参与人员">
<el-option
v-for="item in employeeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="会议内容">
<el-input type="textarea" v-model="qualityMeetingForm.content"></el-input>
</el-form-item>
<el-form-item label="会议记录">
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
multiple
:limit="3"
:on-exceed="handleExceed"
:file-list="qualityMeetingForm.files">
<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="qualityMeetingDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="saveQualityMeeting">确 定</el-button>
</span>
</el-dialog>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
activeTab: 'personnel',
viewType: 'chart',
// 人员分布图数据
personnelQuery: {
project: '',
role: [],
dateRange: ''
},
projectOptions: [
{ value: 'project1', label: '风电塔下检测项目' },
{ value: 'project2', label: '叶片维修项目' },
{ value: 'project3', label: '光伏巡检项目' },
{ value: 'project4', label: '大坝裂缝检测项目' }
],
selectedProject: null,
projects: [
{
id: 'project1',
name: '风电塔下检测项目',
personnel: [
{
id: 'p1-1',
name: '张三',
roles: ['项目经理'],
department: '项目管理部',
startDate: '2023-06-01',
endDate: '2023-08-31',
progress: 65,
description: '负责项目整体管理和协调工作'
},
{
id: 'p1-2',
name: '李四',
roles: ['安全经理'],
department: '安全管理部',
startDate: '2023-06-01',
endDate: '2023-08-31',
progress: 80,
description: '负责项目安全管理和风险控制'
},
{
id: 'p1-3',
name: '王五',
roles: ['施工人员'],
department: '施工部',
startDate: '2023-06-15',
endDate: '2023-07-15',
progress: 100,
description: '负责现场检测工作'
},
{
id: 'p1-4',
name: '赵六',
roles: ['质量经理'],
department: '质量管理部',
startDate: '2023-06-01',
endDate: '2023-08-31',
progress: 70,
description: '负责项目质量监督和检查'
}
]
},
{
id: 'project2',
name: '叶片维修项目',
personnel: [
{
id: 'p2-1',
name: '钱七',
roles: ['项目经理'],
department: '项目管理部',
startDate: '2023-07-01',
endDate: '2023-09-30',
progress: 30,
description: '负责项目整体管理和协调工作'
},
{
id: 'p2-2',
name: '孙八',
roles: ['施工人员', '工作组长'],
department: '施工部',
startDate: '2023-07-01',
endDate: '2023-09-30',
progress: 25,
description: '负责现场维修工作和小组管理'
},
{
id: 'p2-3',
name: '周九',
roles: ['项目远程顾问'],
department: '技术部',
startDate: '2023-07-01',
endDate: '2023-09-30',
progress: 40,
description: '提供远程技术支持和咨询'
}
]
},
{
id: 'project3',
name: '光伏巡检项目',
personnel: [
{
id: 'p3-1',
name: '吴十',
roles: ['项目经理'],
department: '项目管理部',
startDate: '2023-06-20',
endDate: '2023-07-20',
progress: 80,
description: '负责项目整体管理和协调工作'
},
{
id: 'p3-2',
name: '郑十一',
roles: ['施工人员'],
department: '施工部',
startDate: '2023-06-20',
endDate: '2023-07-20',
progress: 85,
description: '负责现场巡检工作'
},
{
id: 'p3-3',
name: '王十二',
roles: ['现场经理'],
department: '施工部',
startDate: '2023-06-20',
endDate: '2023-07-20',
progress: 75,
description: '负责现场管理和协调'
}
]
},
{
id: 'project4',
name: '大坝裂缝检测项目',
personnel: [
{
id: 'p4-1',
name: '冯十三',
roles: ['项目经理'],
department: '项目管理部',
startDate: '2023-07-10',
endDate: '2023-08-10',
progress: 10,
description: '负责项目整体管理和协调工作'
},
{
id: 'p4-2',
name: '陈十四',
roles: ['施工人员'],
department: '施工部',
startDate: '2023-07-10',
endDate: '2023-08-10',
progress: 5,
description: '负责现场检测工作'
},
{
id: 'p4-3',
name: '褚十五',
roles: ['安全经理'],
department: '安全管理部',
startDate: '2023-07-10',
endDate: '2023-08-10',
progress: 15,
description: '负责项目安全管理和风险控制'
}
]
}
],
roleOptions: [
{ value: '施工人员', label: '施工人员' },
{ value: '安全经理', label: '安全经理' },
{ value: '项目经理', label: '项目经理' },
{ value: '商务', label: '商务' },
{ value: '财务', label: '财务' },
{ value: '高级管理员', label: '高级管理员' },
{ value: '项目远程顾问', label: '项目远程顾问' },
{ value: '质量经理', label: '质量经理' },
{ value: '现场经理', label: '现场经理' },
{ value: '工作组长', label: '工作组长' }
],
employeeOptions: [
{ value: '张三', label: '张三' },
{ value: '李四', label: '李四' },
{ value: '王五', label: '王五' },
{ value: '赵六', label: '赵六' },
{ value: '钱七', label: '钱七' },
{ value: '孙八', label: '孙八' },
{ value: '周九', label: '周九' },
{ value: '吴十', label: '吴十' },
{ value: '郑十一', label: '郑十一' },
{ value: '王十二', label: '王十二' },
{ value: '冯十三', label: '冯十三' },
{ value: '陈十四', label: '陈十四' },
{ value: '褚十五', label: '褚十五' }
],
personnelDialogVisible: false,
personnelDialogTitle: '新增人员安排',
personnelForm: {
id: '',
name: '',
project: '',
roles: [],
department: '',
dateRange: '',
progress: 0,
description: ''
},
personnelDetailVisible: false,
currentPersonnel: {},
// 安全管理数据 (保持不变)
activeSafetyCollapse: ['meeting', 'risk', 'training', 'process'],
safetyMeetings: [
{ title: '风电项目安全工作会议', date: '2023-06-10 14:00', organizer: '张三', participants: 8 },
{ title: '高空作业安全培训', date: '2023-06-15 09:30', organizer: '李四', participants: 12 },
{ title: '设备使用安全规范', date: '2023-06-20 10:00', organizer: '王五', participants: 10 }
],
riskPoints: [
{ name: '高空坠落风险', project: '风电塔下检测项目', level: '高', manager: '张三' },
{ name: '设备操作风险', project: '叶片维修项目', level: '中', manager: '李四' },
{ name: '电气安全风险', project: '光伏巡检项目', level: '低', manager: '王五' }
],
trainings: [
{ title: '高空作业安全培训', type: '现场培训', date: '2023-06-15', participants: 12, status: '已完成' },
{ title: '设备安全操作培训', type: '在线培训', date: '2023-06-25', participants: 15, status: '进行中' },
{ title: '应急处理培训', type: '现场培训', date: '2023-07-05', participants: 10, status: '未开始' }
],
processControls: [
{ name: '风电塔检测流程', project: '风电塔下检测项目', riskPoints: 3, manager: '张三' },
{ name: '叶片维修流程', project: '叶片维修项目', riskPoints: 5, manager: '李四' },
{ name: '光伏巡检流程', project: '光伏巡检项目', riskPoints: 2, manager: '王五' }
],
safetyMeetingDialogVisible: false,
safetyMeetingDialogTitle: '新增安全会议',
safetyMeetingForm: {
title: '',
date: '',
organizer: '',
participants: [],
content: '',
files: []
},
// 质量管理数据 (保持不变)
activeQualityCollapse: ['meeting', 'data', 'standard', 'report'],
qualityMeetings: [
{ title: '风电项目质量分析会', date: '2023-06-12 14:00', organizer: '张三', participants: 8 },
{ title: '检测数据质量评审', date: '2023-06-18 09:30', organizer: '李四', participants: 6 },
{ title: '维修质量总结会', date: '2023-06-22 10:00', organizer: '王五', participants: 5 }
],
dataQualityChecks: [
{ project: '风电塔下检测项目', dataType: '检测数据', checker: '张三', checkDate: '2023-06-15', result: '合格' },
{ project: '叶片维修项目', dataType: '维修记录', checker: '李四', checkDate: '2023-06-20', result: '不合格' },
{ project: '光伏巡检项目', dataType: '巡检数据', checker: '王五', checkDate: '2023-06-25', result: '合格' }
],
qualityStandards: [
{ name: '风电检测质量标准', type: '检测标准', version: 'V1.0', publishDate: '2023-01-15', manager: '张三' },
{ name: '叶片维修质量标准', type: '维修标准', version: 'V1.2', publishDate: '2023-03-20', manager: '李四' },
{ name: '光伏巡检质量标准', type: '巡检标准', version: 'V2.0', publishDate: '2023-05-10', manager: '王五' }
],
reportQualityChecks: [
{ project: '风电塔下检测项目', reportType: '检测报告', checker: '张三', checkDate: '2023-06-16', result: '合格' },
{ project: '叶片维修项目', reportType: '维修报告', checker: '李四', checkDate: '2023-06-21', result: '合格' },
{ project: '光伏巡检项目', reportType: '巡检报告', checker: '王五', checkDate: '2023-06-26', result: '不合格' }
],
qualityMeetingDialogVisible: false,
qualityMeetingDialogTitle: '新增质量会议',
qualityMeetingForm: {
title: '',
date: '',
organizer: '',
participants: [],
content: '',
files: []
}
}
},
computed: {
personnelTableData() {
if (!this.selectedProject) return [];
return this.selectedProject.personnel;
},
filteredPersonnelTableData() {
let data = this.personnelTableData;
// 按角色筛选
if (this.personnelQuery.role && this.personnelQuery.role.length > 0) {
data = data.filter(item => {
return this.personnelQuery.role.some(role => item.roles.includes(role));
});
}
// 按时间范围筛选
if (this.personnelQuery.dateRange && this.personnelQuery.dateRange.length === 2) {
const startDate = new Date(this.personnelQuery.dateRange[0]);
const endDate = new Date(this.personnelQuery.dateRange[1]);
data = data.filter(item => {
const itemStart = new Date(item.startDate);
const itemEnd = new Date(item.endDate);
return itemStart <= endDate && itemEnd >= startDate;
});
}
return data;
}
},
mounted() {
this.initPersonnelChart();
},
methods: {
handleTabClick(tab) {
console.log(tab.name);
},
// 人员分布图方法
handleProjectChange(projectId) {
this.selectedProject = this.projects.find(p => p.id === projectId) || null;
if (this.selectedProject) {
this.personnelForm.project = projectId;
this.initPersonnelChart();
}
},
fetchPersonnelData() {
console.log('查询人员分布数据', this.personnelQuery);
// 这里应该是调用API获取数据
// 模拟数据加载
this.$message({
message: '数据加载成功',
type: 'success'
});
this.initPersonnelChart();
},
resetPersonnelQuery() {
this.personnelQuery = {
project: this.personnelQuery.project, // 保留当前项目
role: [],
dateRange: ''
};
},
initPersonnelChart() {
const chartDom = this.$refs.personnelChart;
if (!chartDom || !this.selectedProject) return;
// 按角色统计人员数量
const roleCount = {};
this.roleOptions.forEach(role => {
roleCount[role.value] = 0;
});
this.selectedProject.personnel.forEach(person => {
person.roles.forEach(role => {
if (roleCount.hasOwnProperty(role)) {
roleCount[role]++;
}
});
});
// 过滤掉数量为0的角色
const chartData = Object.entries(roleCount)
.filter(([_, count]) => count > 0)
.map(([role, count]) => ({ value: count, name: role }));
const myChart = echarts.init(chartDom);
const option = {
title: {
text: `${this.selectedProject.name} - 角色分布`,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: chartData.map(item => item.name)
},
series: [
{
name: '角色分布',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: chartData
}
]
};
myChart.setOption(option);
// 响应式调整
window.addEventListener('resize', function() {
myChart.resize();
});
},
getRoleTagType(role) {
const roleColors = {
'施工人员': '',
'安全经理': 'danger',
'项目经理': 'primary',
'商务': 'success',
'财务': 'warning',
'高级管理员': 'info',
'项目远程顾问': '',
'质量经理': 'success',
'现场经理': 'primary',
'工作组长': 'warning'
};
return roleColors[role] || '';
},
getProgressStatus(progress) {
if (progress >= 80) return 'success';
if (progress >= 50) return '';
if (progress >= 20) return 'warning';
return 'exception';
},
showPersonnelDialog() {
if (!this.selectedProject) {
this.$message.warning('请先选择项目');
return;
}
this.personnelDialogTitle = '新增人员安排';
this.personnelForm = {
id: '',
name: '',
project: this.selectedProject.id,
roles: [],
department: '',
dateRange: '',
progress: 0,
description: ''
};
this.personnelDialogVisible = true;
},
editPersonnel(row) {
this.personnelDialogTitle = '编辑人员安排';
this.personnelForm = {
id: row.id,
name: row.name,
project: this.selectedProject.id,
roles: [...row.roles],
department: row.department,
dateRange: [row.startDate, row.endDate],
progress: row.progress,
description: row.description
};
this.personnelDialogVisible = true;
},
viewPersonnelDetail(row) {
this.currentPersonnel = { ...row };
this.personnelDetailVisible = true;
},
savePersonnel() {
console.log('保存人员安排', this.personnelForm);
if (!this.personnelForm.name || this.personnelForm.roles.length === 0) {
this.$message.error('请填写完整信息');
return;
}
const personnelData = {
id: this.personnelForm.id || `p-${Date.now()}`,
name: this.personnelForm.name,
roles: this.personnelForm.roles,
department: this.personnelForm.department,
startDate: this.personnelForm.dateRange[0],
endDate: this.personnelForm.dateRange[1],
progress: this.personnelForm.progress,
description: this.personnelForm.description
};
if (this.personnelForm.id) {
// 更新现有人员
const index = this.selectedProject.personnel.findIndex(p => p.id === this.personnelForm.id);
if (index !== -1) {
this.selectedProject.personnel.splice(index, 1, personnelData);
}
} else {
// 新增人员
this.selectedProject.personnel.push(personnelData);
}
this.$message({
message: '保存成功',
type: 'success'
});
this.personnelDialogVisible = false;
this.initPersonnelChart();
},
// 安全管理方法 (保持不变)
getRiskTagType(level) {
switch(level) {
case '高': return 'danger';
case '中': return 'warning';
case '低': return 'success';
default: return '';
}
},
showSafetyMeetingDialog() {
this.safetyMeetingDialogTitle = '新增安全会议';
this.safetyMeetingForm = {
title: '',
date: '',
organizer: '',
participants: [],
content: '',
files: []
};
this.safetyMeetingDialogVisible = true;
},
viewSafetyMeeting(row) {
console.log('查看安全会议', row);
this.$message({
message: '查看安全会议详情: ' + row.title,
type: 'info'
});
},
editSafetyMeeting(row) {
this.safetyMeetingDialogTitle = '编辑安全会议';
this.safetyMeetingForm = {
title: row.title,
date: row.date,
organizer: row.organizer,
participants: [],
content: '',
files: []
};
this.safetyMeetingDialogVisible = true;
},
saveSafetyMeeting() {
console.log('保存安全会议', this.safetyMeetingForm);
this.$message({
message: '保存成功',
type: 'success'
});
this.safetyMeetingDialogVisible = false;
// 这里应该是调用API保存数据
},
showRiskDialog() {
this.$message({
message: '打开新增风险点对话框',
type: 'info'
});
},
viewRiskDetail(row) {
this.$message({
message: '查看风险点详情: ' + row.name,
type: 'info'
});
},
editRiskPoint(row) {
this.$message({
message: '编辑风险点: ' + row.name,
type: 'info'
});
},
showTrainingDialog() {
this.$message({
message: '打开新增培训计划对话框',
type: 'info'
});
},
viewTrainingDetail(row) {
this.$message({
message: '查看培训详情: ' + row.title,
type: 'info'
});
},
editTraining(row) {
this.$message({
message: '编辑培训: ' + row.title,
type: 'info'
});
},
showProcessDialog() {
this.$message({
message: '打开新增工艺流程对话框',
type: 'info'
});
},
viewProcessDetail(row) {
this.$message({
message: '查看工艺流程详情: ' + row.name,
type: 'info'
});
},
editProcessControl(row) {
this.$message({
message: '编辑工艺流程: ' + row.name,
type: 'info'
});
},
// 质量管理方法 (保持不变)
showQualityMeetingDialog() {
this.qualityMeetingDialogTitle = '新增质量会议';
this.qualityMeetingForm = {
title: '',
date: '',
organizer: '',
participants: [],
content: '',
files: []
};
this.qualityMeetingDialogVisible = true;
},
viewQualityMeeting(row) {
console.log('查看质量会议', row);
this.$message({
message: '查看质量会议详情: ' + row.title,
type: 'info'
});
},
editQualityMeeting(row) {
this.qualityMeetingDialogTitle = '编辑质量会议';
this.qualityMeetingForm = {
title: row.title,
date: row.date,
organizer: row.organizer,
participants: [],
content: '',
files: []
};
this.qualityMeetingDialogVisible = true;
},
saveQualityMeeting() {
console.log('保存质量会议', this.qualityMeetingForm);
this.$message({
message: '保存成功',
type: 'success'
});
this.qualityMeetingDialogVisible = false;
// 这里应该是调用API保存数据
},
showDataQualityDialog() {
this.$message({
message: '打开新增数据质量检查对话框',
type: 'info'
});
},
viewDataQualityDetail(row) {
this.$message({
message: '查看数据质量检查详情: ' + row.project,
type: 'info'
});
},
editDataQualityCheck(row) {
this.$message({
message: '编辑数据质量检查: ' + row.project,
type: 'info'
});
},
showQualityStandardDialog() {
this.$message({
message: '打开新增质量标准对话框',
type: 'info'
});
},
viewQualityStandard(row) {
this.$message({
message: '查看质量标准详情: ' + row.name,
type: 'info'
});
},
editQualityStandard(row) {
this.$message({
message: '编辑质量标准: ' + row.name,
type: 'info'
});
},
showReportQualityDialog() {
this.$message({
message: '打开新增报告检查对话框',
type: 'info'
});
},
viewReportQualityDetail(row) {
this.$message({
message: '查看报告检查详情: ' + row.project,
type: 'info'
});
},
editReportQualityCheck(row) {
this.$message({
message: '编辑报告检查: ' + row.project,
type: 'info'
});
},
// 通用方法
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
beforeRemove(file, fileList) {
return this.$confirm(`确定移除 ${ file.name }`);
}
}
});
</script>
</body>
</html>