Files
td_official/src/views/dhr_demo/renyuanzhuangtai.vue
2025-09-13 18:43:26 +08:00

867 lines
21 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>
<!-- 页面标题 -->
<TitleComponent title="运维组织模块" subtitle="实时监控人员状态、车辆状态和班组状态"></TitleComponent>
<!-- 选项卡 -->
<!-- 选项卡和按钮组合 -->
<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>
<div class="chart-container">
<div class="gauge-chart">
<div class="doughnut-chart">
<!-- 动态镂空环形图 -->
<svg width="200" height="200" viewBox="0 0 200 200">
<!-- 环形背景 -->
<circle cx="100" cy="100" r="70" fill="none" stroke="#f0f2f5" stroke-width="12" class="background-circle" />
<!-- 在线可用部分 - 动态显示 -->
<circle
cx="100"
cy="100"
r="70"
fill="none"
stroke="#165dff"
stroke-width="12"
stroke-linecap="round"
:stroke-dasharray="circumference"
:stroke-dashoffset="onlineOffset"
transform="rotate(-90 100 100)"
class="progress-circle online-progress"
/>
<!-- 离线休息部分 - 动态显示 -->
<circle
cx="100"
cy="100"
r="70"
fill="none"
stroke="#40c9c6"
stroke-width="12"
stroke-linecap="round"
:stroke-dasharray="circumference"
:stroke-dashoffset="offlineOffset"
transform="rotate(-90 100 100)"
class="progress-circle offline-progress"
/>
</svg>
<div class="gauge-text">
<div class="gauge-percentage">{{ onlineRate }}%</div>
<div class="gauge-label">在线可用率</div>
</div>
</div>
</div>
<div class="chart-legend">
<div class="legend-item">
<span class="legend-color online"></span>
<span class="legend-text">在线可用人数 ({{ availablePersonnel }})</span>
</div>
<div class="legend-item">
<span class="legend-color offline"></span>
<span class="legend-text">离线休息人数 ({{ restingPersonnel }})</span>
</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-item">
<div class="stat-value">{{ totalPersonnel }}</div>
<div class="stat-label">总人数</div>
<div class="stat-change">+2</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ availablePersonnel }}</div>
<div class="stat-label">在线可用</div>
<div class="stat-change">+2</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ workingPersonnel }}</div>
<div class="stat-label">工作中</div>
<div class="stat-change">+1</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ restingPersonnel }}</div>
<div class="stat-label">离线休息</div>
<div class="stat-change">-1</div>
</div>
</div>
</div>
</div>
<!-- 右侧人员列表区域带滚动条 -->
<div class="main-content">
<div class="scrollable-content">
<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="person.avatar" alt="头像" 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">{{ person.statusText }}</div>
</div>
</div>
<div class="person-details">
<div class="detail-item">工号: {{ person.id }}</div>
<div class="detail-item">岗位: {{ person.position }}</div>
<div class="detail-item">班组: {{ person.team }}</div>
<div class="detail-item">今日完成: {{ person.completedTasks }}</div>
<div class="detail-item" v-if="person.currentTask">当前任务: {{ person.currentTask }}</div>
<div class="detail-item" v-else>当前任务: </div>
</div>
<div class="person-actions">
<el-button type="text" @click="viewDetails(person)" size="small" class="detail-btn">详情</el-button>
<el-button type="text" @click="assignTask(person)" size="small" class="assign-btn">指派</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import router from '@/router';
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
// 激活的选项卡
const activeTab = ref('personnel');
// 统计数据
const totalPersonnel = ref(36);
const availablePersonnel = ref(18);
const workingPersonnel = ref(12);
const restingPersonnel = ref(6);
const onlineRate = ref(82);
// 环形图相关计算
const radius = 70;
const circumference = 2 * Math.PI * radius;
const onlineOffset = ref(circumference - (onlineRate.value / 100) * circumference);
const offlineOffset = ref(circumference - ((100 - onlineRate.value) / 100) * circumference);
// 监听onlineRate变化更新环形图
watch(onlineRate, (newRate) => {
onlineOffset.value = circumference - (newRate / 100) * circumference;
offlineOffset.value = circumference - ((100 - newRate) / 100) * circumference;
});
// 人员列表数据
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 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('/rili/rili');
};
const handleInspection2 = () => {
router.push('/rili/InspectionManagement');
};
const handleInspection3 = () => {
router.push('/rili/shiyanguanli');
};
const handleInspection4 = () => {
router.push('/rili/baoxiuguanli');
};
const handleInspection5 = () => {
router.push('/rili/qiangxiuguanli');
};
const handleInspection6 = () => {
router.push('/rili/gongdanliebiao');
};
const handleInspection7 = () => {
router.push('/rili/renyuanzhuangtai');
};
const handleInspectionManagement1 = () => {
router.push('/rili/renyuanzhuangtai');
};
const handleInspectionManagement2 = () => {
router.push('/rili/cheliangzhuangtai');
};
const handleInspectionManagement3 = () => {
router.push('/rili/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);
}
.custom-tabs {
padding-top: 1px;
}
.custom-tabs .el-tabs__header {
margin: 0 -20px;
padding: 0 20px;
border-bottom: 1px solid #e4e7ed;
}
.custom-tabs .el-tabs__nav-wrap::after {
height: 0;
}
.custom-tabs .el-tabs__item {
font-size: 14px;
color: #606266;
padding: 16px 20px;
margin-right: 20px;
}
.custom-tabs .el-tabs__item.is-active {
color: #165dff;
font-weight: 500;
border-bottom: 2px solid #165dff;
}
.custom-tabs .el-tabs__item:hover {
color: #165dff;
}
/* 内容容器样式 */
.content-container {
display: flex;
gap: 24px;
width: 100%;
}
/* 左侧边栏样式 */
.sidebar {
width: 320px;
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 {
margin-bottom: 32px;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0;
}
.gauge-chart {
width: 200px;
height: 200px;
position: relative;
}
.doughnut-chart {
width: 100%;
height: 100%;
position: relative;
}
.gauge-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.gauge-percentage {
font-size: 36px;
font-weight: 700;
color: #165dff;
line-height: 1;
}
.gauge-label {
font-size: 15px;
color: #606266;
margin-top: 6px;
font-weight: 500;
}
/* 环形图动画效果 */
.background-circle {
stroke: #f0f2f5;
}
.progress-circle {
transition: stroke-dashoffset 1s ease-in-out;
}
.online-progress {
stroke: #165dff;
filter: drop-shadow(0 0 6px rgba(22, 93, 255, 0.2));
}
.offline-progress {
stroke: #40c9c6;
filter: drop-shadow(0 0 6px rgba(64, 201, 198, 0.2));
}
/* 图例样式 */
.chart-legend {
margin-top: 16px;
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 4px;
flex-shrink: 0;
}
.legend-color.online {
background-color: #165dff;
box-shadow: 0 2px 4px rgba(22, 93, 255, 0.3);
}
.legend-color.offline {
background-color: #40c9c6;
box-shadow: 0 2px 4px rgba(64, 201, 198, 0.3);
}
.legend-text {
font-size: 13px;
color: #606266;
font-weight: 500;
}
/* 统计网格 */
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.stat-item {
background-color: #f8fafc;
border-radius: 10px;
padding: 16px 12px;
text-align: center;
border: 1px solid #f0f0f0;
transition: all 0.3s ease;
}
.stat-item:hover {
background-color: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transform: translateY(-1px);
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: #2c3e50;
margin-bottom: 6px;
line-height: 1.2;
}
.stat-label {
font-size: 13px;
color: #606266;
margin-bottom: 4px;
font-weight: 500;
}
.stat-change {
font-size: 12px;
color: #52c41a;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 2px;
}
.stat-change::before {
content: '↗';
font-size: 10px;
}
.stat-change {
font-size: 12px;
color: #52c41a;
}
/* 主内容区域 */
.main-content {
flex: 1;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
overflow: hidden;
}
.scrollable-content {
max-height: calc(100vh - 340px);
overflow-y: auto;
padding: 28px;
}
/* 增强内容区域标题样式 */
.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(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
.person-card {
border: 1px solid #f0f0f0;
border-radius: 12px;
padding: 20px;
transition: all 0.3s ease;
background-color: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.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: 20px;
}
.avatar {
position: relative;
width: 64px;
height: 64px;
margin-right: 16px;
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: 18px;
height: 18px;
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: 18px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 6px;
line-height: 1.2;
}
.person-status {
font-size: 13px;
padding: 3px 10px;
border-radius: 15px;
display: inline-block;
font-weight: 500;
}
.person-status.online {
background-color: #f6ffed;
color: #52c41a;
}
.person-status.working {
background-color: #e8f3ff;
color: #165dff;
}
.person-status.offline {
background-color: #f5f5f5;
color: #909399;
}
.person-details {
margin-bottom: 20px;
border-top: 1px dashed #f0f0f0;
padding-top: 16px;
}
.detail-item {
font-size: 13px;
color: #606266;
margin-bottom: 10px;
line-height: 1.6;
}
.detail-item:last-child {
margin-bottom: 0;
}
.person-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
border-top: 1px dashed #f0f0f0;
padding-top: 16px;
}
/* 美化按钮样式 */
.detail-btn,
.assign-btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 6px 16px;
border-radius: 6px;
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;
}
}
</style>