1032 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			1032 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | ||
|   <div>
 | ||
|     <div class="execution-records">
 | ||
|       <!-- 顶部导航栏 -->
 | ||
|       <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="search-filter">
 | ||
|         <div class="search-container">
 | ||
|           <el-input
 | ||
|             v-model="searchKeyword"
 | ||
|             placeholder="搜索班组名称或编号"
 | ||
|             class="search-input"
 | ||
|             suffix-icon="el-icon-search"
 | ||
|             @keyup.enter="handleSearch"
 | ||
|           ></el-input>
 | ||
|           <el-button type="primary" class="new-team-btn" @click="handleCreateTeam"> <i class="el-icon-plus"></i> 新增班组 </el-button>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 班组卡片和图表区域 -->
 | ||
|       <div class="team-cards-section">
 | ||
|         <div class="team-cards-container">
 | ||
|           <!-- 班组卡片 -->
 | ||
|           <div v-for="team in filteredTeams" :key="team.id" class="team-card">
 | ||
|             <div class="team-header">
 | ||
|               <div class="team-name">
 | ||
|                 {{ team.name }}
 | ||
|                 <el-tag :type="getTeamStatusType(team.status)" class="team-status-tag">
 | ||
|                   {{ team.status }}
 | ||
|                 </el-tag>
 | ||
|               </div>
 | ||
|               <div class="team-region">负责区域: {{ team.region }}</div>
 | ||
|             </div>
 | ||
| 
 | ||
|             <div class="team-details">
 | ||
|               <div class="team-leader">组长: {{ team.leader }}</div>
 | ||
|               <div class="team-members">
 | ||
|                 成员数: {{ team.memberCount }}人
 | ||
|                 <div class="member-avatars">
 | ||
|                   <el-avatar v-for="i in Math.min(5, team.memberCount)" :key="i" size="small" class="avatar-small">{{
 | ||
|                     String.fromCharCode(64 + i)
 | ||
|                   }}</el-avatar>
 | ||
|                   <span v-if="team.memberCount > 5" class="avatar-more">+{{ team.memberCount - 5 }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
| 
 | ||
|               <div class="team-tasks">
 | ||
|                 <div class="task-label">当前任务数:</div>
 | ||
|                 <div class="task-progress">
 | ||
|                   <div class="progress-bar" :style="{ width: team.taskProgress }"></div>
 | ||
|                   <div class="progress-text">{{ team.currentTasks }}/{{ team.totalTasks }}</div>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
| 
 | ||
|               <div class="team-completion">
 | ||
|                 <div class="completion-label">今日完成率:</div>
 | ||
|                 <div class="completion-bar">
 | ||
|                   <div class="completion-fill" :style="{ width: team.completionRate }" :class="getCompletionClass(team.completionRate)"></div>
 | ||
|                   <div class="completion-text">{{ team.completionRate }}</div>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
| 
 | ||
|             <div class="team-actions">
 | ||
|               <el-button type="text" @click="handleViewTeamDetails(team)" class="action-btn">查看详情</el-button>
 | ||
|               <el-button type="text" @click="handleAssignTasks(team)" class="action-btn assign-btn" v-if="team.status === '正常运行'">
 | ||
|                 分配任务
 | ||
|               </el-button>
 | ||
|               <el-button type="text" @click="handleSupport(team)" class="action-btn support-btn" v-if="team.status === '人员紧张'">
 | ||
|                 支援请求
 | ||
|               </el-button>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <!-- 图表卡片 -->
 | ||
|           <div class="chart-card-container">
 | ||
|             <div class="chart-card">
 | ||
|               <div class="chart-header">
 | ||
|                 <h3>班组周任务情况</h3>
 | ||
|               </div>
 | ||
|               <div class="chart-content">
 | ||
|                 <!-- 环形图容器 - 增加明确的尺寸设置 -->
 | ||
|                 <div id="teamChart" class="chart-container"></div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 班组任务完成情况表格 -->
 | ||
|       <div class="table-wrapper">
 | ||
|         <el-table :data="pagedTableData" stripe style="width: 100%" highlight-current-row class="custom-table">
 | ||
|           <el-table-column align="center" prop="name" label="班组名称" min-width="120"></el-table-column>
 | ||
|           <el-table-column align="center" prop="leader" label="组长" min-width="100"></el-table-column>
 | ||
|           <el-table-column align="center" prop="memberCount" label="成员数" min-width="100"></el-table-column>
 | ||
|           <el-table-column align="center" prop="region" label="负责区域" min-width="150"></el-table-column>
 | ||
|           <el-table-column align="center" prop="currentTasks" label="当前任务" min-width="100"></el-table-column>
 | ||
|           <el-table-column align="center" prop="unfinishedTasks" label="未完成" min-width="100"></el-table-column>
 | ||
|           <el-table-column align="center" prop="completionRate" label="完成率" min-width="120">
 | ||
|             <template #default="scope">
 | ||
|               <div class="completion-bar">
 | ||
|                 <div
 | ||
|                   class="completion-progress"
 | ||
|                   :style="{ width: scope.row.completionRate }"
 | ||
|                   :class="getCompletionClass(scope.row.completionRate)"
 | ||
|                 ></div>
 | ||
|               </div>
 | ||
|             </template>
 | ||
|           </el-table-column>
 | ||
|           <el-table-column align="center" prop="status" label="状态" min-width="100">
 | ||
|             <template #default="scope">
 | ||
|               <el-tag :type="getTeamStatusType(scope.row.status)" class="status-tag">
 | ||
|                 {{ scope.row.status }}
 | ||
|               </el-tag>
 | ||
|             </template>
 | ||
|           </el-table-column>
 | ||
|           <el-table-column align="center" label="操作" min-width="180" fixed="right">
 | ||
|             <template #default="scope">
 | ||
|               <el-button type="text" @click="handleViewTeamDetails(scope.row)" size="small" class="action-btn">详情</el-button>
 | ||
|               <el-button type="text" @click="handleManageTeam(scope.row)" size="small" class="action-btn manage-btn"> 管理 </el-button>
 | ||
|               <el-button
 | ||
|                 type="text"
 | ||
|                 @click="handleSupport(scope.row)"
 | ||
|                 size="small"
 | ||
|                 class="action-btn support-btn"
 | ||
|                 v-if="scope.row.status === '人员紧张'"
 | ||
|               >
 | ||
|                 支援
 | ||
|               </el-button>
 | ||
|             </template>
 | ||
|           </el-table-column>
 | ||
|         </el-table>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 分页区域 -->
 | ||
|       <div class="pagination-section">
 | ||
|         <div class="pagination-info">
 | ||
|           显示第{{ (currentPage - 1) * pageSize + 1 }}到{{ Math.min(currentPage * pageSize, total) }}条,共{{ total }}条记录
 | ||
|         </div>
 | ||
|         <div class="pagination-controls">
 | ||
|           <el-pagination
 | ||
|             @size-change="handleSizeChange"
 | ||
|             @current-change="handleCurrentChange"
 | ||
|             :current-page="currentPage"
 | ||
|             :page-sizes="[10, 20, 30, 40]"
 | ||
|             :page-size="pageSize"
 | ||
|             layout="prev, pager, next, jumper"
 | ||
|             :total="total"
 | ||
|             background
 | ||
|           >
 | ||
|           </el-pagination>
 | ||
|         </div>
 | ||
|       </div>
 | ||
|     </div>
 | ||
|   </div>
 | ||
| </template>
 | ||
| 
 | ||
| <script setup>
 | ||
| import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
 | ||
| import router from '@/router';
 | ||
| import TitleComponent from './TitleComponent.vue';
 | ||
| import * as echarts from 'echarts'; // 导入ECharts
 | ||
| 
 | ||
| // 搜索条件
 | ||
| const searchKeyword = ref('');
 | ||
| 
 | ||
| // 班组数据
 | ||
| const rawTeamData = ref([
 | ||
|   {
 | ||
|     id: 1,
 | ||
|     name: '第一运维组',
 | ||
|     leader: '张工',
 | ||
|     memberCount: 12,
 | ||
|     region: '东区、南区',
 | ||
|     currentTasks: 5,
 | ||
|     totalTasks: 23,
 | ||
|     unfinishedTasks: 18,
 | ||
|     completionRate: '85%',
 | ||
|     taskProgress: '22%',
 | ||
|     status: '正常运行'
 | ||
|   },
 | ||
|   {
 | ||
|     id: 2,
 | ||
|     name: '第二运维组',
 | ||
|     leader: '李工',
 | ||
|     memberCount: 10,
 | ||
|     region: '西区、北区',
 | ||
|     currentTasks: 7,
 | ||
|     totalTasks: 22,
 | ||
|     unfinishedTasks: 15,
 | ||
|     completionRate: '65%',
 | ||
|     taskProgress: '32%',
 | ||
|     status: '正常运行'
 | ||
|   },
 | ||
|   {
 | ||
|     id: 3,
 | ||
|     name: '第三运维组',
 | ||
|     leader: '赵工',
 | ||
|     memberCount: 14,
 | ||
|     region: '市中心区域',
 | ||
|     currentTasks: 12,
 | ||
|     totalTasks: 24,
 | ||
|     unfinishedTasks: 12,
 | ||
|     completionRate: '45%',
 | ||
|     taskProgress: '50%',
 | ||
|     status: '人员紧张'
 | ||
|   }
 | ||
| ]);
 | ||
| 
 | ||
| // 分页相关
 | ||
| const currentPage = ref(1);
 | ||
| const pageSize = ref(10);
 | ||
| const total = ref(rawTeamData.value.length);
 | ||
| 
 | ||
| // 筛选后的班组数据
 | ||
| const filteredTeams = computed(() => {
 | ||
|   let teams = [...rawTeamData.value];
 | ||
| 
 | ||
|   if (searchKeyword.value) {
 | ||
|     const keyword = searchKeyword.value.toLowerCase();
 | ||
|     teams = teams.filter(
 | ||
|       (team) =>
 | ||
|         team.name.toLowerCase().includes(keyword) || team.region.toLowerCase().includes(keyword) || team.leader.toLowerCase().includes(keyword)
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   return teams;
 | ||
| });
 | ||
| 
 | ||
| // 分页处理后的数据
 | ||
| const pagedTableData = computed(() => {
 | ||
|   // 更新总条数
 | ||
|   total.value = filteredTeams.value.length;
 | ||
| 
 | ||
|   // 分页处理
 | ||
|   const startIndex = (currentPage.value - 1) * pageSize.value;
 | ||
|   const endIndex = startIndex + pageSize.value;
 | ||
|   return filteredTeams.value.slice(startIndex, endIndex);
 | ||
| });
 | ||
| 
 | ||
| // 获取班组状态标签样式
 | ||
| const getTeamStatusType = (status) => {
 | ||
|   const statusMap = {
 | ||
|     '正常运行': 'success',
 | ||
|     '人员紧张': 'warning'
 | ||
|   };
 | ||
|   return statusMap[status] || 'default';
 | ||
| };
 | ||
| 
 | ||
| // 获取完成度样式
 | ||
| const getCompletionClass = (completion) => {
 | ||
|   const percentage = parseInt(completion);
 | ||
|   if (percentage >= 80) return 'high';
 | ||
|   if (percentage >= 60) return 'medium';
 | ||
|   return 'low';
 | ||
| };
 | ||
| 
 | ||
| // 搜索处理
 | ||
| const handleSearch = () => {
 | ||
|   currentPage.value = 1; // 重置到第一页
 | ||
| };
 | ||
| 
 | ||
| // 分页事件
 | ||
| const handleSizeChange = (val) => {
 | ||
|   pageSize.value = val;
 | ||
|   currentPage.value = 1;
 | ||
| };
 | ||
| 
 | ||
| const handleCurrentChange = (val) => {
 | ||
|   currentPage.value = val;
 | ||
| };
 | ||
| 
 | ||
| // 操作按钮事件
 | ||
| const handleViewTeamDetails = (team) => {
 | ||
|   console.log('查看班组详情:', team);
 | ||
|   // 实际应用中这里会跳转到详情页
 | ||
| };
 | ||
| 
 | ||
| const handleAssignTasks = (team) => {
 | ||
|   console.log('分配任务给班组:', team);
 | ||
|   // 实际应用中这里会打开任务分配表单
 | ||
| };
 | ||
| 
 | ||
| const handleManageTeam = (team) => {
 | ||
|   console.log('管理班组:', team);
 | ||
|   // 实际应用中这里会打开班组管理页面
 | ||
| };
 | ||
| 
 | ||
| const handleSupport = (team) => {
 | ||
|   console.log('请求支援:', team);
 | ||
|   // 实际应用中这里会打开支援请求表单
 | ||
| };
 | ||
| 
 | ||
| const handleCreateTeam = () => {
 | ||
|   console.log('创建新班组');
 | ||
|   // 实际应用中这里会打开新建班组表单
 | ||
| };
 | ||
| 
 | ||
| // 导航路由跳转
 | ||
| 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');
 | ||
| };
 | ||
| 
 | ||
| // ECharts实例
 | ||
| let chartInstance = null;
 | ||
| 
 | ||
| // 初始化ECharts环形图
 | ||
| const initChart = () => {
 | ||
|   // 使用nextTick确保DOM已完全渲染
 | ||
|   nextTick(() => {
 | ||
|     // 获取DOM元素
 | ||
|     const chartDom = document.getElementById('teamChart');
 | ||
|     if (!chartDom) {
 | ||
|       console.error('图表容器元素未找到');
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     // 销毁已存在的图表实例
 | ||
|     if (chartInstance) {
 | ||
|       chartInstance.dispose();
 | ||
|     }
 | ||
| 
 | ||
|     // 创建新的图表实例
 | ||
|     chartInstance = echarts.init(chartDom);
 | ||
| 
 | ||
|     // 准备图表数据
 | ||
|     const option = {
 | ||
|       tooltip: {
 | ||
|         trigger: 'item',
 | ||
|         formatter: '{a} <br/>{b}: {c} ({d}%)',
 | ||
|         backgroundColor: 'rgba(255, 255, 255, 0.95)',
 | ||
|         borderColor: '#e8e8e8',
 | ||
|         textStyle: {
 | ||
|           color: '#333'
 | ||
|         },
 | ||
|         padding: 12,
 | ||
|         borderRadius: 8,
 | ||
|         boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
 | ||
|       },
 | ||
|       legend: {
 | ||
|         orient: 'vertical',
 | ||
|         left: 10,
 | ||
|         top: 'center',
 | ||
|         itemWidth: 14,
 | ||
|         itemHeight: 14,
 | ||
|         textStyle: {
 | ||
|           fontSize: 12,
 | ||
|           color: '#666'
 | ||
|         },
 | ||
|         itemGap: 12
 | ||
|       },
 | ||
|       series: [
 | ||
|         {
 | ||
|           name: '已完成任务',
 | ||
|           type: 'pie',
 | ||
|           radius: ['40%', '70%'],
 | ||
|           center: ['60%', '50%'],
 | ||
|           avoidLabelOverlap: false,
 | ||
|           itemStyle: {
 | ||
|             borderRadius: 10,
 | ||
|             borderColor: '#fff',
 | ||
|             borderWidth: 2,
 | ||
|             shadowBlur: 15,
 | ||
|             shadowOffsetX: 0,
 | ||
|             shadowColor: 'rgba(0, 0, 0, 0.15)'
 | ||
|           },
 | ||
|           label: {
 | ||
|             show: false,
 | ||
|             position: 'center'
 | ||
|           },
 | ||
|           emphasis: {
 | ||
|             scale: true,
 | ||
|             scaleSize: 15,
 | ||
|             label: {
 | ||
|               show: true,
 | ||
|               fontSize: 18,
 | ||
|               fontWeight: 'bold',
 | ||
|               color: '#333'
 | ||
|             },
 | ||
|             itemStyle: {
 | ||
|               shadowBlur: 25,
 | ||
|               shadowOffsetX: 0,
 | ||
|               shadowColor: 'rgba(0, 0, 0, 0.25)'
 | ||
|             }
 | ||
|           },
 | ||
|           labelLine: {
 | ||
|             show: false
 | ||
|           },
 | ||
|           animationType: 'scale',
 | ||
|           animationEasing: 'elasticOut',
 | ||
|           animationDelay: function (idx) {
 | ||
|             return Math.random() * 300;
 | ||
|           },
 | ||
|           data: [
 | ||
|             { value: 19, name: '第一运维组', itemStyle: { color: '#1890ff' } },
 | ||
|             { value: 14, name: '第二运维组', itemStyle: { color: '#faad14' } },
 | ||
|             { value: 11, name: '第三运维组', itemStyle: { color: '#ff4d4f' } },
 | ||
|             { value: 4 + 8 + 13, name: '未完成', itemStyle: { color: '#f5f5f5' } }
 | ||
|           ]
 | ||
|         }
 | ||
|       ]
 | ||
|     };
 | ||
| 
 | ||
|     // 设置图表配置项
 | ||
|     chartInstance.setOption(option);
 | ||
| 
 | ||
|     // 处理窗口大小变化
 | ||
|     const handleResize = () => {
 | ||
|       chartInstance.resize();
 | ||
|     };
 | ||
| 
 | ||
|     window.addEventListener('resize', handleResize);
 | ||
| 
 | ||
|     // 组件卸载时移除事件监听
 | ||
|     onUnmounted(() => {
 | ||
|       window.removeEventListener('resize', handleResize);
 | ||
|     });
 | ||
|   });
 | ||
| };
 | ||
| 
 | ||
| // 组件挂载时初始化图表
 | ||
| onMounted(() => {
 | ||
|   initChart();
 | ||
| });
 | ||
| 
 | ||
| // 组件卸载时销毁图表实例
 | ||
| onUnmounted(() => {
 | ||
|   if (chartInstance) {
 | ||
|     chartInstance.dispose();
 | ||
|     chartInstance = null;
 | ||
|   }
 | ||
| });
 | ||
| </script>
 | ||
| 
 | ||
| <style scoped>
 | ||
| .execution-records {
 | ||
|   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);
 | ||
| }
 | ||
| /* 搜索和筛选区样式 */
 | ||
| .search-filter {
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 8px;
 | ||
|   margin-bottom: 24px;
 | ||
|   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
 | ||
|   overflow: hidden;
 | ||
| }
 | ||
| 
 | ||
| .search-container {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   padding: 16px 24px;
 | ||
|   border-bottom: 1px solid #f0f0f0;
 | ||
| }
 | ||
| 
 | ||
| .search-input {
 | ||
|   flex: 1;
 | ||
|   max-width: 500px;
 | ||
|   height: 36px;
 | ||
| }
 | ||
| 
 | ||
| .new-team-btn {
 | ||
|   white-space: nowrap;
 | ||
|   margin-left: 16px;
 | ||
| }
 | ||
| 
 | ||
| /* 班组卡片区域样式 */
 | ||
| .team-cards-section {
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 8px;
 | ||
|   margin-bottom: 24px;
 | ||
|   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
 | ||
|   padding: 24px;
 | ||
| }
 | ||
| 
 | ||
| .team-cards-container {
 | ||
|   display: grid;
 | ||
|   grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
 | ||
|   gap: 24px;
 | ||
| }
 | ||
| 
 | ||
| .team-card {
 | ||
|   border: 1px solid #f0f0f0;
 | ||
|   border-radius: 12px;
 | ||
|   padding: 24px;
 | ||
|   transition: all 0.3s ease;
 | ||
|   background-color: #fff;
 | ||
|   box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04);
 | ||
|   position: relative;
 | ||
|   overflow: hidden;
 | ||
| }
 | ||
| 
 | ||
| .team-card::before {
 | ||
|   content: '';
 | ||
|   position: absolute;
 | ||
|   top: 0;
 | ||
|   left: 0;
 | ||
|   width: 4px;
 | ||
|   height: 100%;
 | ||
|   background: linear-gradient(180deg, #1890ff 0%, #40a9ff 100%);
 | ||
|   opacity: 0;
 | ||
|   transition: opacity 0.3s ease;
 | ||
| }
 | ||
| 
 | ||
| .team-card:nth-child(2)::before {
 | ||
|   background: linear-gradient(180deg, #faad14 0%, #ffc53d 100%);
 | ||
| }
 | ||
| 
 | ||
| .team-card:nth-child(3)::before {
 | ||
|   background: linear-gradient(180deg, #ff4d4f 0%, #ff7a45 100%);
 | ||
| }
 | ||
| 
 | ||
| .team-card:hover {
 | ||
|   transform: translateY(-5px);
 | ||
|   box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
 | ||
|   border-color: transparent;
 | ||
| }
 | ||
| 
 | ||
| .team-card:hover::before {
 | ||
|   opacity: 1;
 | ||
| }
 | ||
| 
 | ||
| /* 图表卡片容器样式 */
 | ||
| .chart-card-container {
 | ||
|   display: flex;
 | ||
|   align-items: stretch;
 | ||
| }
 | ||
| 
 | ||
| /* 图表卡片样式 */
 | ||
| .chart-card {
 | ||
|   flex: 1;
 | ||
|   border: 1px solid #f0f0f0;
 | ||
|   border-radius: 8px;
 | ||
|   padding: 20px;
 | ||
|   background-color: #fff;
 | ||
|   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
| }
 | ||
| 
 | ||
| .chart-header {
 | ||
|   margin-bottom: 16px;
 | ||
|   padding-bottom: 12px;
 | ||
|   border-bottom: 1px dashed #f0f0f0;
 | ||
| }
 | ||
| 
 | ||
| .chart-header h3 {
 | ||
|   font-size: 16px;
 | ||
|   font-weight: 600;
 | ||
|   color: #303133;
 | ||
|   margin: 0;
 | ||
| }
 | ||
| 
 | ||
| .chart-content {
 | ||
|   flex: 1;
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   justify-content: center;
 | ||
| }
 | ||
| 
 | ||
| /* 确保图表容器有明确的尺寸 */
 | ||
| .chart-container {
 | ||
|   width: 100%;
 | ||
|   height: 100%;
 | ||
|   min-height: 280px; /* 增加最小高度确保图表可见 */
 | ||
| }
 | ||
| 
 | ||
| .team-header {
 | ||
|   margin-bottom: 16px;
 | ||
|   padding-bottom: 12px;
 | ||
|   border-bottom: 1px dashed #f0f0f0;
 | ||
| }
 | ||
| 
 | ||
| .team-name {
 | ||
|   font-size: 18px;
 | ||
|   font-weight: 600;
 | ||
|   color: #303133;
 | ||
|   margin-bottom: 8px;
 | ||
|   display: flex;
 | ||
|   justify-content: space-between;
 | ||
|   align-items: center;
 | ||
| }
 | ||
| 
 | ||
| .team-region {
 | ||
|   font-size: 14px;
 | ||
|   color: #606266;
 | ||
| }
 | ||
| 
 | ||
| .team-details {
 | ||
|   margin-bottom: 20px;
 | ||
| }
 | ||
| 
 | ||
| .team-leader,
 | ||
| .team-members {
 | ||
|   margin-bottom: 12px;
 | ||
|   font-size: 14px;
 | ||
|   color: #303133;
 | ||
| }
 | ||
| 
 | ||
| /* 成员头像展示 */
 | ||
| .member-avatars {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   margin-top: 8px;
 | ||
|   gap: -8px;
 | ||
| }
 | ||
| 
 | ||
| .avatar-small {
 | ||
|   width: 24px;
 | ||
|   height: 24px;
 | ||
|   line-height: 24px;
 | ||
|   font-size: 12px;
 | ||
|   border: 2px solid #fff;
 | ||
|   background-color: #e6f7ff;
 | ||
|   color: #1890ff;
 | ||
|   margin-left: -8px;
 | ||
| }
 | ||
| 
 | ||
| .avatar-small:nth-child(2) {
 | ||
|   background-color: #fff7e6;
 | ||
|   color: #fa8c16;
 | ||
| }
 | ||
| 
 | ||
| .avatar-small:nth-child(3) {
 | ||
|   background-color: #fff1f0;
 | ||
|   color: #ff4d4f;
 | ||
| }
 | ||
| 
 | ||
| .avatar-small:nth-child(4) {
 | ||
|   background-color: #f6ffed;
 | ||
|   color: #52c41a;
 | ||
| }
 | ||
| 
 | ||
| .avatar-small:nth-child(5) {
 | ||
|   background-color: #f0f2f5;
 | ||
|   color: #666;
 | ||
| }
 | ||
| 
 | ||
| .avatar-more {
 | ||
|   font-size: 12px;
 | ||
|   color: #999;
 | ||
|   margin-left: 4px;
 | ||
| }
 | ||
| 
 | ||
| .team-tasks,
 | ||
| .team-completion {
 | ||
|   margin-bottom: 20px;
 | ||
| }
 | ||
| 
 | ||
| .task-label,
 | ||
| .completion-label {
 | ||
|   display: block;
 | ||
|   margin-bottom: 8px;
 | ||
|   font-size: 14px;
 | ||
|   color: #606266;
 | ||
|   font-weight: 500;
 | ||
| }
 | ||
| 
 | ||
| .task-progress,
 | ||
| .completion-bar {
 | ||
|   width: 100%;
 | ||
|   height: 10px;
 | ||
|   background-color: #f0f2f5;
 | ||
|   border-radius: 5px;
 | ||
|   overflow: hidden;
 | ||
|   position: relative;
 | ||
|   box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
 | ||
| }
 | ||
| 
 | ||
| .progress-bar,
 | ||
| .completion-fill {
 | ||
|   height: 100%;
 | ||
|   border-radius: 5px;
 | ||
|   transition: width 0.3s ease;
 | ||
|   position: relative;
 | ||
|   overflow: hidden;
 | ||
| }
 | ||
| 
 | ||
| .progress-bar {
 | ||
|   background: linear-gradient(90deg, #fa8c16 0%, #ffc53d 100%);
 | ||
|   box-shadow: 0 2px 8px rgba(250, 140, 22, 0.3);
 | ||
| }
 | ||
| 
 | ||
| .progress-bar::after,
 | ||
| .completion-fill::after {
 | ||
|   content: '';
 | ||
|   position: absolute;
 | ||
|   top: 0;
 | ||
|   left: 0;
 | ||
|   right: 0;
 | ||
|   bottom: 0;
 | ||
|   background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100%);
 | ||
|   animation: shimmer 2s infinite;
 | ||
| }
 | ||
| 
 | ||
| @keyframes shimmer {
 | ||
|   0% {
 | ||
|     transform: translateX(-100%);
 | ||
|   }
 | ||
|   100% {
 | ||
|     transform: translateX(100%);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .progress-text,
 | ||
| .completion-text {
 | ||
|   position: absolute;
 | ||
|   right: 6px;
 | ||
|   top: 50%;
 | ||
|   transform: translateY(-50%);
 | ||
|   font-size: 12px;
 | ||
|   color: #606266;
 | ||
| }
 | ||
| 
 | ||
| .completion-fill.high {
 | ||
|   background: linear-gradient(90deg, #52c41a 0%, #73d13d 100%);
 | ||
|   box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
 | ||
| }
 | ||
| 
 | ||
| .completion-fill.medium {
 | ||
|   background: linear-gradient(90deg, #faad14 0%, #ffc53d 100%);
 | ||
|   box-shadow: 0 2px 8px rgba(250, 173, 20, 0.3);
 | ||
| }
 | ||
| 
 | ||
| .completion-fill.low {
 | ||
|   background: linear-gradient(90deg, #ff4d4f 0%, #ff7a45 100%);
 | ||
|   box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);
 | ||
| }
 | ||
| 
 | ||
| .team-actions {
 | ||
|   display: flex;
 | ||
|   justify-content: flex-end;
 | ||
|   gap: 12px;
 | ||
|   padding-top: 16px;
 | ||
|   border-top: 1px dashed #f0f0f0;
 | ||
| }
 | ||
| 
 | ||
| /* 表格样式优化 */
 | ||
| .table-wrapper {
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 12px;
 | ||
|   margin-bottom: 24px;
 | ||
|   box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
 | ||
|   overflow: hidden;
 | ||
| }
 | ||
| 
 | ||
| .custom-table {
 | ||
|   border-collapse: collapse;
 | ||
| }
 | ||
| 
 | ||
| .custom-table th {
 | ||
|   background-color: #fafafa;
 | ||
|   font-weight: 600;
 | ||
|   color: #303133;
 | ||
|   font-size: 14px;
 | ||
|   padding: 12px 8px;
 | ||
|   text-align: center;
 | ||
|   border-bottom: 2px solid #f0f0f0;
 | ||
| }
 | ||
| 
 | ||
| .custom-table td {
 | ||
|   padding: 12px 8px;
 | ||
|   text-align: center;
 | ||
|   border-bottom: 1px solid #f5f7fa;
 | ||
|   color: #606266;
 | ||
|   font-size: 14px;
 | ||
|   transition: background-color 0.2s ease;
 | ||
| }
 | ||
| 
 | ||
| .custom-table tr:hover td {
 | ||
|   background-color: #f8f9ff;
 | ||
| }
 | ||
| 
 | ||
| .custom-table tr.current-row td {
 | ||
|   background-color: #e6f7ff !important;
 | ||
| }
 | ||
| 
 | ||
| /* 状态标签样式 */
 | ||
| .status-tag,
 | ||
| .team-status-tag {
 | ||
|   padding: 4px 12px;
 | ||
|   border-radius: 16px;
 | ||
|   font-size: 12px;
 | ||
|   font-weight: 500;
 | ||
|   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 | ||
|   transition: all 0.3s ease;
 | ||
| }
 | ||
| 
 | ||
| .team-status-tag:hover {
 | ||
|   transform: scale(1.05);
 | ||
|   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
 | ||
| }
 | ||
| 
 | ||
| /* 完成度样式优化 */
 | ||
| .completion-bar {
 | ||
|   width: 100%;
 | ||
|   height: 8px;
 | ||
|   background-color: #f0f2f5;
 | ||
|   border-radius: 4px;
 | ||
|   overflow: hidden;
 | ||
| }
 | ||
| 
 | ||
| .completion-progress {
 | ||
|   height: 100%;
 | ||
|   border-radius: 4px;
 | ||
|   transition: width 0.3s ease;
 | ||
| }
 | ||
| 
 | ||
| .completion-progress.high {
 | ||
|   background-color: #52c41a;
 | ||
| }
 | ||
| 
 | ||
| .completion-progress.medium {
 | ||
|   background-color: #faad14;
 | ||
| }
 | ||
| 
 | ||
| .completion-progress.low {
 | ||
|   background-color: #fa8c16;
 | ||
| }
 | ||
| 
 | ||
| /* 操作按钮样式优化 */
 | ||
| .action-btn {
 | ||
|   color: #333;
 | ||
|   font-size: 13px;
 | ||
|   padding: 4px 10px;
 | ||
|   margin: 0 3px;
 | ||
|   transition: all 0.2s ease;
 | ||
| }
 | ||
| 
 | ||
| .action-btn:hover {
 | ||
|   background-color: #f8f9ff;
 | ||
| }
 | ||
| 
 | ||
| .assign-btn {
 | ||
|   color: #1890ff;
 | ||
| }
 | ||
| 
 | ||
| .assign-btn:hover {
 | ||
|   color: #094ab2;
 | ||
|   background-color: #e6f7ff;
 | ||
| }
 | ||
| 
 | ||
| .manage-btn {
 | ||
|   color: #1890ff;
 | ||
| }
 | ||
| 
 | ||
| .manage-btn:hover {
 | ||
|   color: #094ab2;
 | ||
|   background-color: #e6f7ff;
 | ||
| }
 | ||
| 
 | ||
| .support-btn {
 | ||
|   color: #ff4d4f;
 | ||
| }
 | ||
| 
 | ||
| .support-btn:hover {
 | ||
|   color: #cf1322;
 | ||
|   background-color: #fff2f0;
 | ||
| }
 | ||
| 
 | ||
| /* 分页区域样式 */
 | ||
| .pagination-section {
 | ||
|   display: flex;
 | ||
|   justify-content: space-between;
 | ||
|   align-items: center;
 | ||
|   background-color: #fff;
 | ||
|   padding: 16px 24px;
 | ||
|   border-radius: 8px;
 | ||
|   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
 | ||
| }
 | ||
| 
 | ||
| .pagination-info {
 | ||
|   font-size: 14px;
 | ||
|   color: #606266;
 | ||
| }
 | ||
| 
 | ||
| .pagination-controls .el-pagination {
 | ||
|   margin: 0;
 | ||
| }
 | ||
| 
 | ||
| .pagination-controls .el-pagination button,
 | ||
| .pagination-controls .el-pagination .el-pager li {
 | ||
|   min-width: 36px;
 | ||
|   height: 36px;
 | ||
|   line-height: 36px;
 | ||
|   border-radius: 4px;
 | ||
| }
 | ||
| 
 | ||
| .pagination-controls .el-pagination .el-pager li.active {
 | ||
|   background-color: #165dff;
 | ||
|   color: #fff;
 | ||
| }
 | ||
| 
 | ||
| /* 导航栏样式 */
 | ||
| .navigation-tabs {
 | ||
|   display: flex;
 | ||
|   margin-bottom: 20px;
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 4px;
 | ||
|   box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
 | ||
|   padding: 2px;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab {
 | ||
|   padding: 12px 24px;
 | ||
|   cursor: pointer;
 | ||
|   transition: all 0.3s ease;
 | ||
|   border-radius: 4px;
 | ||
|   font-size: 14px;
 | ||
|   color: #606266;
 | ||
|   border-right: 1px solid #f0f0f0;
 | ||
|   flex: 1;
 | ||
|   text-align: center;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab:last-child {
 | ||
|   border-right: none;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab:hover {
 | ||
|   color: #409eff;
 | ||
|   background-color: #ecf5ff;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab.active {
 | ||
|   background-color: #409eff;
 | ||
|   color: #fff;
 | ||
|   box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
 | ||
| }
 | ||
| 
 | ||
| .nav-tab {
 | ||
|   cursor: pointer;
 | ||
|   user-select: none;
 | ||
| }
 | ||
| 
 | ||
| /* 响应式设计 */
 | ||
| @media (max-width: 1200px) {
 | ||
|   .team-cards-container {
 | ||
|     grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @media (max-width: 768px) {
 | ||
|   .execution-records {
 | ||
|     padding: 10px;
 | ||
|   }
 | ||
| 
 | ||
|   .navigation-tabs {
 | ||
|     flex-wrap: wrap;
 | ||
|   }
 | ||
| 
 | ||
|   .nav-tab {
 | ||
|     flex: 1 0 33%;
 | ||
|     padding: 10px 0;
 | ||
|     font-size: 12px;
 | ||
|   }
 | ||
| 
 | ||
|   .team-cards-container {
 | ||
|     grid-template-columns: 1fr;
 | ||
|   }
 | ||
| 
 | ||
|   .pagination-section {
 | ||
|     flex-direction: column;
 | ||
|     align-items: flex-start;
 | ||
|     gap: 10px;
 | ||
|   }
 | ||
| 
 | ||
|   /* 响应式调整图表大小 */
 | ||
|   .chart-container {
 | ||
|     min-height: 220px;
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |