Merge branch 'lx' of http://xny.yj-3d.com:3000/taoge_xiaodi/maintenance_system into tcy
This commit is contained in:
		| @ -242,7 +242,7 @@ onMounted(() => { | ||||
|     background-color: #fff; | ||||
|     border-radius: 8px; | ||||
|     overflow: hidden; | ||||
|     height: 500px; | ||||
|     height: 435px; | ||||
|     width: 100%; | ||||
|     padding: 10px; | ||||
|     box-sizing: border-box; | ||||
| @ -288,7 +288,7 @@ onMounted(() => { | ||||
|  | ||||
| @media (max-width: 768px) { | ||||
|     .chart-container { | ||||
|         height: 450px; | ||||
|         height: 435px; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -0,0 +1,269 @@ | ||||
| <template> | ||||
|   <!-- 人员排班弹窗 --> | ||||
|   <el-dialog  | ||||
|     :model-value="manageAttendDialogVisible"  | ||||
|     @update:model-value="handleDialogVisibleChange" | ||||
|     title="管理考勤"  | ||||
|     width="500" | ||||
|   > | ||||
|     <el-form :model="attendForm" label-width="100px"> | ||||
|       <el-form-item label="选择日期"> | ||||
|         <el-date-picker | ||||
|           v-model="attendForm.date" | ||||
|           type="date" | ||||
|           placeholder="选择日期" | ||||
|           style="width: 100%;" | ||||
|           :disabled-date="(time) => time.getTime() < Date.now() - 8.64e7" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|        | ||||
|       <!-- 动态排班表单 --> | ||||
|       <div v-for="(item, index) in attendForm.shifts" :key="index" class="dynamic-shift-item"> | ||||
|         <el-form-item :label="index === 0 ? '排班设置' : ''" :required="index === 0"> | ||||
|           <div class="shift-form-row"> | ||||
|             <!-- 排班类型选择 --> | ||||
|             <el-select  | ||||
|               v-model="item.type"  | ||||
|               placeholder="请选择排班类型"  | ||||
|               style="width: 40%; margin-right: 10px;" | ||||
|             > | ||||
|               <el-option  | ||||
|                 v-for="option in availableShiftOptions"  | ||||
|                 :key="option.value"  | ||||
|                 :label="option.label"  | ||||
|                 :value="option.value" | ||||
|               ></el-option> | ||||
|             </el-select> | ||||
|              | ||||
|             <!-- 人员选择 --> | ||||
|             <el-select  | ||||
|               v-model="item.personnel"  | ||||
|               placeholder="请选择人员"  | ||||
|               style="width: 50%; margin-right: 10px;" | ||||
|               multiple  | ||||
|               filterable | ||||
|             > | ||||
|               <el-option  | ||||
|                 v-for="person in props.personnelList"  | ||||
|                 :key="person.value"  | ||||
|                 :label="person.label"  | ||||
|                 :value="person.value" | ||||
|               ></el-option> | ||||
|             </el-select> | ||||
|              | ||||
|             <!-- 删除按钮 (仅在不是第一个项目时显示) --> | ||||
|             <el-button  | ||||
|               v-if="index > 0" | ||||
|               type="danger"  | ||||
|               icon="CircleCloseFilled" | ||||
|               circle  | ||||
|               @click="removeShiftItem(index)" | ||||
|             ></el-button> | ||||
|           </div> | ||||
|         </el-form-item> | ||||
|       </div> | ||||
|        | ||||
|       <!-- 添加排班类型按钮 --> | ||||
|       <el-form-item> | ||||
|         <el-button  | ||||
|           type="primary" | ||||
|           icon="CirclePlusFilled" | ||||
|           @click="addShiftItem" | ||||
|           :disabled="attendForm.shifts.length >= shiftTypes.length" | ||||
|         > | ||||
|           添加排班类型 | ||||
|         </el-button> | ||||
|         <div v-if="attendForm.shifts.length > 0" class="form-tip"> | ||||
|           提示:已添加 {{ attendForm.shifts.length }}/{{ shiftTypes.length }} 种排班类型 | ||||
|         </div> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|      | ||||
|     <template #footer> | ||||
|       <div class="dialog-footer"> | ||||
|         <el-button @click="handleCancel">取消</el-button> | ||||
|         <el-button type="primary" @click="handleConfirm"> | ||||
|           确认 | ||||
|         </el-button> | ||||
|       </div> | ||||
|     </template> | ||||
|   </el-dialog> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref, watch } from 'vue'; | ||||
|  | ||||
| // 定义组件的props | ||||
| const props = defineProps({ | ||||
|   manageAttendDialogVisible: { | ||||
|     type: Boolean, | ||||
|     default: false | ||||
|   }, | ||||
|   // 排班人员列表数据 | ||||
|   personnelList: { | ||||
|     type: Array, | ||||
|     default: () => [ | ||||
|       { label: '张三', value: '1' }, | ||||
|       { label: '李四', value: '2' }, | ||||
|       { label: '王五', value: '3' }, | ||||
|       { label: '赵六', value: '4' }, | ||||
|       { label: '钱七', value: '5' } | ||||
|     ] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| // 定义组件的emits | ||||
| const emit = defineEmits<{ | ||||
|   'update:manageAttendDialogVisible': [value: boolean]; | ||||
|   'confirm': [formData: any]; | ||||
| }>(); | ||||
|  | ||||
| // 排班类型列表 | ||||
| const shiftTypes = [ | ||||
|   { label: '早班', value: '早班' }, | ||||
|   { label: '中班', value: '中班' }, | ||||
|   { label: '晚班', value: '晚班' }, | ||||
|   { label: '休息', value: '休息' } | ||||
| ]; | ||||
|  | ||||
| // 考勤表单数据 | ||||
| const attendForm = ref({ | ||||
|   date: '', | ||||
|   shifts: [ | ||||
|     { type: '', personnel: [] } | ||||
|   ] | ||||
| }); | ||||
|  | ||||
| // 获取可用的排班类型选项(排除已添加的类型) | ||||
| const availableShiftOptions = ref(shiftTypes); | ||||
|  | ||||
| // 监听排班类型变化,更新可用选项 | ||||
| const setupWatchers = () => { | ||||
|   attendForm.value.shifts.forEach((item) => { | ||||
|     watch(() => item.type, (newType) => { | ||||
|       updateAvailableShiftOptions(); | ||||
|     }); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| // 初始化时设置监听器 | ||||
| setupWatchers(); | ||||
|  | ||||
| // 更新可用的排班类型选项 | ||||
| const updateAvailableShiftOptions = () => { | ||||
|   const usedTypes = attendForm.value.shifts | ||||
|     .map(item => item.type) | ||||
|     .filter(type => type !== ''); | ||||
|      | ||||
|   availableShiftOptions.value = shiftTypes.filter(type =>  | ||||
|     !usedTypes.includes(type.value) | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| // 添加新的排班类型项 | ||||
| const addShiftItem = () => { | ||||
|   // 检查是否还有可用的排班类型 | ||||
|   if (attendForm.value.shifts.length < shiftTypes.length) { | ||||
|     attendForm.value.shifts.push({ type: '', personnel: [] }); | ||||
|      | ||||
|     // 为新添加的项添加监听器 | ||||
|     const newItem = attendForm.value.shifts[attendForm.value.shifts.length - 1]; | ||||
|     watch(() => newItem.type, (newType) => { | ||||
|       updateAvailableShiftOptions(); | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 删除排班类型项 | ||||
| const removeShiftItem = (index: number) => { | ||||
|   if (attendForm.value.shifts.length > 1) { | ||||
|     attendForm.value.shifts.splice(index, 1); | ||||
|     updateAvailableShiftOptions(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 处理取消 | ||||
| const handleCancel = () => { | ||||
|   emit('update:manageAttendDialogVisible', false); | ||||
|   resetForm(); | ||||
| }; | ||||
|  | ||||
| // 处理弹窗可见性变化 | ||||
| const handleDialogVisibleChange = (newVisible: boolean) => { | ||||
|   emit('update:manageAttendDialogVisible', newVisible); | ||||
|   if (!newVisible) { | ||||
|     resetForm(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 处理确认 | ||||
| const handleConfirm = () => { | ||||
|   // 提交表单数据给父组件 | ||||
|   emit('confirm', attendForm.value); | ||||
|   emit('update:manageAttendDialogVisible', false); | ||||
|   resetForm(); | ||||
| }; | ||||
|  | ||||
| // 重置表单 | ||||
| const resetForm = () => { | ||||
|   attendForm.value = { | ||||
|     date: '', | ||||
|     shifts: [ | ||||
|       { type: '', personnel: [] } | ||||
|     ] | ||||
|   }; | ||||
|   updateAvailableShiftOptions(); | ||||
|   // 重新设置监听器 | ||||
|   setupWatchers(); | ||||
| }; | ||||
|  | ||||
| // 监听弹窗显示状态变化,在显示时重置表单 | ||||
| watch(() => props.manageAttendDialogVisible, (newVisible) => { | ||||
|   if (newVisible) { | ||||
|     resetForm(); | ||||
|   } | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| /* 动态排班表单样式 */ | ||||
| .dynamic-shift-item { | ||||
|   margin-bottom: 15px; | ||||
|   padding: 10px; | ||||
|   background-color: #f9f9f9; | ||||
|   border-radius: 4px; | ||||
|   border: 1px solid #ebeef5; | ||||
| } | ||||
|  | ||||
| .shift-form-row { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| .form-tip { | ||||
|   color: #909399; | ||||
|   font-size: 12px; | ||||
|   margin-top: 8px; | ||||
|   margin-left: 10px; | ||||
| } | ||||
|  | ||||
| /* 响应式调整 */ | ||||
| @media screen and (max-width: 768px) { | ||||
|   .shift-form-row { | ||||
|     flex-direction: column; | ||||
|     align-items: stretch; | ||||
|   } | ||||
|    | ||||
|   .shift-form-row .el-select { | ||||
|     width: 100% !important; | ||||
|     margin-right: 0 !important; | ||||
|     margin-bottom: 8px; | ||||
|   } | ||||
|    | ||||
|   .shift-form-row .el-button { | ||||
|     align-self: flex-start; | ||||
|     margin-top: 8px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @ -26,14 +26,40 @@ | ||||
|             <div class="week-day">{{ dateInfo.weekDay }}</div> | ||||
|           </div> | ||||
|         </template> | ||||
|         <template #default="scope"> | ||||
|           <div  | ||||
|             class="schedule-cell"  | ||||
|             :style="{ color: getShiftColor(scope.row[`day${index + 1}`]) }" | ||||
|             @click="handleCellClick(scope.row, {...dateInfo, index}, scope)" | ||||
|           > | ||||
|             {{ scope.row[`day${index + 1}`] }} | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|      | ||||
|     <!-- 分页组件 --> | ||||
|     <div class="pagination-container"> | ||||
|       <el-pagination | ||||
|         v-model:current-page="currentPage" | ||||
|         v-model:page-size="pageSize" | ||||
|         :page-sizes="[10, 20, 50, 100]" | ||||
|         layout="total, sizes, prev, pager, next, jumper" | ||||
|         :total="total" | ||||
|         @size-change="handleSizeChange" | ||||
|         @current-change="handleCurrentChange" | ||||
|       /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import { ref, computed, onMounted } from 'vue'; | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   'edit-schedule': [rowData: any, columnData: any, cellEvent: any] | ||||
| }>(); | ||||
|  | ||||
| // 员工列表 | ||||
| const employees = [ | ||||
|   { name: '张三', position: '水泥工', weeklyHours: 142 }, | ||||
| @ -51,6 +77,19 @@ const employees = [ | ||||
| // 排班类型 | ||||
| const shifts = ['早班', '中班', '晚班', '休息']; | ||||
|  | ||||
| // 排班类型与颜色的映射关系 | ||||
| const shiftColorMap = { | ||||
|   '早班': '#67c23a',  // 绿色 | ||||
|   '中班': '#e6a23c',  // 橙色 | ||||
|   '晚班': '#409eff',  // 蓝色 | ||||
|   '休息': '#909399'   // 灰色 | ||||
| }; | ||||
|  | ||||
| // 根据排班类型获取对应的颜色 | ||||
| const getShiftColor = (shiftType: string) => { | ||||
|   return shiftColorMap[shiftType as keyof typeof shiftColorMap] || '#333'; | ||||
| }; | ||||
|  | ||||
| // 获取当前月的日期信息 | ||||
| const currentMonthDates = ref<any[]>([]); | ||||
|  | ||||
| @ -84,9 +123,17 @@ const getCurrentMonthDates = () => { | ||||
|   return dates; | ||||
| }; | ||||
|  | ||||
| // 分页相关状态 | ||||
| const currentPage = ref(1); | ||||
| const pageSize = ref(10); | ||||
| const total = ref(50); // 总数据条数,模拟数据 | ||||
|  | ||||
| // 生成排班数据 | ||||
| const scheduleData = computed(() => { | ||||
|   return Array.from({ length: 20 }, (_, index) => { | ||||
|   const startIndex = (currentPage.value - 1) * pageSize.value; | ||||
|   const endIndex = startIndex + pageSize.value; | ||||
|    | ||||
|   return Array.from({ length: total.value }, (_, index) => { | ||||
|     // 循环使用员工数据 | ||||
|     const employee = employees[index % employees.length]; | ||||
|      | ||||
| @ -105,13 +152,29 @@ const scheduleData = computed(() => { | ||||
|     }); | ||||
|      | ||||
|     return rowData; | ||||
|   }); | ||||
|   }).slice(startIndex, endIndex); | ||||
| }); | ||||
|  | ||||
| // 分页大小变化处理 | ||||
| const handleSizeChange = (size: number) => { | ||||
|   pageSize.value = size; | ||||
|   currentPage.value = 1; // 重置为第一页 | ||||
| }; | ||||
|  | ||||
| // 当前页码变化处理 | ||||
| const handleCurrentChange = (current: number) => { | ||||
|   currentPage.value = current; | ||||
| }; | ||||
|  | ||||
| // 组件挂载时获取当前月数据 | ||||
| onMounted(() => { | ||||
|   currentMonthDates.value = getCurrentMonthDates(); | ||||
| }); | ||||
|  | ||||
| // 处理单元格点击事件 | ||||
| const handleCellClick = (rowData: any, columnData: any, cellEvent: any) => { | ||||
|   emit('edit-schedule', rowData, columnData, cellEvent); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| @ -165,6 +228,18 @@ onMounted(() => { | ||||
|   padding: 8px 0; | ||||
| } | ||||
|  | ||||
| /* 排班单元格样式 */ | ||||
| .schedule-cell { | ||||
|   padding: 8px 0; | ||||
|   cursor: pointer; | ||||
|   transition: all 0.2s ease; | ||||
| } | ||||
|  | ||||
| .schedule-cell:hover { | ||||
|   background-color: #f5f7fa; | ||||
|   transform: scale(1.05); | ||||
| } | ||||
|  | ||||
| .date-number { | ||||
|   font-size: 16px; | ||||
|   font-weight: 600; | ||||
| @ -173,7 +248,20 @@ onMounted(() => { | ||||
| } | ||||
|  | ||||
| .week-day { | ||||
|   font-size: 12px; | ||||
|   color: #666; | ||||
| } | ||||
|     font-size: 12px; | ||||
|     color: #666; | ||||
|   } | ||||
|    | ||||
|   /* 分页容器样式 */ | ||||
|   .pagination-container { | ||||
|     margin-top: 16px; | ||||
|     display: flex; | ||||
|     justify-content: flex-end; | ||||
|     align-items: center; | ||||
|   } | ||||
|    | ||||
|   /* 分页组件样式优化 */ | ||||
|   :deep(.el-pagination) { | ||||
|     font-size: 14px; | ||||
|   } | ||||
| </style> | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| <template> | ||||
|     <!-- 考勤管理 --> | ||||
|     <div class="model"> | ||||
|         <!-- 标题栏 --> | ||||
|         <el-row :gutter="24"> | ||||
| @ -38,19 +39,24 @@ | ||||
|                 <infoBox></infoBox> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|          | ||||
|  | ||||
|         <!-- 第二行:人员排班和出勤趋势分析 --> | ||||
|         <el-row :gutter="20"> | ||||
|             <el-col :span="17"> | ||||
|                 <div class="analysis-content"> | ||||
|                     <attendTrend :attendData="attendData"></attendTrend> | ||||
|                     <el-card> | ||||
|                         <TitleComponent title="人员排班" :fontLevel="2" /> | ||||
|                         <renyuanpaiban></renyuanpaiban> | ||||
|                         <div style="display: flex; justify-content: space-between; align-items: center;"> | ||||
|                             <TitleComponent title="人员排班" :fontLevel="2" /> | ||||
|                             <el-button type="primary" @click="manageAttendDialogVisible = true"> | ||||
|                                 管理考勤 | ||||
|                             </el-button> | ||||
|                         </div> | ||||
|                         <renyuanpaiban @edit-schedule="handleEditSchedule"></renyuanpaiban> | ||||
|                     </el-card> | ||||
|                 </div> | ||||
|             </el-col> | ||||
|              | ||||
|  | ||||
|             <!-- 右侧日历卡片 --> | ||||
|             <el-col :span="7"> | ||||
|                 <div class="calendar-content"> | ||||
| @ -62,17 +68,149 @@ | ||||
|                 </div> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|         <!-- 人员排班弹窗组件 --> | ||||
|         <renyuanguanliDialog  | ||||
|             v-model:manageAttendDialogVisible="manageAttendDialogVisible" | ||||
|             @confirm="handleAttendConfirm" | ||||
|             :personnel-list="paibanRenYuanList" | ||||
|         /> | ||||
|          | ||||
|         <!-- 编辑排班弹窗 --> | ||||
|         <el-dialog v-model="editScheduleDialogVisible" title="修改排班" width="400"> | ||||
|             <el-form :model="editScheduleForm" label-width="100px"> | ||||
|                 <el-form-item label="员工姓名"> | ||||
|                     <el-input v-model="editScheduleForm.name" disabled /> | ||||
|                 </el-form-item> | ||||
|                 <el-form-item label="排班日期"> | ||||
|                     <el-input v-model="editScheduleForm.date" disabled /> | ||||
|                 </el-form-item> | ||||
|                 <el-form-item label="当前排班"> | ||||
|                     <el-input v-model="editScheduleForm.currentShift" disabled /> | ||||
|                 </el-form-item> | ||||
|                 <el-form-item label="修改为"> | ||||
|                     <el-select v-model="editScheduleForm.newShift" placeholder="请选择排班类型" style="width: 100%;"> | ||||
|                         <el-option v-for="option in shiftOptions" :key="option.value" :label="option.label" :value="option.value"></el-option> | ||||
|                     </el-select> | ||||
|                 </el-form-item> | ||||
|             </el-form> | ||||
|              | ||||
|             <template #footer> | ||||
|                 <div class="dialog-footer"> | ||||
|                     <el-button @click="editScheduleDialogVisible = false">取消</el-button> | ||||
|                     <el-button type="primary" @click="handleConfirmEditSchedule"> | ||||
|                         确认修改 | ||||
|                     </el-button> | ||||
|                 </div> | ||||
|             </template> | ||||
|         </el-dialog> | ||||
|     </div> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import infoBox from '@/views/integratedManage/attendManage/components/infoBox.vue' | ||||
| import attendTrend from '@/views/integratedManage/attendManage/components/attendTrend.vue' | ||||
| import todayAttend from '@/views/integratedManage/attendManage/components/leftBox/todayAttend.vue' | ||||
| import approval from '@/views/integratedManage/attendManage/components/leftBox/approval.vue' | ||||
| import calendar from '@/views/integratedManage/attendManage/components/leftBox/calendar.vue' | ||||
| import todayAttend from '@/views/integratedManage/attendManage/components/rightBox/todayAttend.vue' | ||||
| import approval from '@/views/integratedManage/attendManage/components/rightBox/approval.vue' | ||||
| import calendar from '@/views/integratedManage/attendManage/components/rightBox/calendar.vue' | ||||
| import totalView from '@/views/integratedManage/attendManage/components/totalView.vue' | ||||
| import renyuanpaiban from '@/views/integratedManage/attendManage/components/renyuanpaiban.vue' | ||||
| import { ref } from 'vue'; | ||||
| import renyuanguanliDialog from '@/views/integratedManage/attendManage/components/renyuanguanliDialog.vue' | ||||
| import { getPaibanRenYuanList } from '@/api/renyuan/paiban'; | ||||
| import { ref, onMounted } from 'vue'; | ||||
| // 导入用户store | ||||
| import { useUserStore } from '@/store/modules/user'; | ||||
|  | ||||
| // 排班人员列表 | ||||
| const paibanRenYuanList = ref([]); | ||||
|  | ||||
| // 获取排班人员列表 | ||||
| const fetchPaibanRenYuanList = async (deptId?: string) => { | ||||
|   try { | ||||
|     // 如果没有提供deptId,默认使用当前登录用户的部门ID | ||||
|     const targetDeptId = deptId || userStore.deptId; | ||||
|     if (!targetDeptId) { | ||||
|       console.warn('未提供部门ID,无法获取排班人员列表'); | ||||
|       return; | ||||
|     } | ||||
|     const response = await getPaibanRenYuanList(targetDeptId); | ||||
|     // 格式化人员列表数据,确保每个人员对象包含label和value属性 | ||||
|     paibanRenYuanList.value = response.data?.map((user: any) => ({ | ||||
|       label: user.nickName || user.userName || '未知用户', | ||||
|       value: user.userId || user.id || '' | ||||
|     })) || []; | ||||
|      | ||||
|     console.log('获取排班人员列表成功:', paibanRenYuanList.value); | ||||
|   } catch (error) { | ||||
|     console.error('获取排班人员列表失败:', error); | ||||
|     // 错误情况下提供模拟数据,确保功能可用 | ||||
|     paibanRenYuanList.value = [ | ||||
|       { label: '张三', value: '1' }, | ||||
|       { label: '李四', value: '2' }, | ||||
|       { label: '王五', value: '3' }, | ||||
|       { label: '赵六', value: '4' }, | ||||
|       { label: '钱七', value: '5' } | ||||
|     ]; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 人员排班弹窗 | ||||
| const manageAttendDialogVisible = ref(false); | ||||
|  | ||||
| // 处理考勤管理确认 | ||||
| const handleAttendConfirm = (formData: any) => { | ||||
|   console.log('考勤表单数据:', formData); | ||||
|   // 这里可以添加表单提交逻辑 | ||||
| }; | ||||
|  | ||||
| // 编辑排班弹窗 | ||||
| const editScheduleDialogVisible = ref(false); | ||||
|  | ||||
| // 编辑排班表单数据 | ||||
| const editScheduleForm = ref({ | ||||
|   name: '', | ||||
|   date: '', | ||||
|   currentShift: '', | ||||
|   newShift: '' | ||||
| }); | ||||
|  | ||||
| // 排班类型选项 | ||||
| const shiftOptions = [ | ||||
|   { label: '早班', value: '早班' }, | ||||
|   { label: '中班', value: '中班' }, | ||||
|   { label: '晚班', value: '晚班' }, | ||||
|   { label: '休息', value: '休息' } | ||||
| ]; | ||||
|  | ||||
| // 处理编辑排班 | ||||
| const handleEditSchedule = (rowData: any, columnData: any) => { | ||||
|   // 设置表单数据 | ||||
|   editScheduleForm.value = { | ||||
|     name: rowData.name, | ||||
|     date: `${columnData.fullDate}`, | ||||
|     currentShift: '', | ||||
|     newShift: '' | ||||
|   }; | ||||
|    | ||||
|   // 查找当前排班 | ||||
|   Object.keys(rowData).forEach(key => { | ||||
|     if (key.startsWith('day')) { | ||||
|       const dayIndex = parseInt(key.replace('day', '')) - 1; | ||||
|       if (dayIndex === columnData.index) { | ||||
|         editScheduleForm.value.currentShift = rowData[key]; | ||||
|         editScheduleForm.value.newShift = rowData[key]; | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|    | ||||
|   // 显示弹窗 | ||||
|   editScheduleDialogVisible.value = true; | ||||
| }; | ||||
|  | ||||
| // 确认修改排班 | ||||
| const handleConfirmEditSchedule = () => { | ||||
|   console.log('修改排班数据:', editScheduleForm.value); | ||||
|   // 这里可以添加修改排班的逻辑 | ||||
|   editScheduleDialogVisible.value = false; | ||||
| }; | ||||
|  | ||||
| // 出勤数据 - 用于attendTrend组件 | ||||
| const attendData = ref( | ||||
| @ -92,46 +230,46 @@ const attendData = ref( | ||||
|  | ||||
| // Mock数据 - 更新为循环生成所需的数据结构 | ||||
| const totalData = ref({ | ||||
|   attendance: { | ||||
|     value: 248, | ||||
|     change: '+8.2%', | ||||
|     isPositive: true, | ||||
|     chartData: [150, 230, 224, 218, 135, 300, 220], | ||||
|     color: '#FF7D00', | ||||
|     title: '总出勤人数', | ||||
|     compareText: '较昨日同期', | ||||
|     chartType: 'bar' | ||||
|   }, | ||||
|   rest: { | ||||
|     value: 8, | ||||
|     change: '+8.2%', | ||||
|     isPositive: true, | ||||
|     chartData: [10, 12, 15, 8, 7, 9, 10], | ||||
|     color: '#00C48C', | ||||
|     title: '调休', | ||||
|     compareText: '较上月同期', | ||||
|     chartType: 'line' | ||||
|   }, | ||||
|   leave: { | ||||
|     value: 24, | ||||
|     change: '-10%', | ||||
|     isPositive: false, | ||||
|     chartData: [30, 25, 28, 22, 20, 26, 24], | ||||
|     color: '#FF5252', | ||||
|     title: '本月请假', | ||||
|     compareText: '较昨日同期', | ||||
|     chartType: 'line' | ||||
|   }, | ||||
|   rate: { | ||||
|     value: '96.8%', | ||||
|     change: '+10%', | ||||
|     isPositive: true, | ||||
|     chartData: [90, 92, 94, 95, 97, 98, 96.8], | ||||
|     color: '#029CD4', | ||||
|     title: '平均出勤率', | ||||
|     compareText: '较昨日同期', | ||||
|     chartType: 'line' | ||||
|   } | ||||
|     attendance: { | ||||
|         value: 248, | ||||
|         change: '+8.2%', | ||||
|         isPositive: true, | ||||
|         chartData: [150, 230, 224, 218, 135, 300, 220], | ||||
|         color: '#FF7D00', | ||||
|         title: '总出勤人数', | ||||
|         compareText: '较昨日同期', | ||||
|         chartType: 'bar' | ||||
|     }, | ||||
|     rest: { | ||||
|         value: 8, | ||||
|         change: '+8.2%', | ||||
|         isPositive: true, | ||||
|         chartData: [10, 12, 15, 8, 7, 9, 10], | ||||
|         color: '#00C48C', | ||||
|         title: '调休', | ||||
|         compareText: '较上月同期', | ||||
|         chartType: 'line' | ||||
|     }, | ||||
|     leave: { | ||||
|         value: 24, | ||||
|         change: '-10%', | ||||
|         isPositive: false, | ||||
|         chartData: [30, 25, 28, 22, 20, 26, 24], | ||||
|         color: '#FF5252', | ||||
|         title: '本月请假', | ||||
|         compareText: '较昨日同期', | ||||
|         chartType: 'line' | ||||
|     }, | ||||
|     rate: { | ||||
|         value: '96.8%', | ||||
|         change: '+10%', | ||||
|         isPositive: true, | ||||
|         chartData: [90, 92, 94, 95, 97, 98, 96.8], | ||||
|         color: '#029CD4', | ||||
|         title: '平均出勤率', | ||||
|         compareText: '较昨日同期', | ||||
|         chartType: 'line' | ||||
|     } | ||||
| }); | ||||
|  | ||||
| // 审批数据 - 用于approval组件 | ||||
| @ -185,47 +323,56 @@ const approvalData = ref([ | ||||
|  | ||||
| // 今日出勤数据 - 用于todayAttend组件 | ||||
| const todayAttendData = ref({ | ||||
|   attendance: { | ||||
|     count: 150, | ||||
|     icon: '/src/assets/demo/qin.png' | ||||
|   }, | ||||
|   late: { | ||||
|     count: 5, | ||||
|     icon: '/src/assets/demo/chi.png' | ||||
|   }, | ||||
|   earlyLeave: { | ||||
|     count: 2, | ||||
|     icon: '/src/assets/demo/tui.png' | ||||
|   }, | ||||
|   absent: { | ||||
|     count: 8, | ||||
|     icon: '/src/assets/demo/que.png' | ||||
|   } | ||||
|     attendance: { | ||||
|         count: 150, | ||||
|         icon: '/src/assets/demo/qin.png' | ||||
|     }, | ||||
|     late: { | ||||
|         count: 5, | ||||
|         icon: '/src/assets/demo/chi.png' | ||||
|     }, | ||||
|     earlyLeave: { | ||||
|         count: 2, | ||||
|         icon: '/src/assets/demo/tui.png' | ||||
|     }, | ||||
|     absent: { | ||||
|         count: 8, | ||||
|         icon: '/src/assets/demo/que.png' | ||||
|     } | ||||
| }); | ||||
|  | ||||
| // 日历数据 - 用于calendar组件 | ||||
| const calendarData = ref({ | ||||
|   // 初始化当前日期 | ||||
|   today: new Date(), | ||||
|   currentDate: new Date(2025, 8, 27), // 2025年9月27日,截图中显示的日期 | ||||
|   selectedDate: new Date(2025, 8, 27), | ||||
|    | ||||
|   // 模拟考勤数据 | ||||
|   attendanceData: { | ||||
|     2025: { | ||||
|       9: { | ||||
|         1: 'normal', | ||||
|         4: 'late', | ||||
|         8: 'absent', | ||||
|         10: 'leave', | ||||
|         15: 'normal', | ||||
|         20: 'normal', | ||||
|         25: 'late', | ||||
|         27: 'normal' | ||||
|       } | ||||
|     // 初始化当前日期 | ||||
|     today: new Date(), | ||||
|     currentDate: new Date(2025, 8, 27), // 2025年9月27日,截图中显示的日期 | ||||
|     selectedDate: new Date(2025, 8, 27), | ||||
|  | ||||
|     // 模拟考勤数据 | ||||
|     attendanceData: { | ||||
|         2025: { | ||||
|             9: { | ||||
|                 1: 'normal', | ||||
|                 4: 'late', | ||||
|                 8: 'absent', | ||||
|                 10: 'leave', | ||||
|                 15: 'normal', | ||||
|                 20: 'normal', | ||||
|                 25: 'late', | ||||
|                 27: 'normal' | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| }); | ||||
|  | ||||
|  | ||||
| // 初始化用户store | ||||
| const userStore = useUserStore(); | ||||
|  | ||||
| onMounted(() => { | ||||
|   fetchPaibanRenYuanList(String(userStore.deptId)); | ||||
| }); | ||||
|  | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| @ -260,11 +407,11 @@ const calendarData = ref({ | ||||
|     height: 100%; | ||||
| } | ||||
|  | ||||
| .calendar-content .el-card > * { | ||||
| .calendar-content .el-card>* { | ||||
|     margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| .calendar-content .el-card > *:last-child { | ||||
| .calendar-content .el-card>*:last-child { | ||||
|     margin-bottom: 0; | ||||
|     flex: 1; | ||||
| } | ||||
| @ -300,9 +447,9 @@ const calendarData = ref({ | ||||
|     .analysis-content { | ||||
|         gap: 16px; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /* 日历卡片内组件间距 */ | ||||
|     .calendar-content .el-card > * { | ||||
|     .calendar-content .el-card>* { | ||||
|         margin-bottom: 12px; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										274
									
								
								src/views/integratedManage/paibanTimeType.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								src/views/integratedManage/paibanTimeType.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,274 @@ | ||||
| <template> | ||||
|   <div class="p-2"> | ||||
|     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> | ||||
|       <div v-show="showSearch" class="mb-[10px]"> | ||||
|         <el-card shadow="hover"> | ||||
|           <el-form ref="queryFormRef" :model="queryParams" :inline="true"> | ||||
|             <el-form-item label="排班名称" prop="schedulingName"> | ||||
|           <el-input v-model="queryParams.schedulingName" placeholder="请输入排班名称" clearable @keyup.enter="handleQuery" /> | ||||
|         </el-form-item> | ||||
|  | ||||
|             <el-form-item> | ||||
|               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> | ||||
|               <el-button icon="Refresh" @click="resetQuery">重置</el-button> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </el-card> | ||||
|       </div> | ||||
|     </transition> | ||||
|  | ||||
|     <el-card shadow="never"> | ||||
|       <template #header> | ||||
|         <el-row :gutter="10" class="mb8"> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['personnel:schedulingDate:add']">新增</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['personnel:schedulingDate:edit']">修改</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['personnel:schedulingDate:remove']">删除</el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" border :data="schedulingDateList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="排班名称" align="center" prop="schedulingName" /> | ||||
|         <el-table-column label="开始时间" align="center" prop="startTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{scope.row.startTime}}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="结束时间" align="center" prop="endTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ scope.row.endTime}}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-tooltip content="修改" placement="top"> | ||||
|               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['personnel:schedulingDate:edit']"></el-button> | ||||
|             </el-tooltip> | ||||
|             <el-tooltip content="删除" placement="top"> | ||||
|               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['personnel:schedulingDate:remove']"></el-button> | ||||
|             </el-tooltip> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|  | ||||
|       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> | ||||
|     </el-card> | ||||
|     <!-- 添加或修改运维-排班时间类型对话框 --> | ||||
|     <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> | ||||
|       <el-form ref="schedulingDateFormRef" :model="form" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="排班名称" prop="schedulingName"> | ||||
|           <el-input v-model="form.schedulingName" placeholder="请输入排班名称" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="开始时间" prop="startTime"> | ||||
|           <el-time-select clearable | ||||
|             v-model="form.startTime" | ||||
|             value-format="HH:mm:ss" | ||||
|             step="00:10:00" | ||||
|             start="00:00" | ||||
|             end="23:59" | ||||
|             placeholder="请选择开始时间"> | ||||
|           </el-time-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="结束时间" prop="endTime"> | ||||
|           <el-time-select clearable | ||||
|             v-model="form.endTime" | ||||
|             value-format="HH:mm:ss" | ||||
|             step="00:10:00" | ||||
|             start="00:00" | ||||
|             end="23:59" | ||||
|             placeholder="请选择结束时间"> | ||||
|           </el-time-select> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <template #footer> | ||||
|         <div class="dialog-footer"> | ||||
|           <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button> | ||||
|           <el-button @click="cancel">取 消</el-button> | ||||
|         </div> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="SchedulingDate" lang="ts"> | ||||
| import { ref, reactive, toRefs, onMounted, onUnmounted, watch, getCurrentInstance } from 'vue'; | ||||
| import { listSchedulingDate, getSchedulingDate, delSchedulingDate, addSchedulingDate, updateSchedulingDate } from '@/api/renyuan/schedulingDate'; | ||||
| import { SchedulingDateVO, SchedulingDateQuery, SchedulingDateForm } from '@/api/renyuan/schedulingDate/types'; | ||||
| // 导入用户store | ||||
| import { useUserStore } from '@/store/modules/user'; | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
|  | ||||
| // 获取用户store | ||||
| const userStore = useUserStore(); | ||||
|  | ||||
| const schedulingDateList = ref<SchedulingDateVO[]>([]); | ||||
| const buttonLoading = ref(false); | ||||
| const loading = ref(true); | ||||
| const showSearch = ref(true); | ||||
| const ids = ref<Array<string | number>>([]); | ||||
| const single = ref(true); | ||||
| const multiple = ref(true); | ||||
| const total = ref(0); | ||||
|  | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
| const schedulingDateFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| const initFormData: SchedulingDateForm = { | ||||
|   id: undefined, | ||||
|   schedulingName: undefined, | ||||
|   startTime: undefined, | ||||
|   endTime: undefined, | ||||
|   projectId: undefined, | ||||
| } | ||||
| const data = reactive<PageData<SchedulingDateForm, SchedulingDateQuery>>({ | ||||
|   form: {...initFormData}, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     schedulingName: undefined, | ||||
|     startTime: undefined, | ||||
|     endTime: undefined, | ||||
|     projectId: undefined, | ||||
|     params: { | ||||
|     } | ||||
|   }, | ||||
|   rules: { | ||||
|     schedulingName: [ | ||||
|       { required: true, message: "排班名称不能为空", trigger: "blur" } | ||||
|     ], | ||||
|     startTime: [ | ||||
|       { required: true, message: "开始时间不能为空", trigger: "blur" } | ||||
|     ], | ||||
|     endTime: [ | ||||
|       { required: true, message: "结束时间不能为空", trigger: "blur" } | ||||
|     ] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询运维-排班时间类型列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listSchedulingDate(queryParams.value); | ||||
|   schedulingDateList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| } | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| } | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = {...initFormData}; | ||||
|   schedulingDateFormRef.value?.resetFields(); | ||||
| } | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| } | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| } | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: SchedulingDateVO[]) => { | ||||
|   ids.value = selection.map(item => item.id); | ||||
|   single.value = selection.length != 1; | ||||
|   multiple.value = !selection.length; | ||||
| } | ||||
|  | ||||
| /** 新增按钮操作 */ | ||||
| const handleAdd = () => { | ||||
|   reset(); | ||||
|   // 显式设置projectId为当前选中的项目ID | ||||
|   if (userStore.selectedProject && userStore.selectedProject.id) { | ||||
|     form.value.projectId = userStore.selectedProject.id; | ||||
|   } | ||||
|   dialog.visible = true; | ||||
|   dialog.title = "添加运维-排班时间类型"; | ||||
| } | ||||
|  | ||||
| /** 修改按钮操作 */ | ||||
| const handleUpdate = async (row?: SchedulingDateVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0] | ||||
|   const res = await getSchedulingDate(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = "修改运维-排班时间类型"; | ||||
| } | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   schedulingDateFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       if (form.value.id) { | ||||
|         await updateSchedulingDate(form.value).finally(() =>  buttonLoading.value = false); | ||||
|       } else { | ||||
|         await addSchedulingDate(form.value).finally(() =>  buttonLoading.value = false); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess("操作成功"); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: SchedulingDateVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除运维-排班时间类型编号为"' + _ids + '"的数据项?').finally(() => loading.value = false); | ||||
|   await delSchedulingDate(_ids); | ||||
|   proxy?.$modal.msgSuccess("删除成功"); | ||||
|   await getList(); | ||||
| } | ||||
|  | ||||
|  | ||||
| // 监听用户选择的项目变化 | ||||
| watch(() => userStore.selectedProject, (newProject) => { | ||||
|   if (newProject && newProject.id) { | ||||
|     queryParams.value.projectId = newProject.id; | ||||
|     // 只在新增表单时设置projectId,编辑表单保留原有值 | ||||
|     if (!form.value.id) { | ||||
|       form.value.projectId = newProject.id; | ||||
|     } | ||||
|     // 调用getList刷新数据 | ||||
|     getList(); | ||||
|   } | ||||
| }, { immediate: true, deep: true }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
|  | ||||
| // 组件卸载时清空projectId | ||||
| onUnmounted(() => { | ||||
|   queryParams.value.projectId = undefined; | ||||
|   form.value.projectId = undefined; | ||||
| }); | ||||
|  | ||||
|  | ||||
| </script> | ||||
| @ -1,7 +1,6 @@ | ||||
|  | ||||
| <template> | ||||
|     <div class="chart-container"> | ||||
|         <!--组件温度(℃)  图表内容区域 --> | ||||
|         <div ref="chartRef" class="chart-content"></div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| @ -9,64 +9,17 @@ | ||||
|         <span class="update-time">截止至2025/06/30 12:00</span> | ||||
|       </el-col> | ||||
|     </el-row> | ||||
|  | ||||
|  | ||||
|     <!-- 关键指标卡片区域 --> | ||||
|     <el-row class="metrics-container" :gutter="0"> | ||||
|       <el-col :span="6"> | ||||
|       <el-col v-for="card in cardData" :key="card.key" :span="6"> | ||||
|         <div class="metric-card"> | ||||
|           <div class="metric-value">{{ props.dashboardData.todayAlarmTotal }}</div> | ||||
|           <div class="metric-label">今日报警总数</div> | ||||
|           <div class="metric-change">较上周 <span :class="props.dashboardData.updates.todayAlarmTotal.type"> | ||||
|               <img v-if="props.dashboardData.updates.todayAlarmTotal.type === 'up'" src="/src/assets/demo/up.png" | ||||
|           <div class="metric-value">{{ props.dashboardData[card.key] }}</div> | ||||
|           <div class="metric-label">{{ card.label }}</div> | ||||
|           <div class="metric-change">较上周 <span :class="props.dashboardData.updates[card.updateKey].type"> | ||||
|               <img v-if="props.dashboardData.updates[card.updateKey].type === 'up'" src="/src/assets/demo/up.png" | ||||
|                 class="trend-icon" alt="上升"> | ||||
|               <img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">{{ | ||||
|                 props.dashboardData.updates.todayAlarmTotal.value }} | ||||
|             </span> | ||||
|           </div> | ||||
|         </div> | ||||
|       </el-col> | ||||
|  | ||||
|  | ||||
|       <el-col :span="6"> | ||||
|         <div class="metric-card"> | ||||
|           <div class="metric-value">{{ props.dashboardData.unhandledAlarms }}</div> | ||||
|           <div class="metric-label">未处理报警</div> | ||||
|           <div class="metric-change">较上周 <span :class="props.dashboardData.updates.unhandledAlarms.type"> | ||||
|               <img v-if="props.dashboardData.updates.unhandledAlarms.type === 'up'" src="/src/assets/demo/up.png" | ||||
|                 class="trend-icon" alt="上升"> | ||||
|               <img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">{{ | ||||
|                 props.dashboardData.updates.unhandledAlarms.value }} | ||||
|             </span></div> | ||||
|         </div> | ||||
|       </el-col> | ||||
|  | ||||
|  | ||||
|       <el-col :span="6"> | ||||
|         <div class="metric-card"> | ||||
|           <div class="metric-value">{{ props.dashboardData.handledAlarms }}</div> | ||||
|           <div class="metric-label">已处理报警</div> | ||||
|           <div class="metric-change">较上周 <span :class="props.dashboardData.updates.handledAlarms.type"> | ||||
|               <img v-if="props.dashboardData.updates.handledAlarms.type === 'up'" src="/src/assets/demo/up.png" | ||||
|                 class="trend-icon" alt="上升"> | ||||
|               <img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">{{ | ||||
|                 props.dashboardData.updates.handledAlarms.value }} | ||||
|             </span></div> | ||||
|         </div> | ||||
|       </el-col> | ||||
|  | ||||
|  | ||||
|       <el-col :span="6"> | ||||
|         <div class="metric-card"> | ||||
|           <div class="metric-value">{{ props.dashboardData.avgProcessTime }}</div> | ||||
|           <div class="metric-label">平均处理时长</div> | ||||
|           <div class="metric-change"> | ||||
|             较上周 | ||||
|             <span :class="props.dashboardData.updates.avgProcessTime.type"> | ||||
|               <img v-if="props.dashboardData.updates.avgProcessTime.type === 'up'" src="/src/assets/demo/up.png" | ||||
|                 class="trend-icon" alt="上升"> | ||||
|               <img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">{{ | ||||
|                 props.dashboardData.updates.avgProcessTime.value }} | ||||
|                 props.dashboardData.updates[card.updateKey].value }} | ||||
|             </span> | ||||
|           </div> | ||||
|         </div> | ||||
| @ -176,6 +129,30 @@ const props = defineProps({ | ||||
|   } | ||||
| }); | ||||
|  | ||||
| // 卡片数据配置 | ||||
| const cardData = [ | ||||
|   { | ||||
|     key: 'todayAlarmTotal', | ||||
|     label: '今日报警总数', | ||||
|     updateKey: 'todayAlarmTotal' | ||||
|   }, | ||||
|   { | ||||
|     key: 'unhandledAlarms', | ||||
|     label: '未处理报警', | ||||
|     updateKey: 'unhandledAlarms' | ||||
|   }, | ||||
|   { | ||||
|     key: 'handledAlarms', | ||||
|     label: '已处理报警', | ||||
|     updateKey: 'handledAlarms' | ||||
|   }, | ||||
|   { | ||||
|     key: 'avgProcessTime', | ||||
|     label: '平均处理时长', | ||||
|     updateKey: 'avgProcessTime' | ||||
|   } | ||||
| ]; | ||||
|  | ||||
| const timeRange = ref('7days'); | ||||
| const alarmCountRef = ref(null); | ||||
| const processEfficiencyRef = ref(null); | ||||
|  | ||||
| @ -1,11 +1,13 @@ | ||||
| <template> | ||||
|   <div class="pie-chart-container"> | ||||
|     <!-- 标题栏 --> | ||||
|     <div class="chart-header"> | ||||
|        <TitleComponent title="报警类型分布" :fontLevel="2" /> | ||||
|       <el-select v-model="selectedTimeRange" placeholder="选择时间范围" size="small"> | ||||
|         <el-option label="今日" value="today" /> | ||||
|       </el-select> | ||||
|     </div> | ||||
|     <!-- 图表 --> | ||||
|     <div ref="pieChartRef" class="chart-content"></div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -27,6 +29,7 @@ const selectedTimeRange = ref('today'); | ||||
| const pieChartRef = ref(null); | ||||
| let chartInstance = null; | ||||
|  | ||||
|  | ||||
| onMounted(() => { | ||||
|   initChart(); | ||||
|   window.addEventListener('resize', handleResize); | ||||
|  | ||||
| @ -1,5 +1,17 @@ | ||||
| <template> | ||||
|   <div class="detaildata-container"> | ||||
|     <div class="title-container"> | ||||
|       <div class="title-left"> | ||||
|         <TitleComponent title="发电量同比分析" :font-level="2" /> | ||||
|       </div> | ||||
|       <div class="title-right"> | ||||
|         <el-input | ||||
|           placeholder="请输入搜索内容" | ||||
|           style="width: 200px;" | ||||
|           prefix-icon="Search" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|     <el-table | ||||
|       v-loading="loading" | ||||
|       :data="tableData" | ||||
| @ -177,8 +189,24 @@ onMounted(() => { | ||||
| .detaildata-container { | ||||
|   padding: 16px; | ||||
|   background: #fff; | ||||
|   border-radius: 8px; | ||||
|   box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); | ||||
| } | ||||
|  | ||||
| .title-container { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   width: 100%; | ||||
|   margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| .title-left { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .title-right { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .pagination-container { | ||||
|  | ||||
| @ -1,6 +1,12 @@ | ||||
| <template> | ||||
|   <div class="duibifenxi-bar-container"> | ||||
|     <div ref="chartRef" class="chart" style="width: 100%; height: 300px;"></div> | ||||
|     <div class="title"> | ||||
|       <TitleComponent title="发电量同比分析" :font-level="2" /> | ||||
|       <el-select placeholder="请选择线路" style="width: 150px;"> | ||||
|         <el-option label="A线路" value="all"></el-option> | ||||
|       </el-select> | ||||
|     </div> | ||||
|     <div ref="chartRef" class="chart-container"></div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -32,17 +38,17 @@ const defaultCompareData: CompareData = { | ||||
| // 初始化图表 | ||||
| const initChart = () => { | ||||
|   if (!chartRef.value) return | ||||
|    | ||||
|  | ||||
|   chartInstance = echarts.init(chartRef.value) | ||||
|   const data = props.compareData || defaultCompareData | ||||
|    | ||||
|  | ||||
|   const option = { | ||||
|     tooltip: { | ||||
|       trigger: 'axis', | ||||
|       axisPointer: { | ||||
|         type: 'shadow' | ||||
|       }, | ||||
|       formatter: function(params: any) { | ||||
|       formatter: function (params: any) { | ||||
|         const current = params[0] | ||||
|         const lastYear = params[1] | ||||
|         let result = `${current.name}<br/>` | ||||
| @ -78,16 +84,12 @@ const initChart = () => { | ||||
|     }, | ||||
|     yAxis: { | ||||
|       type: 'value', | ||||
|       name: 'kwh', | ||||
|       nameTextStyle: { | ||||
|         color: '#666', | ||||
|         padding: [0, 0, 0, 40] | ||||
|       }, | ||||
|       axisLine: { | ||||
|         show: false | ||||
|       }, | ||||
|       axisLabel: { | ||||
|         color: '#666' | ||||
|         color: '#666', | ||||
|         formatter: '{value} Kwh', | ||||
|       }, | ||||
|       splitLine: { | ||||
|         lineStyle: { | ||||
| @ -123,7 +125,7 @@ const initChart = () => { | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
|    | ||||
|  | ||||
|   chartInstance.setOption(option) | ||||
| } | ||||
|  | ||||
| @ -147,28 +149,35 @@ onUnmounted(() => { | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .duibifenxi-bar-container { | ||||
|   padding: 16px; | ||||
|   padding: 10px; | ||||
|   background: #fff; | ||||
|   border-radius: 8px; | ||||
|   box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); | ||||
|   height: 100%; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   min-height: 300px; | ||||
| } | ||||
|  | ||||
| .chart { | ||||
|   flex: 1; | ||||
|   min-height: 0; | ||||
| .title { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   margin-bottom: 15px; | ||||
|   width: 100%; | ||||
| } | ||||
| .chart-container { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   min-height: 280px; | ||||
| } | ||||
|  | ||||
| // 响应式调整 | ||||
| @media screen and (max-width: 768px) { | ||||
|   .duibifenxi-bar-container { | ||||
|     padding: 12px; | ||||
|     padding: 5px; | ||||
|     min-height: 250px; | ||||
|   } | ||||
|    | ||||
|   .chart { | ||||
|     height: 250px; | ||||
|  | ||||
|   .chart-container { | ||||
|     min-height: 230px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @ -1,43 +1,45 @@ | ||||
| <template> | ||||
|   <div class="tongbifenxi-line-container"> | ||||
|     <div id="tongbifenxiLineChart" class="chart-container"></div> | ||||
|     <div class="title"> | ||||
|       <TitleComponent title="发电量同比分析" :font-level="2" /> | ||||
|       <el-select placeholder="请选择线路" style="width: 150px;"> | ||||
|         <el-option label="A线路" value="all"></el-option> | ||||
|       </el-select> | ||||
|     </div> | ||||
|     <div ref="chartDomRef" class="chart-container"></div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { onMounted, onBeforeUnmount, ref } from 'vue'; | ||||
| import * as echarts from 'echarts'; | ||||
| import TitleComponent from '@/components/TitleComponent/index.vue'; | ||||
|  | ||||
| const chartDomRef = ref<HTMLElement | null>(null); | ||||
| const chartInstance = ref<echarts.ECharts | null>(null); | ||||
|  | ||||
| const initChart = () => { | ||||
|   const chartDom = document.getElementById('tongbifenxiLineChart'); | ||||
|   if (!chartDom) return; | ||||
|   if (!chartDomRef.value) return; | ||||
|  | ||||
|   chartInstance.value = echarts.init(chartDom); | ||||
|   chartInstance.value = echarts.init(chartDomRef.value); | ||||
|  | ||||
|   // 写死的数据 | ||||
|   const dates = ['1号', '2号', '3号', '4号', '5号', '6号', '7号']; | ||||
|   const growthRates = ['1.50', '1.20', '0.50', '0.80', '0.90', '0.30', '-2.00']; | ||||
|   const growthRates = [1.50, 1.20, 0.50, 0.80, 0.90, 0.30, -2.00]; | ||||
|  | ||||
|   const option: echarts.EChartsOption = { | ||||
|     tooltip: { | ||||
|       trigger: 'axis', | ||||
|       backgroundColor: 'rgba(0, 0, 0, 0.7)', | ||||
|       borderColor: '#409eff', | ||||
|       trigger: 'item', | ||||
|       backgroundColor: '#67c23a', | ||||
|       borderWidth: 0, | ||||
|       textStyle: { | ||||
|         color: '#fff' | ||||
|         color: '#fff', | ||||
|         fontSize: 14 | ||||
|       }, | ||||
|       formatter: (params: any) => { | ||||
|         const data = params[0]; | ||||
|         return `${data.name}:\n环比增长率: ${data.value}%`; | ||||
|         return `${params.name}:\n环比增长率:${params.value}%`; | ||||
|       }, | ||||
|       axisPointer: { | ||||
|         type: 'cross', | ||||
|         label: { | ||||
|           backgroundColor: '#6a7985' | ||||
|         } | ||||
|       } | ||||
|       padding: [10, 15] | ||||
|     }, | ||||
|     grid: { | ||||
|       left: '3%', | ||||
| @ -51,7 +53,7 @@ const initChart = () => { | ||||
|         boundaryGap: false, | ||||
|         data: dates, | ||||
|         axisTick: { | ||||
|           alignWithLabel: true | ||||
|           show: false | ||||
|         }, | ||||
|         axisLine: { | ||||
|           lineStyle: { | ||||
| @ -90,7 +92,6 @@ const initChart = () => { | ||||
|       { | ||||
|         name: '环比增长率', | ||||
|         type: 'line', | ||||
|         stack: 'Total', | ||||
|         areaStyle: { | ||||
|           color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ | ||||
|             { | ||||
| @ -103,9 +104,6 @@ const initChart = () => { | ||||
|             } | ||||
|           ]) | ||||
|         }, | ||||
|         emphasis: { | ||||
|           focus: 'series' | ||||
|         }, | ||||
|         lineStyle: { | ||||
|           color: '#67c23a', | ||||
|           width: 3 | ||||
| @ -117,6 +115,17 @@ const initChart = () => { | ||||
|           borderColor: '#fff', | ||||
|           borderWidth: 2 | ||||
|         }, | ||||
|         emphasis: { | ||||
|           focus: 'series', | ||||
|           itemStyle: { | ||||
|             color: '#67c23a', | ||||
|             borderColor: '#fff', | ||||
|             borderWidth: 3, | ||||
|             shadowBlur: 10, | ||||
|             shadowColor: 'rgba(103, 194, 58, 0.5)' | ||||
|           }, | ||||
|            | ||||
|         }, | ||||
|         data: growthRates, | ||||
|         smooth: true | ||||
|       } | ||||
| @ -149,8 +158,14 @@ onBeforeUnmount(() => { | ||||
|   padding: 10px; | ||||
|   box-sizing: border-box; | ||||
|   background: #fff; | ||||
|   border-radius: 4px; | ||||
|   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | ||||
| } | ||||
|  | ||||
| .title { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   margin-bottom: 15px; | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| .chart-container { | ||||
|  | ||||
							
								
								
									
										200
									
								
								src/views/shengchanManage/powerfenxi/components/zonglan.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								src/views/shengchanManage/powerfenxi/components/zonglan.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | ||||
| <template> | ||||
|   <div class="zonglan-container"> | ||||
|     <!-- 循环生成统计卡片 --> | ||||
|     <div v-for="card in statCards" :key="card.id" class="stat-card"> | ||||
|       <div class="card-header"> | ||||
|         <span class="card-title">{{ card.title }}</span> | ||||
|         <el-tooltip content="查看详情" placement="top"> | ||||
|           <el-icon> | ||||
|             <Warning /> | ||||
|           </el-icon> | ||||
|         </el-tooltip> | ||||
|       </div> | ||||
|       <div class="card-content"> | ||||
|         <div class="stat-value">{{ card.value }}</div> | ||||
|         <div class="stat-footer"> | ||||
|           <span class="trend-indicator up"> | ||||
|             <img src="/src/assets/demo/up.png" alt="up" class="trend-icon"> {{ card.trendChange }} | ||||
|           </span> | ||||
|           <el-select v-model="card.selectedTimeRange" placeholder="选择时间范围" style="width: 120px; font-size: 12px;"> | ||||
|             <el-option label="Today" value="today"></el-option> | ||||
|             <el-option label="This Week" value="week"></el-option> | ||||
|             <el-option label="This Month" value="month"></el-option> | ||||
|             <el-option label="This Year" value="year"></el-option> | ||||
|           </el-select> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref } from 'vue'; | ||||
|  | ||||
| // 统计卡片数据 | ||||
| interface StatCard { | ||||
|   id: string; | ||||
|   title: string; | ||||
|   value: string; | ||||
|   trendChange: string; | ||||
|   selectedTimeRange: string; | ||||
| } | ||||
|  | ||||
| const statCards = ref<StatCard[]>([ | ||||
|   { | ||||
|     id: 'power-total', | ||||
|     title: '总发电量', | ||||
|     value: '2,456.8', | ||||
|     trendChange: '4.2%', | ||||
|     selectedTimeRange: 'today' | ||||
|   }, | ||||
|   { | ||||
|     id: 'year-on-year', | ||||
|     title: '同比增长率', | ||||
|     value: '3.8%', | ||||
|     trendChange: '0.5%', | ||||
|     selectedTimeRange: 'today' | ||||
|   }, | ||||
|   { | ||||
|     id: 'month-on-month', | ||||
|     title: '环比增长率', | ||||
|     value: '2.1%', | ||||
|     trendChange: '0.3%', | ||||
|     selectedTimeRange: 'today' | ||||
|   }, | ||||
|   { | ||||
|     id: 'efficiency', | ||||
|     title: '运行效率', | ||||
|     value: '98.6%', | ||||
|     trendChange: '1.2%', | ||||
|     selectedTimeRange: 'today' | ||||
|   } | ||||
| ]); | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .zonglan-container { | ||||
|   display: flex; | ||||
|   gap: 20px; | ||||
|   width: 100%; | ||||
|   padding: 10px; | ||||
|   box-sizing: border-box; | ||||
| } | ||||
|  | ||||
| .stat-card { | ||||
|   flex: 1; | ||||
|   background: #fff; | ||||
|   border-radius: 8px; | ||||
|   padding: 20px; | ||||
|   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||||
|   transition: all 0.3s ease; | ||||
| } | ||||
|  | ||||
| .stat-card:hover { | ||||
|   transform: translateY(-2px); | ||||
|   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | ||||
| } | ||||
|  | ||||
| .card-header { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   margin-bottom: 15px; | ||||
| } | ||||
|  | ||||
| .card-title { | ||||
|   font-size: 14px; | ||||
|   color: #666; | ||||
|   font-weight: 500; | ||||
| } | ||||
|  | ||||
| .info-icon { | ||||
|   color: #c0c4cc; | ||||
|   cursor: pointer; | ||||
|   font-size: 16px; | ||||
|   transition: color 0.3s; | ||||
| } | ||||
|  | ||||
| .info-icon:hover { | ||||
|   color: #409eff; | ||||
| } | ||||
|  | ||||
| .card-content { | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| .stat-value { | ||||
|   font-size: 28px; | ||||
|   font-weight: 600; | ||||
|   color: #303133; | ||||
|   margin-bottom: 10px; | ||||
|   line-height: 1.2; | ||||
|   padding-bottom: 10px; | ||||
|   border-bottom: 1px solid #ebeef5; | ||||
| } | ||||
|  | ||||
| .stat-footer { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .trend-indicator { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   font-size: 12px; | ||||
|   font-weight: 500; | ||||
| } | ||||
|  | ||||
| .trend-indicator.up { | ||||
|   color: #67c23a; | ||||
| } | ||||
|  | ||||
| .trend-indicator.down { | ||||
|   color: #f56c6c; | ||||
| } | ||||
|  | ||||
| .trend-indicator i { | ||||
|   margin-right: 4px; | ||||
|   font-size: 12px; | ||||
| } | ||||
|  | ||||
| .trend-icon { | ||||
|   margin-right: 4px; | ||||
|   vertical-align: middle; | ||||
| } | ||||
|  | ||||
| .time-range { | ||||
|   font-size: 12px; | ||||
|   color: #909399; | ||||
| } | ||||
|  | ||||
| /* 响应式设计 */ | ||||
| @media (max-width: 1200px) { | ||||
|   .zonglan-container { | ||||
|     gap: 15px; | ||||
|   } | ||||
|  | ||||
|   .stat-card { | ||||
|     padding: 15px; | ||||
|   } | ||||
|  | ||||
|   .stat-value { | ||||
|     font-size: 24px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @media (max-width: 768px) { | ||||
|   .zonglan-container { | ||||
|     flex-direction: column; | ||||
|     gap: 12px; | ||||
|   } | ||||
|  | ||||
|   .stat-card { | ||||
|     padding: 15px; | ||||
|   } | ||||
|  | ||||
|   .stat-value { | ||||
|     font-size: 22px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @ -1,12 +1,81 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <DuibifenxiBar></DuibifenxiBar> | ||||
|         <tongbifenxiLine></tongbifenxiLine> | ||||
|         <detaildata></detaildata> | ||||
|     <div class="power-fenxi-container"> | ||||
|         <!-- 标题栏 --> | ||||
|         <el-row :gutter="24"> | ||||
|             <el-col :span="12"> | ||||
|                 <TitleComponent title="电量分析" subtitle="测量在电解过程中消耗的电荷量" /> | ||||
|             </el-col> | ||||
|             <!-- 外层col:控制整体宽度并右对齐,同时作为flex容器 --> | ||||
|             <el-col :span="12" style="display: flex; justify-content: flex-end; align-items: center;"> | ||||
|                 <el-col :span="4"> | ||||
|                     <el-button type="primary"> | ||||
|                         导出数据 | ||||
|                         <el-icon class="el-icon--right"> | ||||
|                             <UploadFilled /> | ||||
|                         </el-icon> | ||||
|                     </el-button> | ||||
|                 </el-col> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|         <!-- 第一排:总览组件 --> | ||||
|         <el-row :gutter="20" class="mb-4"> | ||||
|             <el-col :span="24"> | ||||
|                 <zonglan></zonglan> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|  | ||||
|         <el-row :gutter="24"> | ||||
|             <el-col :span="18"> | ||||
|                 <TitleComponent title="发电量对比分析" :font-level="2" /> | ||||
|             </el-col> | ||||
|             <el-col :span="3"> | ||||
|                 <el-select placeholder="请选择时间" style="width: 100%;"> | ||||
|                     <el-option label="今天" value="all"></el-option> | ||||
|                 </el-select> | ||||
|             </el-col> | ||||
|             <el-col :span="3"> | ||||
|                 <el-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始" | ||||
|                     end-placeholder="结束" style="width: 100%;" /> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|         <el-row :gutter="20" class="mb-4"> | ||||
|             <el-col :span="12"> | ||||
|                 <el-card> | ||||
|                     <DuibifenxiBar></DuibifenxiBar> | ||||
|                 </el-card> | ||||
|             </el-col> | ||||
|             <el-col :span="12"> | ||||
|                 <el-card> | ||||
|                     <tongbifenxiLine></tongbifenxiLine> | ||||
|                 </el-card> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|  | ||||
|         <!-- 第三排:详细数据组件 --> | ||||
|         <el-row :gutter="20"> | ||||
|             <el-col :span="24"> | ||||
|                 <el-card> | ||||
|                     <detaildata></detaildata> | ||||
|                 </el-card> | ||||
|             </el-col> | ||||
|         </el-row> | ||||
|     </div> | ||||
| </template> | ||||
| <script setup> | ||||
| import TitleComponent from '@/components/TitleComponent/index.vue'; | ||||
| import detaildata from '@/views/shengchanManage/powerfenxi/components/detaildata.vue' | ||||
| import tongbifenxiLine from './components/tongbifenxiLine.vue'; | ||||
| import DuibifenxiBar from './components/duibifenxiBar.vue'; | ||||
| import tongbifenxiLine from '@/views/shengchanManage/powerfenxi/components/tongbifenxiLine.vue'; | ||||
| import DuibifenxiBar from '@/views/shengchanManage/powerfenxi/components/duibifenxiBar.vue'; | ||||
| import zonglan from '@/views/shengchanManage/powerfenxi/components/zonglan.vue'; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .power-fenxi-container { | ||||
|     padding: 20px; | ||||
|     background-color: rgba(242, 248, 252, 1); | ||||
| } | ||||
|  | ||||
| .mb-4 { | ||||
|     margin-bottom: 20px; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -15,92 +15,113 @@ | ||||
|                 <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="创建时间" style="width: 308px"> | ||||
|               <el-date-picker | ||||
|                 v-model="dateRange" | ||||
|                 value-format="YYYY-MM-DD HH:mm:ss" | ||||
|                 type="daterange" | ||||
|                 range-separator="-" | ||||
|                 start-placeholder="开始日期" | ||||
|                 end-placeholder="结束日期" | ||||
|                 :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" | ||||
|               ></el-date-picker> | ||||
|             </el-form-item> | ||||
|  | ||||
|             <el-form-item> | ||||
|               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> | ||||
|               <el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['system:role:query']">搜索</el-button> | ||||
|               <el-button icon="Refresh" @click="resetQuery">重置</el-button> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </el-card> | ||||
|       </div> | ||||
|     </transition> | ||||
|  | ||||
|     <el-card shadow="hover"> | ||||
|       <template #header> | ||||
|         <el-row :gutter="10"> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()">删除</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table ref="roleTableRef" border v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column v-if="false" label="角色编号" prop="roleId" width="120" /> | ||||
|         <el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" /> | ||||
|         <el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" /> | ||||
|         <el-table-column label="显示顺序" prop="roleSort" width="100" /> | ||||
|         <el-table-column label="状态" align="center" width="100"> | ||||
|           <template #default="scope"> | ||||
|             <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> | ||||
|     <el-row :gutter="20"> | ||||
|       <!-- 部门树 --> | ||||
|       <el-col :lg="4" :xs="24" style=""> | ||||
|         <el-card shadow="hover"> | ||||
|           <el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable /> | ||||
|           <el-tree | ||||
|             ref="deptTreeRef" | ||||
|             class="mt-2" | ||||
|             node-key="id" | ||||
|             :data="deptOptions" | ||||
|             :props="{ label: 'label', children: 'children' }" | ||||
|             :expand-on-click-node="false" | ||||
|             :filter-node-method="filterNode" | ||||
|             highlight-current | ||||
|             default-expand-all | ||||
|             @node-click="handleNodeClick" | ||||
|           /> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|       <el-col :lg="20" :xs="24"> | ||||
|         <el-card shadow="hover"> | ||||
|           <template #header> | ||||
|             <el-row :gutter="10"> | ||||
|               <el-col :span="1.5"> | ||||
|                 <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button> | ||||
|               </el-col> | ||||
|               <el-col :span="1.5"> | ||||
|                 <el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()" | ||||
|                   >修改</el-button | ||||
|                 > | ||||
|               </el-col> | ||||
|               <el-col :span="1.5"> | ||||
|                 <el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()" | ||||
|                   >删除</el-button | ||||
|                 > | ||||
|               </el-col> | ||||
|               <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar> | ||||
|             </el-row> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="创建时间" align="center" prop="createTime"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ proxy.parseTime(scope.row.createTime) }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|           <el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"> | ||||
|             <el-table-column type="selection" width="55" align="center" /> | ||||
|             <el-table-column v-if="false" label="角色编号" prop="roleId" width="120" /> | ||||
|             <el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" /> | ||||
|             <el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" /> | ||||
|             <el-table-column label="显示顺序" prop="roleSort" width="100" /> | ||||
|             <el-table-column label="状态" align="center" width="100"> | ||||
|               <template #default="scope"> | ||||
|                 <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|             <el-table-column label="创建时间" align="center" prop="createTime"> | ||||
|               <template #default="scope"> | ||||
|                 <span>{{ proxy.parseTime(scope.row.createTime) }}</span> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|  | ||||
|         <el-table-column fixed="right" label="操作" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top"> | ||||
|               <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button> | ||||
|             </el-tooltip> | ||||
|             <el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top"> | ||||
|               <el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button> | ||||
|             </el-tooltip> | ||||
|             <el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top"> | ||||
|               <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button> | ||||
|             </el-tooltip> | ||||
|             <el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top"> | ||||
|               <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button> | ||||
|             </el-tooltip> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|             <el-table-column fixed="right" label="操作" width="180"> | ||||
|               <template #default="scope"> | ||||
|                 <el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top"> | ||||
|                   <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button> | ||||
|                 </el-tooltip> | ||||
|                 <el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top"> | ||||
|                   <el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button> | ||||
|                 </el-tooltip> | ||||
|                 <el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top"> | ||||
|                   <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button> | ||||
|                 </el-tooltip> | ||||
|                 <el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top"> | ||||
|                   <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button> | ||||
|                 </el-tooltip> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|           </el-table> | ||||
|  | ||||
|       <pagination | ||||
|         v-if="total > 0" | ||||
|         v-model:total="total" | ||||
|         v-model:page="queryParams.pageNum" | ||||
|         v-model:limit="queryParams.pageSize" | ||||
|         @pagination="getList" | ||||
|       /> | ||||
|     </el-card> | ||||
|           <pagination | ||||
|             v-if="total > 0" | ||||
|             v-model:total="total" | ||||
|             v-model:page="queryParams.pageNum" | ||||
|             v-model:limit="queryParams.pageSize" | ||||
|             @pagination="getList" | ||||
|           /> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|     </el-row> | ||||
|  | ||||
|     <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body> | ||||
|       <el-form ref="roleFormRef" :model="form" :rules="rules" label-width="100px"> | ||||
|       <el-form ref="roleFormRef" :model="form" :rules="rules" label-width="110px"> | ||||
|         <el-form-item label="所属部门" prop="deptId"> | ||||
|           <el-cascader | ||||
|             :options="deptOptions" | ||||
|             v-model="form.deptId" | ||||
|             placeholder="请选择所属部门" | ||||
|             clearable | ||||
|             filterable | ||||
|             :show-all-levels="false" | ||||
|             :props="{ value: 'id', emitPath: false, checkStrictly: true }" | ||||
|           > | ||||
|           </el-cascader> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="角色名称" prop="roleName"> | ||||
|           <el-input v-model="form.roleName" placeholder="请输入角色名称" /> | ||||
|         </el-form-item> | ||||
| @ -124,7 +145,7 @@ | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="菜单权限"> | ||||
|           <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox> | ||||
|           <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand(Boolean($event), 'menu')">展开/折叠</el-checkbox> | ||||
|           <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox> | ||||
|           <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox> | ||||
|           <el-tree | ||||
| @ -135,9 +156,12 @@ | ||||
|             node-key="id" | ||||
|             :check-strictly="!form.menuCheckStrictly" | ||||
|             empty-text="加载中,请稍候" | ||||
|             :props="{ label: 'label', children: 'children' } as any" | ||||
|             :props="{ label: 'label', children: 'children' }" | ||||
|           ></el-tree> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="是否为特殊角色"> | ||||
|           <el-switch v-model="form.isSpecial" active-value="1" inactive-value="0" active-text="是" inactive-text="否"> </el-switch> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="备注"> | ||||
|           <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> | ||||
|         </el-form-item> | ||||
| @ -165,7 +189,7 @@ | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item v-show="form.dataScope === '2'" label="数据权限"> | ||||
|           <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox> | ||||
|           <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand(Boolean($event), 'dept')">展开/折叠</el-checkbox> | ||||
|           <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox> | ||||
|           <el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox> | ||||
|           <el-tree | ||||
| @ -177,7 +201,7 @@ | ||||
|             node-key="id" | ||||
|             :check-strictly="!form.deptCheckStrictly" | ||||
|             empty-text="加载中,请稍候" | ||||
|             :props="{ label: 'label', children: 'children' } as any" | ||||
|             :props="{ label: 'label', children: 'children' }" | ||||
|           ></el-tree> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
| @ -196,6 +220,8 @@ import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updat | ||||
| import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index'; | ||||
| import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types'; | ||||
| import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types'; | ||||
| import api from '@/api/system/user'; | ||||
| import { DeptTreeVO, DeptVO } from '@/api/system/dept/types'; | ||||
|  | ||||
| const router = useRouter(); | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| @ -214,8 +240,11 @@ const menuExpand = ref(false); | ||||
| const menuNodeAll = ref(false); | ||||
| const deptExpand = ref(true); | ||||
| const deptNodeAll = ref(false); | ||||
| const deptOptions = ref<DeptTreeOption[]>([]); | ||||
| const deptOptions = ref<DeptTreeVO[]>([]); | ||||
| const enabledDeptOptions = ref<DeptTreeVO[]>([]); | ||||
|  | ||||
| const openDataScope = ref(false); | ||||
| const deptName = ref(''); | ||||
|  | ||||
| /** 数据范围选项*/ | ||||
| const dataScopeOptions = ref([ | ||||
| @ -232,8 +261,9 @@ const roleFormRef = ref<ElFormInstance>(); | ||||
| const dataScopeRef = ref<ElFormInstance>(); | ||||
| const menuRef = ref<ElTreeInstance>(); | ||||
| const deptRef = ref<ElTreeInstance>(); | ||||
| const deptTreeRef = ref<ElTreeInstance>(); | ||||
|  | ||||
| const initForm: RoleForm = { | ||||
| const initForm = { | ||||
|   roleId: undefined, | ||||
|   roleSort: 1, | ||||
|   status: '0', | ||||
| @ -244,17 +274,22 @@ const initForm: RoleForm = { | ||||
|   remark: '', | ||||
|   dataScope: '1', | ||||
|   menuIds: [], | ||||
|   deptIds: [] | ||||
|   deptId: '', | ||||
|   isSpecial: null, | ||||
|   deptIds: [], | ||||
|   roleSource: '1' | ||||
| }; | ||||
|  | ||||
| const data = reactive<PageData<RoleForm, RoleQuery>>({ | ||||
| const data = reactive({ | ||||
|   form: { ...initForm }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     roleName: '', | ||||
|     roleKey: '', | ||||
|     status: '' | ||||
|     deptId: '', | ||||
|     status: '', | ||||
|     roleSource: '1' | ||||
|   }, | ||||
|   rules: { | ||||
|     roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }], | ||||
| @ -269,6 +304,18 @@ const dialog = reactive<DialogOption>({ | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| /** 通过条件过滤节点  */ | ||||
| const filterNode = (value: string, data: any) => { | ||||
|   if (!value) return true; | ||||
|   return data.label.indexOf(value) !== -1; | ||||
| }; | ||||
|  | ||||
| /** 节点单击事件 */ | ||||
| const handleNodeClick = (data: DeptVO) => { | ||||
|   queryParams.value.deptId = data.id as string; | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * 查询角色列表 | ||||
|  */ | ||||
| @ -293,6 +340,9 @@ const handleQuery = () => { | ||||
| const resetQuery = () => { | ||||
|   dateRange.value = ['', '']; | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   queryParams.value.pageNum = 1; | ||||
|   queryParams.value.deptId = undefined; | ||||
|   deptTreeRef.value?.setCurrentKey(undefined); | ||||
|   handleQuery(); | ||||
| }; | ||||
| /**删除按钮操作 */ | ||||
| @ -323,7 +373,7 @@ const handleSelectionChange = (selection: RoleVO[]) => { | ||||
|  | ||||
| /** 角色状态修改 */ | ||||
| const handleStatusChange = async (row: RoleVO) => { | ||||
|   const text = row.status === '0' ? '启用' : '停用'; | ||||
|   let text = row.status === '0' ? '启用' : '停用'; | ||||
|   try { | ||||
|     await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?'); | ||||
|     await changeRoleStatus(row.roleId, row.status); | ||||
| @ -340,17 +390,17 @@ const handleAuthUser = (row: RoleVO) => { | ||||
|  | ||||
| /** 查询菜单树结构 */ | ||||
| const getMenuTreeselect = async () => { | ||||
|   const res = await menuTreeselect(); | ||||
|   const res = await menuTreeselect({ menuSource: '1' }); | ||||
|   menuOptions.value = res.data; | ||||
| }; | ||||
| /** 所有部门节点数据 */ | ||||
| const getDeptAllCheckedKeys = (): any => { | ||||
|   // 目前被选中的部门节点 | ||||
|   const checkedKeys = deptRef.value?.getCheckedKeys(); | ||||
|   let checkedKeys = deptRef.value?.getCheckedKeys(); | ||||
|   // 半选中的部门节点 | ||||
|   const halfCheckedKeys = deptRef.value?.getHalfCheckedKeys(); | ||||
|   let halfCheckedKeys = deptRef.value?.getHalfCheckedKeys(); | ||||
|   if (halfCheckedKeys) { | ||||
|     checkedKeys?.unshift(...halfCheckedKeys); | ||||
|     checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys); | ||||
|   } | ||||
|   return checkedKeys; | ||||
| }; | ||||
| @ -390,28 +440,28 @@ const handleUpdate = async (row?: RoleVO) => { | ||||
| }; | ||||
| /** 根据角色ID查询菜单树结构 */ | ||||
| const getRoleMenuTreeselect = (roleId: string | number) => { | ||||
|   return roleMenuTreeselect(roleId).then((res): RoleMenuTree => { | ||||
|   return roleMenuTreeselect(roleId, { menuSource: '1' }).then((res): RoleMenuTree => { | ||||
|     menuOptions.value = res.data.menus; | ||||
|     return res.data; | ||||
|   }); | ||||
| }; | ||||
| /** 根据角色ID查询部门树结构 */ | ||||
| const getRoleDeptTreeSelect = async (roleId: string | number) => { | ||||
|   const res = await deptTreeSelect(roleId); | ||||
|   const res = await deptTreeSelect(roleId, { roleSource: '1' }); | ||||
|   deptOptions.value = res.data.depts; | ||||
|   return res.data; | ||||
| }; | ||||
| /** 树权限(展开/折叠)*/ | ||||
| const handleCheckedTreeExpand = (value: boolean, type: string) => { | ||||
|   if (type == 'menu') { | ||||
|     const treeList = menuOptions.value; | ||||
|     let treeList = menuOptions.value; | ||||
|     for (let i = 0; i < treeList.length; i++) { | ||||
|       if (menuRef.value) { | ||||
|         menuRef.value.store.nodesMap[treeList[i].id].expanded = value; | ||||
|       } | ||||
|     } | ||||
|   } else if (type == 'dept') { | ||||
|     const treeList = deptOptions.value; | ||||
|     let treeList = deptOptions.value; | ||||
|     for (let i = 0; i < treeList.length; i++) { | ||||
|       if (deptRef.value) { | ||||
|         deptRef.value.store.nodesMap[treeList[i].id].expanded = value; | ||||
| @ -438,11 +488,11 @@ const handleCheckedTreeConnect = (value: any, type: string) => { | ||||
| /** 所有菜单节点数据 */ | ||||
| const getMenuAllCheckedKeys = (): any => { | ||||
|   // 目前被选中的菜单节点 | ||||
|   const checkedKeys = menuRef.value?.getCheckedKeys(); | ||||
|   let checkedKeys = menuRef.value?.getCheckedKeys(); | ||||
|   // 半选中的菜单节点 | ||||
|   const halfCheckedKeys = menuRef.value?.getHalfCheckedKeys(); | ||||
|   let halfCheckedKeys = menuRef.value?.getHalfCheckedKeys(); | ||||
|   if (halfCheckedKeys) { | ||||
|     checkedKeys?.unshift(...halfCheckedKeys); | ||||
|     checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys); | ||||
|   } | ||||
|   return checkedKeys; | ||||
| }; | ||||
| @ -496,8 +546,28 @@ const cancelDataScope = () => { | ||||
|   form.value = { ...initForm }; | ||||
|   openDataScope.value = false; | ||||
| }; | ||||
| /** 查询部门下拉树结构 */ | ||||
| const getDeptTree = async () => { | ||||
|   const res = await api.deptTreeSelect({ isShow: '1' }); | ||||
|   deptOptions.value = res.data; | ||||
|   enabledDeptOptions.value = filterDisabledDept(res.data); | ||||
| }; | ||||
|  | ||||
| /** 过滤禁用的部门 */ | ||||
| const filterDisabledDept = (deptList: DeptTreeVO[]) => { | ||||
|   return deptList.filter((dept) => { | ||||
|     if (dept.disabled) { | ||||
|       return false; | ||||
|     } | ||||
|     if (dept.children && dept.children.length) { | ||||
|       dept.children = filterDisabledDept(dept.children); | ||||
|     } | ||||
|     return true; | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getDeptTree(); // 初始化部门数据 | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
|  | ||||
		Reference in New Issue
	
	Block a user