wujingjing
2025-04-07 457cc6cf166d3b6c22be4f78c1db8802a7fbb4c7
src/views/project/ch/workspace/situation/index.vue
@@ -1,16 +1,491 @@
<template>
    <div class="workspace-situation">
        <div class="workspace-situation-header">
            <div class="workspace-situation-header-title">
                <span>个人工作台概况</span>
            </div>
        </div>
    </div>
   <div class="workspace-container h-full">
      <!-- 左侧主要内容区 -->
      <div class="flex flex-col h-full overflow-hidden" :style="{ gap: layoutGap }">
         <!-- 工作概览 -->
         <div class="overview-section flex-0 bg-white border border-solid border-white rounded-lg p-[20px] h-[150px]">
            <div class="section-header mb-4">
               <h3 class="font-bold">工作概览</h3>
            </div>
            <div class="overview-cards">
               <!-- 管理人数 -->
               <div class="overview-card bg-[#dee8ff] rounded-lg p-4 flex items-center">
                  <i class="i-carbon:user-multiple text-3xl text-blue-500 mr-4"></i>
                  <div>
                     <div class="text-3xl font-bold text-[#4f85f6]">3</div>
                     <div class="text-gray-600">管理人数</div>
                  </div>
               </div>
               <!-- 待办事项 -->
               <div class="overview-card bg-[#faeaed] rounded-lg p-4 flex items-center">
                  <i class="i-carbon:task text-3xl text-red-500 mr-4"></i>
                  <div>
                     <div class="text-3xl font-bold text-red-500">3</div>
                     <div class="text-gray-600">待办事项</div>
                  </div>
               </div>
               <!-- 预警事项 -->
               <div class="overview-card bg-[#dbf2f8] rounded-lg p-4 flex items-center">
                  <i class="i-carbon:warning text-3xl text-cyan-500 mr-4"></i>
                  <div>
                     <div class="text-3xl font-bold text-cyan-500">3</div>
                     <div class="text-gray-600">预警事项</div>
                  </div>
               </div>
            </div>
         </div>
         <div class="flex flex-col flex-auto" :style="{ gap: layoutGap }">
            <!-- 待办事项和我发起的表格 -->
            <div class="flex h-1/2" :style="{ gap: layoutGap }">
               <!-- 待办事项 -->
               <div class="bg-white rounded-lg p-4 shadow-sm flex flex-col w-1/2">
                  <div class="flex justify-between items-center mb-4 flex-0">
                     <div class="text-lg font-bold">待办事项</div>
                     <el-tabs v-model="todoType" class="todo-tabs">
                        <el-tab-pane label="待办事项" name="todo" />
                        <el-tab-pane label="已办事项" name="done" />
                     </el-tabs>
                  </div>
                  <el-table class="flex-auto" :data="todoList" style="width: 100%" size="small">
                     <el-table-column prop="name" label="事件类型" min-width="120">
                        <template #default="{ row }">
                           <span :class="getEventTypeClass(row.type)">{{ row.name }}</span>
                        </template>
                     </el-table-column>
                     <el-table-column prop="sender" label="发起人" width="100" />
                     <el-table-column prop="time" label="发起时间" width="100" />
                     <el-table-column prop="status" label="状态" width="80">
                        <template #default="{ row }">
                           <el-tag :type="getStatusType(row.status)" size="small">
                              {{ row.status }}
                           </el-tag>
                        </template>
                     </el-table-column>
                     <el-table-column label="操作" width="80" fixed="right">
                        <template #default>
                           <el-button type="primary" size="small" class="custom-button">办理</el-button>
                        </template>
                     </el-table-column>
                  </el-table>
               </div>
               <!-- 我发起的 -->
               <div class="bg-white rounded-lg p-4 shadow-sm flex flex-col h-full w-1/2">
                  <div class="flex justify-between items-center mb-4 flex-0">
                     <div class="text-lg font-bold">我发起的</div>
                     <el-tabs v-model="initiatedType" class="todo-tabs">
                        <el-tab-pane label="我发起" name="initiated" />
                        <el-tab-pane label="我收到" name="received" />
                     </el-tabs>
                  </div>
                  <el-table :data="initiatedList" class="flex-auto" style="width: 100%" size="small">
                     <el-table-column prop="type" label="类型" min-width="120">
                        <template #default="{ row }">
                           <span :class="getEventTypeClass(row.type)">{{ row.type }}</span>
                        </template>
                     </el-table-column>
                     <el-table-column prop="sender" label="发起人" width="100" />
                     <el-table-column prop="time" label="发起时间" width="100" />
                     <el-table-column prop="status" label="状态" width="80">
                        <template #default="{ row }">
                           <el-tag :type="getStatusType(row.status)" size="small">
                              {{ row.status }}
                           </el-tag>
                        </template>
                     </el-table-column>
                     <el-table-column label="操作" width="80" fixed="right">
                        <template #default>
                           <el-button type="primary" size="small" class="custom-button">详情</el-button>
                        </template>
                     </el-table-column>
                  </el-table>
               </div>
            </div>
            <!-- 预警表格 -->
            <div class="bg-white rounded-lg p-4 shadow-sm h-1/2">
               <div class="text-lg font-bold mb-4">预警</div>
               <el-table :data="warningList" style="width: 100%" size="small">
                  <el-table-column prop="studentId" label="学号" min-width="120" />
                  <el-table-column prop="name" label="姓名" width="100" />
                  <el-table-column prop="type" label="学生类型" width="100" />
                  <el-table-column prop="guardianId" label="护照号" min-width="150" />
                  <el-table-column prop="warningType" label="预警类型" width="120">
                     <template #default="{ row }">
                        <el-tag :type="getWarningType(row.warningType)" size="small">
                           {{ row.warningType }}
                        </el-tag>
                     </template>
                  </el-table-column>
                  <el-table-column label="操作" width="100" fixed="right">
                     <template #default>
                        <el-button type="primary" size="small" class="custom-button">提交预警</el-button>
                     </template>
                  </el-table-column>
               </el-table>
            </div>
         </div>
      </div>
      <!-- 右侧边栏 -->
      <div class="sidebar h-full overflow-hidden flex flex-col" :style="{ gap: layoutGap }">
         <!-- 个人信息 -->
         <div class="profile-card p-[20px] bg-white border border-solid border-white rounded-lg h-[150px] flex-0">
            <div class="section-header mb-4">
               <h3>个人信息</h3>
            </div>
            <div class="profile-header flex gap-4">
               <!-- <div class="avatar-placeholder">
                  <i class="i-carbon:user text-3xl text-gray-400"></i>
               </div> -->
               <div class="profile-info flex flex-col gap-2">
                  <div class="flex items-center" v-for="item in 3" :key="item">
                     <span class="mr-1">用户名:</span>
                     <span>wjj</span>
                  </div>
               </div>
                    <div class="profile-info flex flex-col gap-2">
                  <div class="flex items-center" v-for="item in 3" :key="item">
                     <span class="mr-1">用户名:</span>
                     <span>wjj</span>
                  </div>
               </div>
            </div>
         </div>
         <!-- 统计图表 -->
         <div class="chart-container p-[20px] h-1/2 overflow-hidden">
            <div class="chart-header">
               <h3>统计图表</h3>
               <span class="subtitle">国别</span>
            </div>
            <div class="chart" ref="chartRef"></div>
         </div>
         <!-- 日历 -->
         <div class="calendar-container p-[20px] h-1/2 overflow-hidden">
            <div class="calendar-header">
               <h3>时间</h3>
               <div class="flex items-center gap-2">
                  <el-date-picker v-model="currentMonth" type="month" format="YYYY年MM月" :placeholder="'选择月份'" size="small" />
               </div>
            </div>
            <div class="calendar-notice mb-0.5">您今日无待办</div>
            <el-calendar v-model="currentDate">
               <template #dateCell="{ data }">
                  <div class="custom-calendar-cell">
                     <span class="date-text">{{ data.day.split('-')[2] }}</span>
                     <div v-if="hasEvent(data)" class="event-dot"></div>
                  </div>
               </template>
            </el-calendar>
         </div>
      </div>
   </div>
</template>
<script setup lang="ts" name="WorkspaceSituation">
import { ref } from 'vue';
<script setup lang="ts">
import * as echarts from 'echarts';
import { computed, onMounted, ref } from 'vue';
const layoutGap = '10px';
interface CalendarData {
   day: string;
   [key: string]: any;
}
// 状态变量
const todoType = ref('todo');
const initiatedType = ref('initiated');
const currentDate = ref(new Date('2025-04-06'));
const currentMonth = computed(() => {
   const date = currentDate.value;
   return date instanceof Date ? date : new Date(date);
});
const chartRef = ref<HTMLElement>();
// 示例数据 - 增加更多数据
const todoList = ref([
   { name: '实验室更新', type: 'update', sender: '王美丽', time: '2021.12.06', status: '待确认' },
   { name: '新生指导', type: 'guide', sender: '李明', time: '2021.12.06', status: '未读' },
   { name: '活动报名', type: 'activity', sender: '张三', time: '2021.12.06', status: '已读' },
   { name: '课程变更', type: 'update', sender: '李四', time: '2021.12.06', status: '待确认' },
   { name: '会议通知', type: 'notice', sender: '王五', time: '2021.12.06', status: '未读' },
   { name: '教材订购', type: 'order', sender: '赵六', time: '2021.12.06', status: '已处理' },
   { name: '成绩录入', type: 'grade', sender: '孙七', time: '2021.12.06', status: '待确认' },
   { name: '请假审批', type: 'leave', sender: '周八', time: '2021.12.06', status: '已读' },
   { name: '请假审批', type: 'leave', sender: '周八', time: '2021.12.06', status: '已读' },
   { name: '请假审批', type: 'leave', sender: '周八', time: '2021.12.06', status: '已读' },
   { name: '请假审批', type: 'leave', sender: '周八', time: '2021.12.06', status: '已读' },
   { name: '请假审批', type: 'leave', sender: '周八', time: '2021.12.06', status: '已读' },
]);
const initiatedList = ref([
   { type: '活动', sender: '王美丽', time: '2021.12.06', status: '进行中' },
   { type: '报到注册', sender: '李明', time: '2021.12.06', status: '关闭' },
   { type: '课程变更', sender: '张三', time: '2021.12.06', status: '待审核' },
   { type: '会议通知', sender: '李四', time: '2021.12.06', status: '已完成' },
   { type: '教材订购', sender: '王五', time: '2021.12.06', status: '进行中' },
   { type: '成绩录入', sender: '赵六', time: '2021.12.06', status: '待审核' },
   { type: '请假审批', sender: '孙七', time: '2021.12.06', status: '已拒绝' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
   { type: '活动策划', sender: '周八', time: '2021.12.06', status: '进行中' },
]);
const warningList = ref([
   { studentId: 'N20930498594', name: '王美丽', type: '高中', guardianId: '309209382903943', warningType: '迟到预警' },
   { studentId: 'N20930498595', name: '李明', type: '高中', guardianId: '309209382903944', warningType: '缺勤预警' },
   { studentId: 'N20930498596', name: '张三', type: '初中', guardianId: '309209382903945', warningType: '成绩预警' },
   { studentId: 'N20930498597', name: '李四', type: '高中', guardianId: '309209382903946', warningType: '行为预警' },
   { studentId: 'N20930498598', name: '王五', type: '初中', guardianId: '309209382903947', warningType: '迟到预警' },
]);
// 工具函数
const getEventTypeClass = (type: string) => {
   const classes = {
      update: 'text-green-500',
      guide: 'text-blue-500',
      activity: 'text-orange-500',
      notice: 'text-purple-500',
      order: 'text-cyan-500',
      grade: 'text-pink-500',
      leave: 'text-indigo-500',
   };
   return classes[type] || '';
};
const getStatusType = (status: string) => {
   const types = {
      待确认: 'warning',
      未读: 'danger',
      已读: 'info',
      已处理: 'success',
      进行中: 'primary',
      关闭: 'info',
      待审核: 'warning',
      已完成: 'success',
      已拒绝: 'danger',
   };
   return types[status] || 'default';
};
const getWarningType = (type: string) => {
   const types = {
      迟到预警: 'danger',
      缺勤预警: 'warning',
      成绩预警: 'info',
      行为预警: 'warning',
   };
   return types[type] || 'default';
};
const hasEvent = (date: CalendarData) => {
   // 实现判断日期是否有事件的逻辑
   return Math.random() > 0.8;
};
// 图表初始化
onMounted(() => {
   if (chartRef.value) {
      const chart = echarts.init(chartRef.value);
      chart.setOption({
         xAxis: {
            type: 'category',
            data: ['中国', '德国', '法国', '英国', '新西兰', '美国', '瑞士'],
         },
         yAxis: {
            type: 'value',
         },
         series: [
            {
               data: [80, 50, 100, 30, 70, 80, 60],
               type: 'bar',
               barWidth: '30%',
               itemStyle: {
                  color: '#409EFF',
               },
            },
         ],
      });
   }
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.workspace-container {
   display: grid;
   grid-template-columns: 1fr 300px;
   gap: v-bind(layoutGap);
   padding: 20px;
   background-color: #f5f7fa;
}
.overview-cards {
   display: grid;
   grid-template-columns: repeat(3, 1fr);
   gap: 20px;
}
// 自定义按钮样式
.custom-button {
   --el-button-bg-color: var(--color-btn-base);
   --el-button-border-color: var(--color-btn-base);
   --el-button-hover-bg-color: var(--color-btn-hover);
   --el-button-hover-border-color: var(--color-btn-hover);
}
// 自定义 tabs 样式
:deep(.todo-tabs) {
   .el-tabs__header {
      margin: 0;
   }
   .el-tabs__nav-wrap::after {
      display: none;
   }
   .el-tabs__item {
      padding: 0 10px;
      height: 32px;
      line-height: 32px;
      font-size: 14px;
      &.is-active {
         color: var(--color-btn-base);
      }
   }
   .el-tabs__active-bar {
      background-color: var(--color-btn-base);
   }
}
// 自定义日历样式
:deep(.el-calendar) {
   --el-calendar-border: none;
   --el-calendar-header-border-bottom: none;
   background: none;
   .el-calendar__header {
      display: none;
   }
   .el-calendar__body {
      padding: 12px 0;
   }
   .el-calendar-table {
      td {
         border: none;
         padding: 4px;
      }
      .current {
         background: none;
      }
   }
}
.custom-calendar-cell {
   height: 32px;
   display: flex;
   align-items: center;
   justify-content: center;
   position: relative;
   .date-text {
      font-size: 14px;
   }
   .event-dot {
      position: absolute;
      top: 2px;
      right: 2px;
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background-color: var(--el-color-primary);
   }
}
.calendar-notice {
   background-color: #fff7e6;
   color: #fa8c16;
   padding: 8px 12px;
   border-radius: 4px;
   font-size: 14px;
}
.avatar-placeholder {
   width: 64px;
   height: 64px;
   border-radius: 50%;
   background-color: #f5f7fa;
   display: flex;
   align-items: center;
   justify-content: center;
}
:deep(.el-calendar-table .el-calendar-day) {
   --el-calendar-cell-width: 10px;
}
.chart-container {
   background: white;
   border-radius: 8px;
   .chart {
      height: 300px;
   }
   .chart-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 16px;
      .subtitle {
         color: var(--el-text-color-secondary);
         font-size: 14px;
      }
   }
}
.calendar-container {
   background: white;
   border-radius: 8px;
   .calendar-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 16px;
      h3 {
         font-size: 16px;
         font-weight: 500;
         margin: 0;
      }
   }
}
:deep(.el-date-picker) {
   --el-input-width: 120px;
}
.section-header {
   h3 {
      font-size: 16px;
      // font-weight: 500;
      // margin: 0;
      // color: var(--el-text-color-primary);
   }
}
</style>