diff --git a/app/Exports/CommonExport.php b/app/Exports/CommonExport.php index c90a038..8b1f56b 100755 --- a/app/Exports/CommonExport.php +++ b/app/Exports/CommonExport.php @@ -20,7 +20,30 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With public $fields; public $data; public $hasUsersField = false; - public $usersColumnIndex = null; + public $hasProjectUsersField = false; + public $totalColumns = 0; + public $totalRows = 0; + public $expandedFields = []; + + // 学员信息子列定义 + const USERS_SUB_COLUMNS = [ + 'user_index' => '序号', + 'user_no' => '学号', + 'user_name' => '姓名', + 'user_schoolmate' => '校友', + 'user_position' => '职位', + 'user_mobile' => '手机', + 'user_course' => '报名课程', + 'user_sign_date' => '报名时间', + ]; + + // 项目经理信息子列定义 + const PROJECT_USERS_SUB_COLUMNS = [ + 'pm_platform' => '管理平台', + 'pm_name' => '项目经理', + 'pm_invest_date' => '首次出资时间', + 'pm_amount' => '投资金额', + ]; public function __construct($data, $exportFields) { @@ -29,17 +52,49 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With // 数据 $this->data = $data; - // 检查是否有 users 字段 - if (is_array($exportFields)) { - $index = 1; - foreach (array_keys($exportFields) as $field) { - if (str_contains($field, 'users')) { - $this->hasUsersField = true; - $this->usersColumnIndex = $this->getColumnLetter($index); + // 构建扩展后的字段列表 + $this->buildExpandedFields(); + } + + /** + * 构建扩展后的字段列表(将users和project_users字段展开成多列) + */ + private function buildExpandedFields() + { + if (!is_array($this->fields)) { + return; + } + + $index = 1; + foreach ($this->fields as $field => $label) { + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { + $this->hasUsersField = true; + // 展开学员信息为多列 + foreach (self::USERS_SUB_COLUMNS as $subField => $subLabel) { + $this->expandedFields[$subField] = $subLabel; + $index++; + } + } elseif (str_contains($field, 'project_users')) { + $this->hasProjectUsersField = true; + // 展开项目经理信息为多列 + foreach (self::PROJECT_USERS_SUB_COLUMNS as $subField => $subLabel) { + $this->expandedFields[$subField] = $subLabel; + $index++; } + } else { + $this->expandedFields[$field] = $label; $index++; } } + $this->totalColumns = count($this->expandedFields); + } + + /** + * 是否需要二级表头 + */ + private function needsDoubleHeader() + { + return $this->hasUsersField || $this->hasProjectUsersField; } /** @@ -63,22 +118,23 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With { $widths = []; $index = 1; - foreach (array_keys($this->fields) as $field) { + foreach (array_keys($this->expandedFields) as $field) { $letter = $this->getColumnLetter($index); - if (str_contains($field, 'users')) { - // 学员信息列设置较宽 - $widths[$letter] = 80; - } elseif (str_contains($field, 'partners') || str_contains($field, 'project_users')) { - // 股东和项目经理列 + if (in_array($field, ['user_course', 'user_name'])) { + $widths[$letter] = 25; + } elseif (in_array($field, ['user_mobile', 'user_sign_date', 'pm_invest_date', 'pm_amount'])) { + $widths[$letter] = 15; + } elseif (in_array($field, ['user_index', 'user_schoolmate'])) { + $widths[$letter] = 8; + } elseif (in_array($field, ['pm_platform', 'pm_name'])) { + $widths[$letter] = 18; + } elseif (str_contains($field, 'partners')) { $widths[$letter] = 50; } elseif (str_contains($field, 'all_course')) { - // 课程列 $widths[$letter] = 40; } elseif (str_contains($field, 'company_name') || str_contains($field, 'address')) { - // 公司名称和地址 $widths[$letter] = 30; } else { - // 默认列宽 $widths[$letter] = 15; } $index++; @@ -91,13 +147,14 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With */ public function styles(Worksheet $sheet): array { - $rowCount = count($this->data) + 1; // 数据行数 + 表头 - $colCount = count($this->fields); - $lastCol = $this->getColumnLetter($colCount); + $lastCol = $this->getColumnLetter($this->totalColumns); + $dataStartRow = $this->needsDoubleHeader() ? 3 : 2; - return [ - // 表头样式 - 1 => [ + $styles = []; + + if ($this->needsDoubleHeader()) { + // 二级表头样式 - 第一行 + $styles[1] = [ 'font' => ['bold' => true, 'size' => 12], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, @@ -105,23 +162,51 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With ], 'fill' => [ 'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, - 'startColor' => ['rgb' => 'E0E0E0'], + 'startColor' => ['rgb' => 'D0D0D0'], ], - ], - // 所有数据区域样式 - "A1:{$lastCol}{$rowCount}" => [ + ]; + // 二级表头样式 - 第二行 + $styles[2] = [ + 'font' => ['bold' => true, 'size' => 11], 'alignment' => [ - 'vertical' => Alignment::VERTICAL_TOP, - 'wrapText' => true, // 自动换行 + 'horizontal' => Alignment::HORIZONTAL_CENTER, + 'vertical' => Alignment::VERTICAL_CENTER, ], - 'borders' => [ - 'allBorders' => [ - 'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN, - 'color' => ['rgb' => 'CCCCCC'], - ], + 'fill' => [ + 'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, + 'startColor' => ['rgb' => 'E8E8E8'], + ], + ]; + } else { + // 单行表头样式 + $styles[1] = [ + 'font' => ['bold' => true, 'size' => 12], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + 'vertical' => Alignment::VERTICAL_CENTER, + ], + 'fill' => [ + 'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, + 'startColor' => ['rgb' => 'E0E0E0'], + ], + ]; + } + + // 所有数据区域样式 + $styles["A1:{$lastCol}" . ($this->totalRows + $dataStartRow - 1)] = [ + 'alignment' => [ + 'vertical' => Alignment::VERTICAL_CENTER, + 'wrapText' => true, + ], + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN, + 'color' => ['rgb' => 'CCCCCC'], ], ], ]; + + return $styles; } /** @@ -132,34 +217,62 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With return [ AfterSheet::class => function (AfterSheet $event) { $sheet = $event->sheet->getDelegate(); - $rowCount = count($this->data) + 1; - - // 设置表头行高 - $sheet->getRowDimension(1)->setRowHeight(25); - - // 遍历数据行,根据内容设置行高 - for ($row = 2; $row <= $rowCount; $row++) { - // 获取该行的最大内容行数 - $maxLines = 1; - foreach (array_keys($this->fields) as $index => $field) { - $col = $this->getColumnLetter($index + 1); - $cellValue = $sheet->getCell($col . $row)->getValue(); - if ($cellValue) { - $lines = substr_count($cellValue, "\n") + 1; - $maxLines = max($maxLines, $lines); - } - } - // 设置行高(每行内容约15像素,最小20,最大400) - $rowHeight = min(400, max(20, $maxLines * 15)); - $sheet->getRowDimension($row)->setRowHeight($rowHeight); + + if ($this->needsDoubleHeader()) { + // 处理二级表头的单元格合并 + $this->mergeHeaderCells($sheet); + + // 设置表头行高 + $sheet->getRowDimension(1)->setRowHeight(25); + $sheet->getRowDimension(2)->setRowHeight(22); + + // 冻结前两行 + $sheet->freezePane('A3'); + } else { + $sheet->getRowDimension(1)->setRowHeight(25); + $sheet->freezePane('A2'); } - // 冻结首行 - $sheet->freezePane('A2'); + // 设置数据行高 + $dataStartRow = $this->needsDoubleHeader() ? 3 : 2; + for ($row = $dataStartRow; $row <= $this->totalRows + $dataStartRow - 1; $row++) { + $sheet->getRowDimension($row)->setRowHeight(22); + } }, ]; } + /** + * 合并表头单元格 + */ + private function mergeHeaderCells(Worksheet $sheet) + { + $index = 1; + foreach ($this->fields as $field => $label) { + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { + // 学员信息列:合并第一行的多个单元格 + $startCol = $this->getColumnLetter($index); + $endCol = $this->getColumnLetter($index + count(self::USERS_SUB_COLUMNS) - 1); + $sheet->mergeCells("{$startCol}1:{$endCol}1"); + $sheet->setCellValue("{$startCol}1", $label); + $index += count(self::USERS_SUB_COLUMNS); + } elseif (str_contains($field, 'project_users')) { + // 项目经理信息列:合并第一行的多个单元格 + $startCol = $this->getColumnLetter($index); + $endCol = $this->getColumnLetter($index + count(self::PROJECT_USERS_SUB_COLUMNS) - 1); + $sheet->mergeCells("{$startCol}1:{$endCol}1"); + $sheet->setCellValue("{$startCol}1", $label); + $index += count(self::PROJECT_USERS_SUB_COLUMNS); + } else { + // 其他列:合并第一行和第二行 + $col = $this->getColumnLetter($index); + $sheet->mergeCells("{$col}1:{$col}2"); + $sheet->setCellValue("{$col}1", $label); + $index++; + } + } + } + /** * 数组转集合 * @throws ErrorException @@ -173,65 +286,222 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With if (!is_array($this->fields)) { throw new ErrorException('导出字段必须是数组'); } - // 获取表头 - $header = array_values($this->fields); - $moreFileds = []; - if (empty($clear)) { - // 表头追加附属数据 - if (isset($this->data[0]['data']) && is_array($this->data[0]['data'])) { - $moreHeader = array_column($this->data[0]['data'], 'name'); - // 获取头信息 - $header = array_merge($header, $moreHeader); - // 获取字段信息 - $moreFileds = array_column($this->data[0]['data'], 'field'); - } - } - // 获取字段指向 - $fields = array_keys($this->fields); + $newList = []; - foreach ($this->data as $info) { - $temp = []; - foreach ($fields as $field) { - if (str_contains($field, 'idcard')) { - // 身份证 - $temp[$field] = ' ' . $this->getDotValue($info, $field); - } elseif (str_contains($field, 'all_course')) { - // 所有课程 - $temp[$field] = $this->allCourse($info); - } elseif (str_contains($field, 'partners')) { - // 股东信息 - $temp[$field] = $this->partners($info); + + if ($this->needsDoubleHeader()) { + // 有需要展开的字段:创建二级表头 + // 第一行表头(主表头)- 在 mergeHeaderCells 中处理 + $header1 = []; + foreach ($this->fields as $field => $label) { + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { + foreach (self::USERS_SUB_COLUMNS as $subLabel) { + $header1[] = ''; + } } elseif (str_contains($field, 'project_users')) { - // 项目经理 - $temp[$field] = $this->projectManager($info); - } elseif (str_contains($field, 'users')) { - // 学员信息 - $temp[$field] = $this->getUsers($info); + foreach (self::PROJECT_USERS_SUB_COLUMNS as $subLabel) { + $header1[] = ''; + } } else { - $temp[$field] = $this->getDotValue($info, $field); + $header1[] = ''; } } - // 如果有自定义数据,全部附件上去 - $t2 = []; + $newList[] = $header1; + + // 第二行表头(子表头) + $header2 = []; + foreach ($this->fields as $field => $label) { + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { + foreach (self::USERS_SUB_COLUMNS as $subLabel) { + $header2[] = $subLabel; + } + } elseif (str_contains($field, 'project_users')) { + foreach (self::PROJECT_USERS_SUB_COLUMNS as $subLabel) { + $header2[] = $subLabel; + } + } else { + $header2[] = ''; + } + } + $newList[] = $header2; + + // 数据行:每个展开记录占一行 + foreach ($this->data as $info) { + $expandedRows = $this->getExpandedRows($info); + if (empty($expandedRows)) { + // 没有展开数据,输出一行空数据 + $row = $this->buildExpandedRow($info, [], []); + $newList[] = $row; + } else { + foreach ($expandedRows as $expandedRow) { + $row = $this->buildExpandedRow($info, $expandedRow['users'] ?? [], $expandedRow['project_users'] ?? []); + $newList[] = $row; + } + } + } + } else { + // 没有需要展开的字段:使用原有逻辑 + $header = array_values($this->fields); + $moreFileds = []; if (empty($clear)) { - if (isset($info['data']) && $info['data'] && !empty($moreFileds)) { - $dataCollect = collect($info['data']); - foreach ($moreFileds as $moreFiled) { - $value = ($dataCollect->where('field', $moreFiled)->first()['value']) ?? ''; - if (str_contains($moreFiled, 'idcard')) { - $t2[$moreFiled] = ' ' . $value; - } else { - $t2[$moreFiled] = $value; + if (isset($this->data[0]['data']) && is_array($this->data[0]['data'])) { + $moreHeader = array_column($this->data[0]['data'], 'name'); + $header = array_merge($header, $moreHeader); + $moreFileds = array_column($this->data[0]['data'], 'field'); + } + } + $newList[] = $header; + + foreach ($this->data as $info) { + $temp = []; + foreach (array_keys($this->fields) as $field) { + if (str_contains($field, 'idcard')) { + $temp[$field] = ' ' . $this->getDotValue($info, $field); + } elseif (str_contains($field, 'all_course')) { + $temp[$field] = $this->allCourse($info); + } elseif (str_contains($field, 'partners')) { + $temp[$field] = $this->partners($info); + } else { + $temp[$field] = $this->getDotValue($info, $field); + } + } + $t2 = []; + if (empty($clear)) { + if (isset($info['data']) && $info['data'] && !empty($moreFileds)) { + $dataCollect = collect($info['data']); + foreach ($moreFileds as $moreFiled) { + $value = ($dataCollect->where('field', $moreFiled)->first()['value']) ?? ''; + if (str_contains($moreFiled, 'idcard')) { + $t2[$moreFiled] = ' ' . $value; + } else { + $t2[$moreFiled] = $value; + } } } } + $newList[] = array_values($temp + $t2); } - $newList[] = $temp + $t2; } - array_unshift($newList, $header); //插入表头 + + $this->totalRows = count($newList) - ($this->needsDoubleHeader() ? 2 : 1); return new Collection($newList); } + /** + * 获取展开后的所有行数据 + */ + private function getExpandedRows($info) + { + $usersData = $this->hasUsersField ? $this->getUsersExpanded($info) : []; + $projectUsersData = $this->hasProjectUsersField ? $this->getProjectUsersExpanded($info) : []; + + // 计算最大行数 + $maxRows = max(count($usersData), count($projectUsersData), 1); + + $result = []; + for ($i = 0; $i < $maxRows; $i++) { + $result[] = [ + 'users' => $usersData[$i] ?? [], + 'project_users' => $projectUsersData[$i] ?? [], + ]; + } + + return $result; + } + + /** + * 获取展开的项目经理数据(每条记录一行) + */ + private function getProjectUsersExpanded($info) + { + if (empty($info['project_users'])) { + return []; + } + + $result = []; + foreach ($info['project_users'] as $item) { + $result[] = [ + 'pm_platform' => $item['groupName'] ?? '-', + 'pm_name' => $item['userName'] ?? '-', + 'pm_invest_date' => $item['investDate'] ?? '-', + 'pm_amount' => $item['amount'] ?? '-', + ]; + } + return $result; + } + + /** + * 获取展开的学员数据(每个学员每条课程一行) + */ + private function getUsersExpanded($info) + { + if (empty($info['users'])) { + return []; + } + + $result = []; + $userIndex = 1; + foreach ($info['users'] as $user) { + if (!empty($user['course_signs'])) { + foreach ($user['course_signs'] as $signIndex => $sign) { + $result[] = [ + 'user_index' => $signIndex === 0 ? $userIndex : '', + 'user_no' => $signIndex === 0 ? ($user['no'] ?? '-') : '', + 'user_name' => $signIndex === 0 ? ($user['username'] ?? '-') : '', + 'user_schoolmate' => $signIndex === 0 ? ($user['is_schoolmate_text'] ?? '-') : '', + 'user_position' => $signIndex === 0 ? ($user['company_position'] ?? '-') : '', + 'user_mobile' => $signIndex === 0 ? ($user['mobile'] ?? '-') : '', + 'user_course' => $sign['course']['name'] ?? '-', + 'user_sign_date' => isset($sign['created_at']) ? substr($sign['created_at'], 0, 10) : '-', + ]; + } + } else { + $result[] = [ + 'user_index' => $userIndex, + 'user_no' => $user['no'] ?? '-', + 'user_name' => $user['username'] ?? '-', + 'user_schoolmate' => $user['is_schoolmate_text'] ?? '-', + 'user_position' => $user['company_position'] ?? '-', + 'user_mobile' => $user['mobile'] ?? '-', + 'user_course' => '-', + 'user_sign_date' => '-', + ]; + } + $userIndex++; + } + return $result; + } + + /** + * 构建包含展开数据的行 + */ + private function buildExpandedRow($info, $userData, $projectUserData) + { + $row = []; + foreach ($this->fields as $field => $label) { + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { + // 填充学员信息列 + foreach (array_keys(self::USERS_SUB_COLUMNS) as $subField) { + $row[] = $userData[$subField] ?? ''; + } + } elseif (str_contains($field, 'project_users')) { + // 填充项目经理信息列 + foreach (array_keys(self::PROJECT_USERS_SUB_COLUMNS) as $subField) { + $row[] = $projectUserData[$subField] ?? ''; + } + } elseif (str_contains($field, 'idcard')) { + $row[] = ' ' . $this->getDotValue($info, $field); + } elseif (str_contains($field, 'all_course')) { + $row[] = $this->allCourse($info); + } elseif (str_contains($field, 'partners')) { + $row[] = $this->partners($info); + } else { + $row[] = $this->getDotValue($info, $field); + } + } + return $row; + } + /** * .号转数组层级并返回对应的值 * @param $key @@ -293,49 +563,9 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With { $list = []; foreach ($data['project_users'] as $item) { - $list[] = $item['groupName'] . '-' . ($item['userName'] ?? '') . '-' . ($item['investDate'] ?? ''); + $list[] = $item['groupName'] . '-' . ($item['userName'] ?? '') . '-' . ($item['investDate'] ?? '') . '-' . ($item['amount'] ?? ''); } return implode("、\r\n", $list); } - /** - * 获取学员信息(优化显示格式) - * @param $data - */ - function getUsers($data) - { - if (empty($data['users'])) { - return ''; - } - - $list = []; - $index = 1; - foreach ($data['users'] as $item) { - // 基本信息行 - $userInfo = []; - $userInfo[] = "【学员{$index}】"; - $userInfo[] = "学号: " . ($item['no'] ?? '-'); - $userInfo[] = "姓名: " . ($item['username'] ?? '-'); - $userInfo[] = "校友: " . ($item['is_schoolmate_text'] ?? '-'); - $userInfo[] = "职位: " . ($item['company_position'] ?? '-'); - $userInfo[] = "手机: " . ($item['mobile'] ?? '-'); - - // 课程报名信息 - if (!empty($item['course_signs'])) { - $userInfo[] = "报名课程:"; - foreach ($item['course_signs'] as $signIndex => $sign) { - $courseName = $sign['course']['name'] ?? '-'; - $signDate = $sign['created_at'] ?? '-'; - $userInfo[] = " " . ($signIndex + 1) . ". {$courseName} ({$signDate})"; - } - } - - $list[] = implode("\n", $userInfo); - $index++; - } - - // 用分隔线分隔不同学员 - return implode("\n" . str_repeat("-", 40) . "\n", $list); - } - } diff --git a/app/Http/Controllers/Admin/CalendarsController.php b/app/Http/Controllers/Admin/CalendarsController.php index 1490dbf..ba19e6b 100644 --- a/app/Http/Controllers/Admin/CalendarsController.php +++ b/app/Http/Controllers/Admin/CalendarsController.php @@ -50,7 +50,7 @@ class CalendarsController extends BaseController public function index() { $all = \request()->all(); - $list = Calendar::with('course', 'courseContent','historyCourses') + $list = Calendar::with('course', 'courseContent', 'historyCourses') ->where(function ($query) use ($all) { if (isset($all['month'])) { $query->where('start_time', 'like', $all['month'] . '%'); @@ -61,7 +61,21 @@ class CalendarsController extends BaseController $list = $list->toArray(); return Excel::download(new CommonExport($list, $all['export_fields'] ?? ''), ($all['file_name'] ?? '') . date('YmdHis') . '.xlsx'); } - return $this->success($list); + // 本月日历天数 + $monthDayCalendar = Calendar::where(function ($query) use ($all) { + if (isset($all['month'])) { + $query->where('start_time', 'like', $all['month'] . '%'); + } + })->where('is_count_days', 1)->sum('days'); + // 本年日历天数 + $yearDayCalendar = Calendar::where(function ($query) use ($all) { + if (isset($all['month'])) { + // 获取$all['month']的年份部分 + $year = date('Y', strtotime($all['month'])); + $query->where('start_time', 'like', $year . '%'); + } + })->where('is_count_days', 1)->sum('days'); + return $this->success(compact('list', 'monthDayCalendar', 'yearDayCalendar')); } /** diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index f8ca8b9..df4617e 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -141,7 +141,7 @@ class OtherController extends CommonController // 重点上市公司 $list['cover_stock_total'] = CourseSign::shangshi(); // 本月课程 - $monthCourses = Calendar::with('course.teacher')->where('type', 1) + $monthCourses = Calendar::with('course.teacher') ->where('date', 'like', '%' . date('Y-m') . '%') ->get(); // 课程统计 @@ -222,7 +222,7 @@ class OtherController extends CommonController $course_type_id = $params['course_type_id']; $courses = $params['courses']; // 被投企业数 - $list['course_signs_invested'] = CourseSign::yhInvested($start_date, $end_date); + // $list['course_signs_invested'] = CourseSign::yhInvested($start_date, $end_date, $courses->pluck('id')->toArray()); // 报名人数 $list['course_signs_total'] = CourseSign::courseSignsTotal($start_date, $end_date, null, $courses->pluck('id')); // 审核通过人数 @@ -233,10 +233,14 @@ class OtherController extends CommonController $calendar = Calendar::where(function ($query) use ($start_date, $end_date) { $query->whereBetween('start_time', [$start_date, $end_date]) ->orWhereBetween('end_time', [$start_date, $end_date]); - })->whereIn('course_id', $courses->pluck('id')); + })->where(function ($query) use ($courses) { + if (request('course_type_id')) { + $query->whereIn('course_id', $courses->pluck('id')); + } + }); $list['course_total'] = (clone $calendar)->count(); // 开课天数 - $list['course_day_total'] = (clone $calendar)->sum('days'); + $list['course_day_total'] = (clone $calendar)->where('is_count_days', 1)->sum('days'); // 上市公司数(所有上市公司) $list['company_market_total'] = Company::companyMarket($start_date, $end_date); @@ -272,7 +276,7 @@ class OtherController extends CommonController foreach ($courseTypes as $courseType) { // 获取课程 $courses2 = Course::where('type', $courseType->id) - ->whereHas('course', function ($query) use ($start_date, $end_date) { + ->where(function ($query) use ($start_date, $end_date) { // 开始结束日期的筛选。or查询 if ($start_date && $end_date) { $query->whereBetween('start_date', [$start_date, $end_date]) @@ -288,11 +292,19 @@ class OtherController extends CommonController 'course_type_signs_pass_unique' => CourseSign::courseSignsTotalByUnique($start_date, $end_date, 1, $courses2->pluck('id'), null), 'course_name' => $course->name, 'course_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, [$course->id]), + // 跟班学员数量 + 'genban_total' => CourseSign::genban($start_date, $end_date, [$course->id]), + // 被投企业数 + 'yh_invested_total' => CourseSign::yhInvested($start_date, $end_date, [$course->id]), + // 元禾同事数 + 'company_join_total' => CourseSign::companyJoin($start_date, $end_date, [$course->id]), ]; } } // 附加历史课程数据 - $historyCourses = HistoryCourse::where(function ($query) use ($start_date, $end_date) { + $historyCourses = HistoryCourse::whereHas('calendar', function ($query) { + $query->where('is_count_people', 1); + })->where(function ($query) use ($start_date, $end_date) { // 开始结束日期的筛选。or查询 $query->whereBetween('start_time', [$start_date, $end_date]) ->orWhereBetween('end_time', [$start_date, $end_date]); @@ -353,7 +365,7 @@ class OtherController extends CommonController switch ($export_type) { case 'course_signs_invested': // 被投企业明细 - 使用与coursesHome相同的算法 - $companies = CourseSign::yhInvested($start_date, $end_date, true); + $companies = CourseSign::yhInvested($start_date, $end_date, $course_ids, true); foreach ($companies as $company) { $data[] = [ 'company_name' => $company->company_name, @@ -493,6 +505,9 @@ class OtherController extends CommonController 'course_type_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, $courses2->pluck('id')), 'course_type_signs_pass_unique' => CourseSign::courseSignsTotalByUnique($start_date, $end_date, 1, $courses2->pluck('id'), null), 'course_signs_pass' => CourseSign::courseSignsTotal($start_date, $end_date, 1, [$course->id]), + 'genban_total' => CourseSign::genban($start_date, $end_date, [$course->id]), + 'yh_invested_total' => CourseSign::yhInvested($start_date, $end_date, [$course->id]), + 'company_join_total' => CourseSign::companyJoin($start_date, $end_date, [$course->id]), ]; } } @@ -502,6 +517,9 @@ class OtherController extends CommonController 'course_type_signs_pass' => '课程体系培养人数', 'course_type_signs_pass_unique' => '课程体系去重培养人数', 'course_signs_pass' => '课程培养人数', + 'genban_total' => '跟班学员数', + 'yh_invested_total' => '被投企业数', + 'company_join_total' => '元禾同事数', ]; $filename = '课程分类明细'; break; @@ -573,6 +591,7 @@ class OtherController extends CommonController 'mobile' => $user->mobile ?? '', 'company_name' => $user->company_name ?? '', 'company_position' => $user->company_position ?? '', + 'from' => $user->from ?? '', ]; } $fields = [ @@ -581,6 +600,7 @@ class OtherController extends CommonController 'mobile' => '手机号', 'company_name' => '企业名称', 'company_position' => '职位', + 'from' => '标签', ]; $filename = '跟班学员明细'; break; @@ -677,8 +697,12 @@ class OtherController extends CommonController case 'course_total': // 开课场次明细 - 与coursesHome算法一致 - $calendars = Calendar::whereIn('course_id', $course_ids) - ->whereBetween('date', [$start_date, $end_date]) + $calendars = Calendar::whereBetween('date', [$start_date, $end_date]) + ->where(function ($query) use ($course_ids) { + if (request('course_type_id')) { + $query->whereIn('course_id', $course_ids); + } + }) ->with('course') ->get(); @@ -705,8 +729,12 @@ class OtherController extends CommonController case 'course_day_total': // 开课天数明细 - 与coursesHome算法一致 - $calendars = Calendar::whereIn('course_id', $course_ids) - ->whereBetween('date', [$start_date, $end_date]) + $calendars = Calendar::whereBetween('date', [$start_date, $end_date]) + ->where(function ($query) use ($course_ids) { + if (request('course_type_id')) { + $query->whereIn('course_id', $course_ids); + } + })->where('is_count_days', 1) ->with('course') ->get(); diff --git a/app/Models/CourseSign.php b/app/Models/CourseSign.php index 3938c4b..0f266ec 100755 --- a/app/Models/CourseSign.php +++ b/app/Models/CourseSign.php @@ -110,7 +110,9 @@ class CourseSign extends SoftDeletesModel // 基础数据 $baseTotal = $totalQuery->count(); // 历史数据 - $historyTotal = HistoryCourse::where(function ($query) use ($start_date, $end_date) { + $historyTotal = HistoryCourse::whereHas('calendar', function ($query) { + $query->where('is_count_people', 1); + })->where(function ($query) use ($start_date, $end_date) { // 开始结束日期的筛选。or查询 $query->whereBetween('start_time', [$start_date, $end_date]) ->orWhereBetween('end_time', [$start_date, $end_date]); @@ -133,7 +135,9 @@ class CourseSign extends SoftDeletesModel } else { $baseTotal = $user->count(); // 历史数据 - $historyTotal = HistoryCourse::where(function ($query) use ($start_date, $end_date) { + $historyTotal = HistoryCourse::whereHas('calendar', function ($query) { + $query->where('is_count_people', 1); + })->where(function ($query) use ($start_date, $end_date) { // 开始结束日期的筛选。or查询 $query->whereBetween('start_time', [$start_date, $end_date]) ->orWhereBetween('end_time', [$start_date, $end_date]); @@ -144,20 +148,57 @@ class CourseSign extends SoftDeletesModel } /** - * 指定时间内的被投企业 + * 被投企业统计 + * @param string|null $start_date 开始日期 + * @param string|null $end_date 结束日期 + * @param array|null $course_ids 课程ID(仅在自定义时间时生效) + * @param bool $retList 是否返回列表 */ - public static function yhInvested($start_date = null, $end_date = null, $retList = false) + public static function yhInvested($start_date = null, $end_date = null, $course_ids = null, $retList = false) { - $courseSignByTypeQuery = self::getStudentList($start_date, $end_date, 1, null); - $list = Company::whereHas('users', function ($query) use ($courseSignByTypeQuery) { - $query->whereIn('id', $courseSignByTypeQuery->get()->pluck('user_id')); + // 判断是否使用默认时间 + $isDefaultDate = empty($start_date) || $start_date == CourseType::START_DATE || empty($course_ids); + dd(empty($start_date) , $start_date == CourseType::START_DATE , empty($course_ids)); + // 获取学员ID列表 + if ($isDefaultDate) { + // 默认时间:获取所有学员,不限制课程 + $userIds = self::getStudentList($start_date, $end_date, 1, null)->get()->pluck('user_id'); + } else { + // 自定义时间:按课程筛选学员 + $userIds = self::getStudentList($start_date, $end_date, 1, $course_ids)->get()->pluck('user_id'); + } + + // 获取这些学员所在的被投企业 + $companies = Company::whereHas('users', function ($query) use ($userIds) { + $query->whereIn('id', $userIds); })->where('is_yh_invested', 1)->get(); + + // 自定义时间:需要按被投时间筛选 + if (!$isDefaultDate) { + $startDate = substr($start_date, 0, 10); + $endDate = substr($end_date, 0, 10); + + // 筛选出被投时间在范围内的企业 + $filteredCompanies = []; + foreach ($companies as $company) { + $projectUsers = $company->project_users ?? []; + foreach ($projectUsers as $item) { + $investDate = $item['investDate'] ?? null; + // 检查被投时间是否在范围内 + if ($investDate && $investDate >= $startDate && $investDate <= $endDate) { + $filteredCompanies[] = $company; + break; // 只要有一条满足就加入 + } + } + } + $companies = collect($filteredCompanies); + } + + // 返回结果 if ($retList) { - // 返回列表 - return $list; + return $companies->values(); } else { - // 返回统计数据 - return $list->count(); + return $companies->count(); } } @@ -173,7 +214,7 @@ class CourseSign extends SoftDeletesModel { $courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids); $courseSigns = $courseSignsQuery->whereHas('user', function ($query) { - $query->where('from', '%' . '跟班学员' . '%'); + $query->where('from', 'like', '%跟班学员%'); })->get(); if ($retList) { @@ -337,10 +378,21 @@ class CourseSign extends SoftDeletesModel // 检测关键词 $companyNameKeyword = [ - '元禾控股', '元禾原点', '元禾厚望', '元禾重元', - '元禾璞华', '元禾谷风', '元禾绿柳', '元禾辰坤', '元禾沙湖', - '禾裕集团', '苏州科服', '信诚管理咨询', - '集成电路公司', '常州团队', '国企元禾' + '元禾控股', + '元禾原点', + '元禾厚望', + '元禾重元', + '元禾璞华', + '元禾谷风', + '元禾绿柳', + '元禾辰坤', + '元禾沙湖', + '禾裕集团', + '苏州科服', + '信诚管理咨询', + '集成电路公司', + '常州团队', + '国企元禾' ]; $company = Company::where(function ($query) use ($companyNameKeyword) { foreach ($companyNameKeyword as $item) { @@ -372,7 +424,7 @@ class CourseSign extends SoftDeletesModel { $courseSignsQuery = self::getStudentList($start_date, $end_date, 1, $course_ids); $courseSigns = $courseSignsQuery->whereHas('user', function ($query) { - $query->where('from', '%' . '跟班学员' . '%'); + $query->where('from', 'like', '%跟班学员%'); })->get(); if ($retList) { return User::with('company')->whereIn('id', $courseSigns->pluck('user_id'))->get(); diff --git a/database/migrations/2025_11_26_100100_add_count_fields_to_calendars_table.php b/database/migrations/2025_11_26_100100_add_count_fields_to_calendars_table.php index fe1df14..9a537ab 100644 --- a/database/migrations/2025_11_26_100100_add_count_fields_to_calendars_table.php +++ b/database/migrations/2025_11_26_100100_add_count_fields_to_calendars_table.php @@ -13,8 +13,8 @@ return new class extends Migration { public function up() { Schema::table('calendars', function (Blueprint $table) { - $table->tinyInteger('is_count_days')->default(1)->comment('是否统计天数 0否 1是'); - $table->tinyInteger('is_count_people')->default(1)->comment('是否统计人数 0否 1是'); + $table->tinyInteger('is_count_days')->nullable()->comment('是否统计天数 0否 1是'); + $table->tinyInteger('is_count_people')->nullable()->comment('是否统计人数 0否 1是'); }); }