diff --git a/app/Exports/CommonExport.php b/app/Exports/CommonExport.php index 4367da7..8b1f56b 100755 --- a/app/Exports/CommonExport.php +++ b/app/Exports/CommonExport.php @@ -20,7 +20,7 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With public $fields; public $data; public $hasUsersField = false; - public $usersColumnStartIndex = null; + public $hasProjectUsersField = false; public $totalColumns = 0; public $totalRows = 0; public $expandedFields = []; @@ -37,6 +37,14 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With 'user_sign_date' => '报名时间', ]; + // 项目经理信息子列定义 + const PROJECT_USERS_SUB_COLUMNS = [ + 'pm_platform' => '管理平台', + 'pm_name' => '项目经理', + 'pm_invest_date' => '首次出资时间', + 'pm_amount' => '投资金额', + ]; + public function __construct($data, $exportFields) { // 需要导出的字段。格式:['name'=>'名字','user.sex'=>'性别'] @@ -49,7 +57,7 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With } /** - * 构建扩展后的字段列表(将users字段展开成多列) + * 构建扩展后的字段列表(将users和project_users字段展开成多列) */ private function buildExpandedFields() { @@ -59,14 +67,20 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With $index = 1; foreach ($this->fields as $field => $label) { - if (str_contains($field, 'users')) { + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { $this->hasUsersField = true; - $this->usersColumnStartIndex = $index; // 展开学员信息为多列 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++; @@ -75,6 +89,14 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With $this->totalColumns = count($this->expandedFields); } + /** + * 是否需要二级表头 + */ + private function needsDoubleHeader() + { + return $this->hasUsersField || $this->hasProjectUsersField; + } + /** * 获取列字母 */ @@ -100,11 +122,13 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With $letter = $this->getColumnLetter($index); if (in_array($field, ['user_course', 'user_name'])) { $widths[$letter] = 25; - } elseif (in_array($field, ['user_mobile', 'user_sign_date'])) { + } 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 (str_contains($field, 'partners') || str_contains($field, 'project_users')) { + } 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; @@ -124,11 +148,11 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With public function styles(Worksheet $sheet): array { $lastCol = $this->getColumnLetter($this->totalColumns); - $dataStartRow = $this->hasUsersField ? 3 : 2; + $dataStartRow = $this->needsDoubleHeader() ? 3 : 2; $styles = []; - if ($this->hasUsersField) { + if ($this->needsDoubleHeader()) { // 二级表头样式 - 第一行 $styles[1] = [ 'font' => ['bold' => true, 'size' => 12], @@ -194,7 +218,7 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With AfterSheet::class => function (AfterSheet $event) { $sheet = $event->sheet->getDelegate(); - if ($this->hasUsersField) { + if ($this->needsDoubleHeader()) { // 处理二级表头的单元格合并 $this->mergeHeaderCells($sheet); @@ -210,7 +234,7 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With } // 设置数据行高 - $dataStartRow = $this->hasUsersField ? 3 : 2; + $dataStartRow = $this->needsDoubleHeader() ? 3 : 2; for ($row = $dataStartRow; $row <= $this->totalRows + $dataStartRow - 1; $row++) { $sheet->getRowDimension($row)->setRowHeight(22); } @@ -225,17 +249,22 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With { $index = 1; foreach ($this->fields as $field => $label) { - if (str_contains($field, 'users')) { + 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); @@ -260,16 +289,19 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With $newList = []; - if ($this->hasUsersField) { - // 有学员字段:创建二级表头 + if ($this->needsDoubleHeader()) { + // 有需要展开的字段:创建二级表头 // 第一行表头(主表头)- 在 mergeHeaderCells 中处理 $header1 = []; foreach ($this->fields as $field => $label) { - if (str_contains($field, 'users')) { - // 学员信息占用多列,第一行只需要占位 + if (str_contains($field, 'users') && !str_contains($field, 'project_users')) { foreach (self::USERS_SUB_COLUMNS as $subLabel) { $header1[] = ''; } + } elseif (str_contains($field, 'project_users')) { + foreach (self::PROJECT_USERS_SUB_COLUMNS as $subLabel) { + $header1[] = ''; + } } else { $header1[] = ''; } @@ -279,33 +311,36 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With // 第二行表头(子表头) $header2 = []; foreach ($this->fields as $field => $label) { - if (str_contains($field, 'users')) { + 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) { - $usersData = $this->getUsersExpanded($info); - if (empty($usersData)) { - // 没有学员数据,输出一行空数据 - $row = $this->buildRowWithoutUsers($info, []); + $expandedRows = $this->getExpandedRows($info); + if (empty($expandedRows)) { + // 没有展开数据,输出一行空数据 + $row = $this->buildExpandedRow($info, [], []); $newList[] = $row; } else { - // 每个学员记录一行 - foreach ($usersData as $userData) { - $row = $this->buildRowWithoutUsers($info, $userData); + 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)) { @@ -326,8 +361,6 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With $temp[$field] = $this->allCourse($info); } elseif (str_contains($field, 'partners')) { $temp[$field] = $this->partners($info); - } elseif (str_contains($field, 'project_users')) { - $temp[$field] = $this->projectManager($info); } else { $temp[$field] = $this->getDotValue($info, $field); } @@ -350,10 +383,53 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With } } - $this->totalRows = count($newList) - ($this->hasUsersField ? 2 : 1); + $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; + } + /** * 获取展开的学员数据(每个学员每条课程一行) */ @@ -397,25 +473,28 @@ class CommonExport implements FromCollection, WithStyles, WithColumnWidths, With } /** - * 构建包含学员数据的行 + * 构建包含展开数据的行 */ - private function buildRowWithoutUsers($info, $userData) + private function buildExpandedRow($info, $userData, $projectUserData) { $row = []; foreach ($this->fields as $field => $label) { - if (str_contains($field, 'users')) { + 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); - } elseif (str_contains($field, 'project_users')) { - $row[] = $this->projectManager($info); } else { $row[] = $this->getDotValue($info, $field); } @@ -484,7 +563,7 @@ 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); } diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index b14c587..c2dc2b3 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -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,7 +233,11 @@ 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)->where('is_count_days', 1)->sum('days'); @@ -288,6 +292,12 @@ 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]), ]; } } @@ -355,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, @@ -495,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]), ]; } } @@ -504,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; @@ -679,8 +695,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(); @@ -707,8 +727,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 e7178c7..16856bf 100755 --- a/app/Models/CourseSign.php +++ b/app/Models/CourseSign.php @@ -148,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; + + // 获取学员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(); } } @@ -341,10 +378,21 @@ class CourseSign extends SoftDeletesModel // 检测关键词 $companyNameKeyword = [ - '元禾控股', '元禾原点', '元禾厚望', '元禾重元', - '元禾璞华', '元禾谷风', '元禾绿柳', '元禾辰坤', '元禾沙湖', - '禾裕集团', '苏州科服', '信诚管理咨询', - '集成电路公司', '常州团队', '国企元禾' + '元禾控股', + '元禾原点', + '元禾厚望', + '元禾重元', + '元禾璞华', + '元禾谷风', + '元禾绿柳', + '元禾辰坤', + '元禾沙湖', + '禾裕集团', + '苏州科服', + '信诚管理咨询', + '集成电路公司', + '常州团队', + '国企元禾' ]; $company = Company::where(function ($query) use ($companyNameKeyword) { foreach ($companyNameKeyword as $item) {