Files
maintenance_system/src/views/zhinengxunjian/renyuanzhuangtai.vue
2025-10-11 09:59:06 +08:00

943 lines
23 KiB
Vue
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.

<template>
<div>
<div class="operation-organization">
<!-- 顶部导航栏 -->
<!-- <div class="navigation-tabs">
<div class="nav-tab" @click="handleInspection1">待办事项</div>
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
<div class="nav-tab" @click="handleInspection3">试验管理</div>
<div class="nav-tab" @click="handleInspection4">报修管理</div>
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
<div class="nav-tab" @click="handleInspection6">工单管理</div>
<div class="nav-tab active" @click="handleInspection7">运维组织</div>
</div> -->
<!-- 选项卡 -->
<div class="tabs-wrapper">
<div style="display: flex; align-items: center; gap: 10px">
<el-button type="primary" @click="handleInspectionManagement1">人员状态</el-button>
<el-button type="primary" @click="handleInspectionManagement2">车辆状态</el-button>
<el-button type="primary" @click="handleInspectionManagement3">班组状态</el-button>
</div>
</div>
<!-- 内容区域 -->
<div class="content-container">
<!-- 左侧数据概览区域 -->
<div class="sidebar">
<div class="stats-card">
<h3 class="stats-title">人员数据总览</h3>
<!-- 使用ECharts饼图替换原有的环形图 -->
<div class="chart-container">
<div ref="personnelChart" class="personnel-chart"></div>
</div>
<div class="stats-grid">
<!-- 总人数 -->
<div class="stat-item">
<div class="stat-icon orange">
<img src="@/assets/images/renyuan.png" class="stat-icon-img" alt="总人数" />
</div>
<div class="stat-label">总人数</div>
<div class="stat-value">{{ totalPersonnel }}</div>
<div class="stat-desc">所有运维人员总数</div>
<div class="stat-change green">+1</div>
</div>
<!-- 在线可用 -->
<div class="stat-item">
<div class="stat-icon blue">
<img src="@/assets/images/zaixian.png" class="stat-icon-img" alt="在线可用" />
</div>
<div class="stat-label">在线可用</div>
<div class="stat-value">{{ availablePersonnel }}</div>
<div class="stat-desc">在线可以投入工作的人员</div>
<div class="stat-change green">+2</div>
</div>
<!-- 工作中 -->
<div class="stat-item">
<div class="stat-icon green">
<img src="@/assets/images/gongzuo.png" class="stat-icon-img" alt="工作中" />
</div>
<div class="stat-label">工作中</div>
<div class="stat-value">{{ workingPersonnel }}</div>
<div class="stat-desc">工作中人数</div>
<div class="stat-change orange">1.2/</div>
</div>
<!-- 离线/休息 -->
<div class="stat-item">
<div class="stat-icon gray">
<img src="@/assets/images/lixian.png" class="stat-icon-img" alt="离线休息" />
</div>
<div class="stat-label">离线/休息</div>
<div class="stat-value">{{ restingPersonnel }}</div>
<div class="stat-desc">休假人员</div>
<div class="stat-change gray">休息离线的人员</div>
</div>
</div>
</div>
</div>
<!-- 右侧人员列表区域带滚动条 -->
<div class="main-content">
<div class="scroll-wrapper">
<!-- 固定的顶部空间不随内容滚动 -->
<div class="fixed-top-space"></div>
<!-- 可滚动的内容区域 -->
<div class="scrollable-content">
<div class="scrollable-inner">
<div class="personnel-grid">
<div v-for="(person, index) in personnelList" :key="index" class="person-card">
<div class="person-header">
<div class="avatar">
<img src="@/assets/images/attendanceperson.png" class="avatar-img" />
<div class="status-indicator" :class="person.statusClass"></div>
</div>
<div class="person-info">
<div class="person-name">{{ person.name }}</div>
<div class="person-status" :class="person.statusClass">{{ person.statusText }}</div>
</div>
</div>
<div class="person-details">
<div class="detail-row">
<div class="detail-item">工号: {{ person.id }}</div>
<div class="detail-item">岗位: {{ person.position }}</div>
</div>
<div class="detail-row">
<div class="detail-item">班组: {{ person.team }}</div>
<div class="detail-item">今日完成: {{ person.completedTasks }}</div>
</div>
<div class="detail-row">
<div class="detail-item full-width">当前任务: {{ person.currentTask || '无' }}</div>
</div>
</div>
<div class="person-actions">
<el-button type="text" @click="viewDetails(person)" class="detail-btn">详情</el-button>
<el-button type="text" @click="assignTask(person)" class="assign-btn">
{{ person.statusClass === 'online' ? '派单' : person.statusClass === 'working' ? '联系' : '留言' }}
</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import router from '@/router';
import * as echarts from 'echarts';
//
// 统计数据(保持原有数据不变)
const totalPersonnel = ref(36);
const availablePersonnel = ref(18);
const workingPersonnel = ref(12);
const restingPersonnel = ref(6);
const onlineRate = ref(82);
// 人员状态数据 - 使用原有数据结构
const personnelStatusData = {
'在线可用': availablePersonnel.value,
'离线休息': restingPersonnel.value
};
// 初始化图表
const personnelChart = ref(null);
let chartInstance = null;
// 人员列表数据(保持不变)
const personnelList = ref([
{
id: 'EMP-2023-001',
name: '张工',
position: '设备维护工程师',
team: '第一运维组',
statusText: '在线可用',
statusClass: 'online',
completedTasks: 2,
currentTask: '',
avatar: 'https://p9-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/937facf77da3466fafaf9ff8f0223333.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-006',
name: '李工',
position: '系统工程师',
team: '第三运维组',
statusText: '工作中',
statusClass: 'working',
completedTasks: 1,
currentTask: 'WO-2023-0619-055',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/63a989286b91488ca0c4a0141041ea41.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-015',
name: '刘工',
position: '安全检查工程师',
team: '第一运维组',
statusText: '工作中',
statusClass: 'working',
completedTasks: 0,
currentTask: 'WO-2023-0618-054',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/0a6cf54a4a1c4623b8365939c8d61adc.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-022',
name: '孙工',
position: '安装调试工程师',
team: '第三运维组',
statusText: '离线休息',
statusClass: 'offline',
completedTasks: 2,
currentTask: '',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/f3e766fffb5d4573945ef7501894c461.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-008',
name: '李工',
position: '系统工程师',
team: '第三运维组',
statusText: '工作中',
statusClass: 'working',
completedTasks: 1,
currentTask: 'WO-2023-0619-055',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/d315aa56eb894980bf090804594ccf13.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-016',
name: '刘工',
position: '安全检查工程师',
team: '第一运维组',
statusText: '工作中',
statusClass: 'working',
completedTasks: 0,
currentTask: 'WO-2023-0618-054',
avatar: 'https://p9-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/937facf77da3466fafaf9ff8f0223333.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-002',
name: '张工',
position: '设备维护工程师',
team: '第一运维组',
statusText: '在线可用',
statusClass: 'online',
completedTasks: 2,
currentTask: '',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/63a989286b91488ca0c4a0141041ea41.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-009',
name: '李工',
position: '系统工程师',
team: '第三运维组',
statusText: '工作中',
statusClass: 'working',
completedTasks: 1,
currentTask: 'WO-2023-0619-055',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/0a6cf54a4a1c4623b8365939c8d61adc.png~tplv-a9rns2rl98-24:720:720.png'
},
{
id: 'EMP-2023-017',
name: '刘工',
position: '安全检查工程师',
team: '第一运维组',
statusText: '工作中',
statusClass: 'working',
completedTasks: 0,
currentTask: 'WO-2023-0618-054',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/f3e766fffb5d4573945ef7501894c461.png~tplv-a9rns2rl98-24:720:720.png'
}
]);
// 初始化饼图
const initChart = () => {
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(personnelChart.value);
const names = Object.keys(personnelStatusData);
const values = Object.values(personnelStatusData).map((item) => Number(Number(item).toFixed(2)));
const sumValue = values.reduce((total, num) => total + num, 0);
// 设置图表配置 - 美化饼图样式,保持原有数据
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c}人 ({d}%)'
},
legend: {
show: true,
orient: 'horizontal',
bottom: 10,
itemWidth: 12,
itemHeight: 12,
textStyle: {
color: '#606266',
fontSize: 14
}
},
series: [
// 主饼图
{
name: '人员状态',
type: 'pie',
radius: ['40%', '70%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 4,
borderColor: '#fff',
borderWidth: 2,
shadowBlur: 6,
shadowColor: 'rgba(0, 0, 0, 0.1)'
},
label: {
show: false,
position: 'outside',
formatter: '{d}%',
fontSize: 14,
fontWeight: 'bold',
color: '#303133',
lineHeight: 20
},
labelLine: {
show: true,
length: 15,
length2: 10,
lineStyle: {
width: 1
}
},
emphasis: {
scale: true,
scaleSize: 15
},
data: [
{
value: availablePersonnel.value,
name: '在线可用人数',
itemStyle: {
color: '#165dff'
}
},
{
value: restingPersonnel.value,
name: '离线休息人数',
itemStyle: {
color: '#40c9c6'
},
emphasis: {
itemStyle: {
shadowBlur: 15,
shadowColor: 'rgba(64, 201, 198, 0.4)'
}
}
}
]
}
]
};
chartInstance.setOption(option);
};
// 监听数据变化,更新图表
watch([availablePersonnel, workingPersonnel, restingPersonnel], () => {
// 更新人员状态数据
personnelStatusData['在线可用'] = availablePersonnel.value;
personnelStatusData['离线休息'] = restingPersonnel.value;
// 重新初始化图表
initChart();
});
// 页面加载完成后初始化图表
onMounted(() => {
initChart();
// 监听窗口大小变化,调整图表尺寸
window.addEventListener('resize', () => {
if (chartInstance) {
chartInstance.resize();
}
});
});
// 选项卡点击事件
const handleTabClick = (tab) => {
console.log('切换到选项卡:', tab.name);
if (tab.name === 'vehicles') {
// 加载车辆状态数据
} else if (tab.name === 'teams') {
// 加载班组状态数据
} else {
// 加载人员状态数据
}
};
// 人员卡片操作
const viewDetails = (person) => {
console.log('查看详情:', person);
// 跳转到人员详情页
};
const assignTask = (person) => {
console.log('指派任务给:', person);
// 打开任务指派对话框
};
// 导航路由跳转
const handleInspection1 = () => {
router.push('/znxj/rili');
};
const handleInspection2 = () => {
router.push('/znxj/xjgl/InspectionManagement');
};
const handleInspection3 = () => {
router.push('/znxj/sygl/shiyanguanli');
};
const handleInspection4 = () => {
router.push('/znxj/bxgl/baoxiuguanli');
};
const handleInspection5 = () => {
router.push('/znxj/qxgl/qiangxiuguanli');
};
const handleInspection6 = () => {
router.push('/znxj/gdgl/gongdanliebiao');
};
const handleInspection7 = () => {
router.push('/znxj/ywzz/renyuanzhuangtai');
};
const handleInspectionManagement1 = () => {
router.push('/znxj/ywzz/renyuanzhuangtai');
};
const handleInspectionManagement2 = () => {
router.push('/znxj/ywzz/cheliangzhuangtai');
};
const handleInspectionManagement3 = () => {
router.push('/znxj/ywzz/banzhuzhuangtai');
};
</script>
<style scoped>
.operation-organization {
padding: 20px;
background-color: #f5f7fa;
min-height: 100vh;
}
/* 选项卡样式 */
.tabs-wrapper {
background-color: #fff;
padding: 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
/* 内容容器样式 */
.content-container {
display: flex;
gap: 24px;
width: 100%;
}
/* 左侧边栏样式 */
.sidebar {
width: 400px;
flex-shrink: 0;
}
.stats-card {
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
padding: 24px;
height: 100%;
}
.stats-title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin: 0 0 24px 0;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
/* 图表容器 */
.chart-container {
border: 1px solid #f0f0f0;
border-radius: 20%;
margin-bottom: 32px;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0;
}
.personnel-chart {
width: 100%;
height: 300px;
}
/* 统计网格 */
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px; /* 项之间的间距 */
}
/* 单个统计项:纵向居中,卡片化 */
.stat-item {
background-color: #fff;
border-radius: 8px;
padding: 16px;
text-align: center;
border: 1px solid #f0f0f0;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03);
display: flex;
flex-direction: column;
align-items: center;
}
/* 图标容器:圆形背景 + 颜色区分 */
.stat-icon {
width: 40px;
height: 40px;
border-radius: 50%; /* 圆形背景 */
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
/* 不同图标背景色 */
.stat-icon.orange {
background-color: #fff2e8;
}
.stat-icon.blue {
background-color: #e6f4ff;
}
.stat-icon.green {
background-color: #f6ffed;
}
.stat-icon.gray {
background-color: #f5f5f5;
}
/* 图标尺寸 */
.stat-icon-img {
width: 24px;
height: 24px;
object-fit: contain;
}
/* 标题:小字号 + 浅灰色 */
.stat-label {
font-size: 14px;
color: #666;
margin-bottom: 4px;
}
/* 数值:大字号 + 深灰色 + 加粗 */
.stat-value {
font-size: 20px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
/* 描述:更小字号 + 浅灰色 */
.stat-desc {
font-size: 12px;
color: #999;
margin-bottom: 6px;
}
/* 变化/额外说明:彩色小字号 */
.stat-change {
font-size: 12px;
}
.stat-change.green {
color: #00b42a;
} /* 绿色(增长) */
.stat-change.orange {
color: #ff7d00;
} /* 橙色(效率) */
.stat-change.gray {
color: #999;
} /* 灰色(说明) */
/* 主内容区域 */
.main-content {
flex: 1;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
overflow: hidden;
padding: 0 28px 28px 28px;
}
/* 滚动包装器 */
.scroll-wrapper {
height: 100%;
position: relative;
}
/* 固定的顶部空间,不随内容滚动 */
.fixed-top-space {
height: 40px;
flex-shrink: 0;
}
/* 可滚动的内容区域 */
.scrollable-content {
max-height: calc(120vh - 340px - 40px); /* 减去顶部固定空间的高度 */
overflow: auto;
scrollbar-width: thin;
scrollbar-color: rgba(150, 150, 150, 0.5) transparent;
}
/* 内容包装器 */
.content-wrapper {
padding: 0 28px 60px 28px;
}
/* Webkit浏览器自定义滚动条样式 */
.scrollable-content::-webkit-scrollbar {
width: 8px;
}
.scrollable-content::-webkit-scrollbar-track {
background: transparent;
border-radius: 4px;
}
.scrollable-content::-webkit-scrollbar-thumb {
background-color: rgba(150, 150, 150, 0.5);
border-radius: 4px;
border: 2px solid transparent;
background-clip: content-box;
}
.scrollable-content::-webkit-scrollbar-thumb:hover {
background-color: rgba(150, 150, 150, 0.8);
}
/* 增强内容区域标题样式 */
.main-content-title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 24px;
padding-bottom: 12px;
border-bottom: 2px solid #f0f0f0;
}
/* 人员网格 */
.personnel-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.person-card {
border: 1px solid #f0f0f0;
border-radius: 8px;
padding: 16px;
transition: all 0.3s ease;
background-color: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
min-height: 220px;
}
.person-card:hover {
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
border-color: #e6f7ff;
transform: translateY(-2px);
}
.person-header {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.avatar {
position: relative;
width: 56px;
height: 56px;
margin-right: 12px;
flex-shrink: 0;
}
.avatar-img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
border: 2px solid #f0f0f0;
}
.status-indicator {
position: absolute;
bottom: 0;
right: 0;
width: 16px;
height: 16px;
border-radius: 50%;
border: 3px solid #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.status-indicator.online {
background-color: #52c41a;
}
.status-indicator.working {
background-color: #165dff;
}
.status-indicator.offline {
background-color: #909399;
}
.person-info {
flex: 1;
}
.person-name {
font-size: 16px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 4px;
line-height: 1.2;
}
.person-status {
font-size: 12px;
padding: 2px 8px;
border-radius: 12px;
display: inline-block;
font-weight: 500;
}
.person-status.online {
background-color: #cbfad6;
color: #19b949;
}
.person-status.working {
background-color: #fff7e6;
color: #fa8c16;
}
.person-status.offline {
background-color: #f5f5f5;
color: #909399;
}
.person-details {
margin-bottom: 16px;
border-top: none;
padding-top: 0;
}
.detail-row {
display: flex;
margin-bottom: 8px;
border-bottom: 1px dotted #f0f0f0;
padding-bottom: 8px;
}
.detail-row:last-child {
margin-bottom: 0;
border-bottom: none;
padding-bottom: 0;
}
.detail-item {
font-size: 13px;
color: #606266;
line-height: 1.6;
flex: 1;
padding: 0 8px;
}
.detail-item:first-child {
padding-left: 0;
}
.detail-item:last-child {
padding-right: 0;
}
.detail-item.full-width {
flex: 1 0 100%;
padding: 0;
}
.person-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
border-top: 1px solid #f0f0f0;
padding-top: 16px;
margin-top: 16px;
}
/* 美化按钮样式 */
.detail-btn,
.assign-btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 6px 16px;
border-radius: 16px;
font-size: 13px;
font-weight: 500;
transition: all 0.3s ease;
border: 1px solid transparent;
}
.detail-btn {
color: #165dff;
background-color: #f0f7ff;
border-color: #d6e4ff;
}
.detail-btn:hover {
color: #094ab2;
background-color: #e6f7ff;
border-color: #91bfff;
}
.assign-btn {
color: #fa8c16;
background-color: #fff9f0;
border-color: #ffe7ba;
}
.assign-btn:hover {
color: #e67700;
background-color: #fff7e6;
border-color: #ffd591;
}
/* 导航栏样式 */
.navigation-tabs {
display: flex;
margin-bottom: 24px;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
padding: 4px;
overflow: hidden;
}
.nav-tab {
padding: 14px 24px;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
color: #606266;
border-right: 1px solid #f0f0f0;
flex: 1;
text-align: center;
position: relative;
z-index: 1;
}
.nav-tab:last-child {
border-right: none;
}
.nav-tab:hover {
color: #165dff;
background-color: #f0f7ff;
}
.nav-tab.active {
background: linear-gradient(135deg, #165dff 0%, #4080ff 100%);
color: #fff;
box-shadow: 0 4px 12px rgba(22, 93, 255, 0.3);
border-right-color: transparent;
}
.nav-tab.active:hover {
background: linear-gradient(135deg, #094ab2 0%, #3366cc 100%);
}
.nav-tab {
cursor: pointer;
user-select: none;
}
/* 滚动条样式 */
.scrollable-content::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.scrollable-content::-webkit-scrollbar-track {
background: #f5f7fa;
border-radius: 3px;
}
.scrollable-content::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
}
.scrollable-content::-webkit-scrollbar-thumb:hover {
background: #909399;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.content-container {
flex-direction: column;
}
.sidebar {
width: 100%;
margin-bottom: 24px;
}
.scrollable-content {
max-height: 600px;
}
}
@media (max-width: 768px) {
.operation-organization {
padding: 10px;
}
.navigation-tabs {
flex-wrap: wrap;
}
.nav-tab {
flex: 1 0 33%;
padding: 10px 0;
font-size: 12px;
}
.personnel-grid {
grid-template-columns: 1fr;
}
.personnel-chart {
height: 250px;
}
}
</style>