cqp 2 hónapja
szülő
commit
34b4c0fd9d

+ 9 - 4
app/Exports/MultiSheetExport.php

@@ -8,20 +8,25 @@ use Maatwebsite\Excel\Concerns\WithMultipleSheets;
 class MultiSheetExport implements WithMultipleSheets
 {
     protected $data;
+    protected $type;
+    protected $column;
+    protected $timeRow;
 
-    public function __construct(array $data)
+    public function __construct(array $data, int $type = 1, array $column, array $timeRow)
     {
         $this->data = $data;
+        $this->type = $type;
+        $this->column = $column;
+        $this->timeRow = $timeRow;
     }
 
     public function sheets(): array
     {
         $sheets = [];
-
         foreach ($this->data as $sheetName => $sheetData) {
-            $sheets[] = new SingleSheetExport($sheetData, $sheetName);
+            // 将 type 传给 SingleSheetExport
+            $sheets[] = new SingleSheetExport($sheetData, $sheetName, $this->type, $this->column, $this->timeRow);
         }
-
         return $sheets;
     }
 }

+ 158 - 34
app/Exports/SingleSheetExport.php

@@ -7,6 +7,9 @@ use Maatwebsite\Excel\Concerns\FromCollection;
 use Maatwebsite\Excel\Concerns\WithTitle;
 use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
 use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithCustomStartCell;
+use Maatwebsite\Excel\Events\AfterSheet;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
@@ -14,87 +17,208 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use PhpOffice\PhpSpreadsheet\Style\Alignment;
 use PhpOffice\PhpSpreadsheet\Style\Border;
 
-class SingleSheetExport extends DefaultValueBinder implements FromCollection, WithTitle, WithCustomValueBinder, WithStyles
+class SingleSheetExport extends DefaultValueBinder implements
+    FromCollection,
+    WithTitle,
+    WithCustomValueBinder,
+    WithStyles,
+    WithEvents,
+    WithCustomStartCell
 {
     protected $data;
     protected $sheetName;
+    protected $type; // 1: 研发人员, 2: 研发设备
+    protected $columns;
+    protected $timeRow;
 
-    public function __construct(array $data, string $sheetName)
+    public function __construct(array $data, string $sheetName, int $type = 1, array $columns, array $timeRow)
     {
         $this->data = $data;
         $this->sheetName = $sheetName;
+        $this->type = $type;
+        $this->columns = $columns;
+        $this->timeRow = $timeRow;
     }
 
     /**
-     * 数据集合
+     * 设置数据开始写入的起始单元格
+     * 因为 A1-A4 被标题占用,数据从 A5 开始
      */
+    public function startCell(): string
+    {
+        return 'A5';
+    }
+
     public function collection()
     {
         return new Collection($this->data);
     }
 
-    /**
-     * Sheet 名称
-     */
     public function title(): string
     {
         return $this->sheetName;
     }
 
-    /**
-     * 自定义单元格绑定(防止数字丢失前导零 / 科学计数法)
-     */
     public function bindValue(Cell $cell, $value)
     {
-        // 如果是纯数字,则以字符串形式写入
-        if (is_numeric($value)) {
+        if (is_numeric($value) && strlen($value) > 10) {
             $cell->setValueExplicit($value, DataType::TYPE_STRING);
             return true;
         }
-
         return parent::bindValue($cell, $value);
     }
 
+    /**
+     * 处理合并单元格和固定文字
+     */
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function(AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+                $sheet->freezePane('A6');
+                $highestColumn = $sheet->getHighestColumn();
+                $highestRow = $sheet->getHighestRow();
+
+                // 1. 设置下拉筛选 (Auto Filter)
+                // 范围从 A5 开始到最后一列的最后一行
+                $sheet->setAutoFilter('A5:' . $highestColumn . $highestRow);
+
+                // --- 原有的配置定义 ---
+                $config = [
+                    1 => [
+                        'title' => '研发人员工时统计单',
+                        'dept'  => '部门:研发部',
+                        'desc'  => '现就本月研发人员的工时统计数据如下:',
+                    ],
+                    2 => [
+                        'title' => '仪器设备运行工时统计单',
+                        'dept'  => '部门:研发部、生产部',
+                        'desc'  => '现就本月研发设备的工时统计数据如下:',
+                    ]
+                ];
+                $c = $config[$this->type] ?? $config[1];
+
+                // --- 第一行:主标题 ---
+                $sheet->mergeCells('A1:W1');
+                $sheet->setCellValue('A1', $c['title']);
+                $sheet->getStyle('A1')->getFont()->setSize(12)->setBold(false)->setName('宋体');
+                $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+
+                // --- 第二行:部门 ---
+                $sheet->setCellValue('A2', $c['dept']);
+                $sheet->getStyle('A2')->getFont()->setName('宋体');
+
+                // --- 第三行:描述语 + Sheet名 ---
+                $sheet->mergeCells('A3:U3');
+                $sheet->setCellValue('A3', $c['desc']);
+                $sheet->getStyle('A3')->getFont()->setName('宋体');
+
+                $sheet->mergeCells('W3:Y3');
+                $sheet_name = str_replace('合计','',$this->sheetName);
+                $sheet->setCellValue('W3', $sheet_name);
+                $sheet->getStyle('W3')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+                $sheet->getStyle('W3')->getFont()->setName('宋体');
+
+                foreach ($this->columns as $index => $colKey) {
+                    if (isset($this->timeRow[$colKey]) && $this->timeRow[$colKey] !== '') {
+                        // 根据索引获取列字母 (0->A, 1->B...)
+                        $colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($index + 1);
+
+                        // 写入 A4, B4...
+                        $sheet->setCellValue($colLetter . '4', $this->timeRow[$colKey]);
+
+                        // 样式:小字、居中、无边框(或根据你喜好)
+                        $sheet->getStyle($colLetter . '4')->applyFromArray([
+                            'font' => ['size' => 9, 'name' => 'Times New Roman'],
+                            'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
+                        ]);
+                    }
+                }
+
+                // 也可以给第 4 行设置一个较窄的行高,让它看起来更像表头附件
+                $sheet->getRowDimension(4)->setRowHeight(15);
+            },
+        ];
+    }
+
     public function styles(Worksheet $sheet)
     {
         $highestRow = $sheet->getHighestRow();
         $highestColumn = $sheet->getHighestColumn();
-        $fullRange = 'A1:' . $highestColumn . $highestRow;
-
-        // 1. 设置全表(表头+表体)统一样式
-        $sheet->getStyle($fullRange)->applyFromArray([
-            'font' => [
-                'name' => '宋体',
-                'size' => 12,
-                'bold' => false,
-            ],
+
+        // 样式应用范围从 A5(表头)开始到最后
+        $dataRange = 'A5:' . $highestColumn . $highestRow;
+
+        // 全表基础样式(针对数据区)
+        $sheet->getStyle($dataRange)->applyFromArray([
+            'font' => ['name' => '宋体', 'size' => 12],
             'alignment' => [
                 'vertical' => Alignment::VERTICAL_CENTER,
-                'horizontal' => Alignment::HORIZONTAL_CENTER, // 设置全表水平居中
+                'horizontal' => Alignment::HORIZONTAL_CENTER,
             ],
             'borders' => [
                 'allBorders' => [
-                    'borderStyle' => Border::BORDER_THIN, // 给所有单元格加上细边框(可选)
-                    'color' => ['argb' => '000000'],
+                    'borderStyle' => Border::BORDER_THIN,
                 ],
             ],
         ]);
 
-        // 2. 针对表头(第一行)的特殊处理
-        // 如果全表已经居中且不加粗,这里主要用于确保表头有别于表体的样式(例如设置背景色或更粗的底边框)
-        $sheet->getStyle('A1:' . $highestColumn . '1')->applyFromArray([
+        // 表头(第五行)特殊处理
+        $sheet->getStyle('A5:' . $highestColumn . '5')->applyFromArray([
             'borders' => [
-                'bottom' => [
-                    'borderStyle' => Border::BORDER_MEDIUM, // 表头底部稍微加粗
-                ],
+                'bottom' => ['borderStyle' => Border::BORDER_MEDIUM],
             ],
         ]);
 
-        // 3. 自动列宽
-        foreach ($sheet->getColumnIterator() as $column) {
-            $sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
+        // 3. 根据 type 类型定制列宽
+        if ($this->type === 1) {
+            // --- 研发人员格式设置 ---
+            $this->setEmployeeWidths($sheet);
+        } else {
+            // --- 研发设备格式设置 ---
+            $this->setDeviceWidths($sheet);
         }
 
         return [];
     }
-}
+
+    /**
+     * 研发人员列宽定制
+     */
+    private function setEmployeeWidths(Worksheet $sheet)
+    {
+        foreach ($sheet->getColumnIterator() as $column) {
+            $col = $column->getColumnIndex();
+            $sheet->getColumnDimension($col)->setAutoSize(false);
+
+            switch ($col) {
+                case 'A': $sheet->getColumnDimension($col)->setWidth(6); break;  // 序号
+                case 'B': $sheet->getColumnDimension($col)->setWidth(12); break; // 姓名
+                case 'E': $sheet->getColumnDimension($col)->setWidth(25); break; // RD编号/项目名(通常较长)
+                case 'F':
+                case 'G':
+                case 'H': $sheet->getColumnDimension($col)->setWidth(10); break; // 各种比例 %
+                default:  $sheet->getColumnDimension($col)->setWidth(12); break; // 动态RD列
+            }
+        }
+    }
+
+    /**
+     * 研发设备列宽定制
+     */
+    private function setDeviceWidths(Worksheet $sheet)
+    {
+        foreach ($sheet->getColumnIterator() as $column) {
+            $col = $column->getColumnIndex();
+            $sheet->getColumnDimension($col)->setAutoSize(false);
+
+            switch ($col) {
+                case 'A': $sheet->getColumnDimension($col)->setWidth(6); break;  // 序号
+                case 'B': $sheet->getColumnDimension($col)->setWidth(20); break; // 设备名称(通常比人名长)
+                case 'C': $sheet->getColumnDimension($col)->setWidth(15); break; // 型号/编号
+                default:  $sheet->getColumnDimension($col)->setWidth(12); break;
+            }
+        }
+    }
+}

+ 156 - 21
app/Service/ExportFileService.php

@@ -4,6 +4,11 @@ namespace App\Service;
 
 use App\Exports\ExportOrder;
 use App\Exports\MultiSheetExport;
+use App\Model\CalendarDetails;
+use App\Model\Device;
+use App\Model\Employee;
+use App\Model\EmployeeRole;
+use App\Model\Item;
 use App\Model\RevenueCost;
 use Maatwebsite\Excel\Facades\Excel;
 
@@ -17,12 +22,25 @@ class ExportFileService extends Service
         1
     ];
 
+    public $special_func = [
+        0 => 'five',
+        1 => 'six',
+    ];
+
     public function exportAll($data,$user){
         if(empty($data['menu_id'])) return [false, '菜单ID不能为空'];
         list($function, $name) = EmployeeService::fillMenu2($data['menu_id'], $user);
         if (empty($function) || ! method_exists(self::class, $function)) return [false, "导出方法不存在,请联系开发"];
         self::$filename = $name;
 
+        //特殊的导出
+        if(in_array($function, $this->special_func)){
+            list($status, $return) = $this->$function($data,$user);
+            if(! $status) return [false, $return];
+
+            return [true, $return];
+        }
+
         $export_type = $data['export_type'] ?? 0;
         if(! isset(self::$export_type[$export_type])) return [false,'导出文件方式错误或者不存在'];
         if(empty($export_type)){
@@ -167,42 +185,159 @@ class ExportFileService extends Service
         return [true, $this->saveExportData($return,$header)];
     }
 
-    public function five($ergs,$user){
-        // 导出数据
-        $return = [];
+    public function five($ergs, $user) {
         $header_default = $user['e_header_default'];
-        $column = array_column($header_default,'key');
-        $header = array_column($header_default,'value');
+        $column = array_column($header_default, 'key'); // 对应数据的键名
+        $header = array_column($header_default, 'value'); // Excel 展现的中文表头
+
+        array_unshift($column, 'index_no'); // 内部识别用的 key
+        array_unshift($header, '序号');     // Excel 显示的表头
 
         $service = new StatisticsService();
-        list($status, $result) = $service->statisticsEmployeeCommon($ergs, $user);
-        if(! $status) return [false, $result];
+        list($status, $res) = $service->statisticsEmployeeExportCommon($ergs, $user);
+        if (!$status) return [false, $res];
+        list($model, $timeRange, $totalWorkDays) = $res;
+
+        $rawData = $model->get()->toArray();
+
+        // 获取员工名单(避免在循环里重复查询)
+        $employee_id = EmployeeRole::where('del_time', 0)
+            ->where('role_id', 87)
+            ->pluck('employee_id')
+            ->toArray();
+        $employee = Employee::where('del_time', 0)
+            ->whereIn('id', $employee_id)
+            ->where('id', '<>', Employee::SPECIAL_ADMIN)
+            ->select('id', 'emp_name as name')
+            ->get()->toArray();
+        $item_total = Item::where('del_time', 0)->select('code', 'id','start_time','end_time')->get()->toArray();
+
+        // 生成起止时间行
+        $timeRow = $this->getItemTimeRow($column, $item_total);
+
+        // 最终交给 MultiSheetExport 的数组
+        $finalExportData = [];
+
+        $month = date("Y.m", $timeRange[0]);
+        // --- 1. 处理全月汇总 Sheet ---
+        $summaryData = $service->statisticsEmployeeFillExportData($rawData, $timeRange, $totalWorkDays, $employee,$item_total);
+        // 格式化数据:只取 header 中定义的列,并按顺序排列
+        $finalExportData[$month . '合计'] = $this->formatSheetData($header, $column, $summaryData);
+
+        // --- 2. 处理每日明细 Sheets ---
+        $dataByDay = [];
+        foreach ($rawData as $row) {
+            $dateKey = date('Y-m-d', $row['order_time']);
+            $dataByDay[$dateKey][] = $row;
+        }
 
-        list($model, $search, $day) = $result;
-        $list = $model->get()->toArray();
-        $list = $service->statisticsEmployeeFillData($list, $search, $day);
-        $this->fillData($list, $column, $return);
+        foreach ($dataByDay as $date => $dayRows) {
+            $dayTimestamp = strtotime($date);
+            // 这里调用你的填充逻辑
+            $dayResult = $service->statisticsEmployeeFillExportData($dayRows, [$dayTimestamp], 1, $employee, $item_total);
+            // 格式化并存入结果集
+            $finalExportData[date('Y.m.d',$dayTimestamp) . '合计'] = $this->formatSheetData($header, $column, $dayResult);
+        }
 
-        return [true, $this->saveExportData($return,$header)];
+        // 3. 调用保存方法
+        return [true, $this->saveExportData2($finalExportData,2,$column,$timeRow)];
     }
 
     public function six($ergs,$user){
         // 导出数据
-        $return = [];
         $header_default = $user['e_header_default'];
         $column = array_column($header_default,'key');
         $header = array_column($header_default,'value');
 
+        array_unshift($column, 'index_no'); // 内部识别用的 key
+        array_unshift($header, '序号');     // Excel 显示的表头
+
         $service = new StatisticsService();
-        list($status, $result) = $service->statisticsDeviceCommon($ergs, $user);
+        list($status, $result) = $service->statisticsDeviceExportCommon($ergs, $user);
         if(! $status) return [false, $result];
+        list($model, $timeRange) = $result;
+
+        $rawData = $model->get()->toArray();
+
+        $device = Device::where('del_time',0)
+            ->where('is_use',Device::type_one)
+            ->select('id','title as name','code','type','type_2','power','in_time','number')
+            ->get()->toArray();
 
-        list($model, $search) = $result;
-        $list = $model->get()->toArray();
-        $list = $service->statisticsDeviceFillData($list, $search);
-        $this->fillData($list, $column, $return);
+        $item_total = Item::where('del_time', 0)->where('is_use', 1)->select('code', 'id','start_time','end_time')->get()->toArray();
 
-        return [true , $this->saveExportData($return,$header)];
+        // 生成起止时间行
+        $timeRow = $this->getItemTimeRow($column, $item_total);
+
+        // 最终交给 MultiSheetExport 的数组
+        $finalExportData = [];
+
+        $month = date("Y.m", $timeRange[0]);
+        // --- 1. 处理全月汇总 Sheet ---
+        $summaryData = $service->statisticsDeviceFillExportData($rawData, $timeRange, $device, $item_total);
+        // 格式化数据:只取 header 中定义的列,并按顺序排列
+        $finalExportData[$month . '合计'] = $this->formatSheetData($header, $column, $summaryData);
+
+        // --- 2. 处理每日明细 Sheets ---
+        $dataByDay = [];
+        foreach ($rawData as $row) {
+            $dateKey = date('Y-m-d', $row['order_time']);
+            $dataByDay[$dateKey][] = $row;
+        }
+
+        foreach ($dataByDay as $date => $dayRows) {
+            $dayTimestamp = strtotime($date);
+            // 这里调用你的填充逻辑
+            $dayResult = $service->statisticsDeviceFillExportData($dayRows, [$dayTimestamp], $device, $item_total);
+            // 格式化并存入结果集
+            $finalExportData[date('Y.m.d',$dayTimestamp) . '合计'] = $this->formatSheetData($header, $column, $dayResult);
+        }
+
+        // 3. 调用保存方法
+        return [true, $this->saveExportData2($finalExportData, 2,$column,$timeRow)];
+    }
+
+    private function getItemTimeRow($column, $item_total) {
+        $item_time_map = [];
+        foreach ($item_total as $it) {
+            $start = !empty($it['start_time']) ? date('Y.m', (int)$it['start_time']) : '';
+            $end = !empty($it['end_time']) ? date('Y.m', (int)$it['end_time']) : '';
+            $item_time_map[$it['id']] = ($start || $end) ? "{$start}-{$end}" : '';
+        }
+
+        $timeRow = [];
+        foreach ($column as $key) {
+            if (strpos($key, 'extra_') === 0) {
+                $itemId = str_replace('extra_', '', $key);
+                $timeRow[$key] = $item_time_map[$itemId] ?? '';
+            } else {
+                $timeRow[$key] = ''; // 其他字段(序号、设备名等)全为空
+            }
+        }
+        return $timeRow;
+    }
+
+    /**
+     * 辅助方法:将数据按表头顺序对齐,并把表头插入到第一行
+     */
+    private function formatSheetData($header, $column, $dataRows) {
+        $sheet = [];
+        $sheet[] = $header; // 第一行放入表头文字
+
+        $count = 1; // 初始化序号计数器
+        foreach ($dataRows as $row) {
+            $temp = [];
+            foreach ($column as $key) {
+                if ($key === 'index_no') {
+                    $temp[] = $count; // 填充序号
+                } else {
+                    $temp[] = $row[$key] ?? '';
+                }
+            }
+            $sheet[] = $temp;
+            $count++; // 序号自增
+        }
+        return $sheet;
     }
 
     public function saveExportData($data, $headers, $type = 'default',$file_name = ''){
@@ -212,10 +347,10 @@ class ExportFileService extends Service
         return $filename;
     }
 
-    public function saveExportData2($data,$file_name = ''){
+    public function saveExportData2($data,$type = 1,$column,$timeRow, $file_name = ''){
         if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
         $filename =  $file_name . '.' . 'xlsx';
-        \Maatwebsite\Excel\Facades\Excel::store(new MultiSheetExport($data),"/public/export/{$filename}", null, 'Xlsx', []);
+        \Maatwebsite\Excel\Facades\Excel::store(new MultiSheetExport($data, $type,$column,$timeRow),"/public/export/{$filename}", null, 'Xlsx', []);
 
         return $filename;
     }

+ 0 - 3
app/Service/RdGenerateService.php

@@ -15,9 +15,6 @@ class RdGenerateService extends Service
         }
 
         try {
-            // 设置脚本执行时间,防止大数据量时 PHP 超时
-            set_time_limit(300);
-
             DB::transaction(function () use ($data) {
                 $this->doGenerate($data['month']);
             });

+ 189 - 5
app/Service/StatisticsService.php

@@ -10,6 +10,7 @@ use App\Model\EmployeeDetails;
 use App\Model\EmployeeRole;
 use App\Model\Item;
 use App\Model\RdDetails;
+use Carbon\Carbon;
 
 class StatisticsService extends Service
 {
@@ -35,7 +36,7 @@ class StatisticsService extends Service
             ->where('b.order_time', '<=', $end_time)
             ->where('a.type', RdDetails::type_one)
             ->select('b.item_id','b.total_hours','a.data_id')
-            ->orderby('order_time', 'desc');
+            ->orderby('b.order_time', 'desc');
 
         if(! empty($data['item_code'])) {
             $item_id = Item::where('code', 'LIKE', '%'.$data['item_code'].'%')
@@ -191,7 +192,7 @@ class StatisticsService extends Service
             ->where('b.order_time', '<=', $end_time)
             ->where('a.type', RdDetails::type_two)
             ->select('b.item_id','b.total_hours','a.data_id')
-            ->orderby('order_time', 'desc');
+            ->orderby('b.order_time', 'desc');
 
         if(! empty($data['item_code'])) {
             $item_id = Item::where('code', 'LIKE', '%'.$data['item_code'].'%')
@@ -302,9 +303,6 @@ class StatisticsService extends Service
 
             $device[$key]['type_title'] = Device::$type[$value['type']] ?? "";
             $device[$key]['type_2_title'] = Device::$type_2[$value['type_2']] ?? "";
-            $device[$key]['rate_one'] = "100%";
-            $device[$key]['rate_two'] = "100%";
-            $device[$key]['rate_three'] = "100%";
 
             $rate_one = "100";
             $device[$key]['rate_one'] = $rate_one . "%";
@@ -339,4 +337,190 @@ class StatisticsService extends Service
 
         return $timestamps;
     }
+
+    public function statisticsEmployeeExportCommon($data, $user){
+        if(empty($data['month'])) return [false, '请选择导出的月份'];
+        $month = $data['month'];
+
+        $start_time = Carbon::createFromFormat('Y-m', $month)->startOfMonth()->timestamp;
+        $end_time   = Carbon::createFromFormat('Y-m', $month)->endOfMonth()->timestamp;
+
+        $model = RdDetails::from('rd_details as a')
+            ->leftJoin('rd as b','b.id','a.rd_id')
+            ->where('a.del_time',0)
+            ->where('b.del_time',0)
+            ->where('b.order_time', '>=', $start_time)
+            ->where('b.order_time', '<=', $end_time)
+            ->where('a.type', RdDetails::type_one)
+            ->select('b.item_id','b.total_hours','a.data_id','b.order_time')
+            ->orderby('b.order_time', 'asc');
+
+        $time = $this->getMonthsFirstDayTimestamps([
+            0 => $start_time,
+            1 => $end_time,
+        ]);
+
+        $day = CalendarDetails::where('del_time',0)
+            ->where('time', '>=', $start_time)
+            ->where('time', '<=', $end_time)
+            ->where('is_work', 1)
+            ->count();
+
+        return [true, [$model, $time, $day]];
+    }
+
+    public function statisticsEmployeeFillExportData($data, $ergs, $day,$employee,$item_total){
+        // 3. 统计传入数据中的工时(可能是月度,也可能是某一天)
+        $employee_map = [];
+        $employee_map_2 = [];
+        foreach ($data as $value) {
+            $eid = $value['data_id'];
+            $iid = $value['item_id'];
+            $hours = $value['total_hours'];
+
+            $employee_map[$eid] = bcadd($employee_map[$eid] ?? 0, $hours, 2);
+            $employee_map_2[$eid][$iid] = bcadd($employee_map_2[$eid][$iid] ?? 0, $hours, 2);
+        }
+
+        $item_map = array_column($item_total, 'code', 'id');
+        $empty_item_structure = array_fill_keys(array_keys($item_map), 0);
+
+        // 4. 组装最终行数据
+        foreach ($employee as $key => $value) {
+            $curr_id = $value['id'];
+
+            // 计算额定工时
+            $hours = bcmul($day, 8);
+            $employee[$key]['set_total_hours'] = $hours;
+
+            // 计算研发工时
+            $rd_total_minutes = $employee_map[$curr_id] ?? 0;
+            $rd_total_hours = bcdiv($rd_total_minutes, 60, 2);
+            $employee[$key]['rd_total_hours'] = $rd_total_hours;
+
+            // 项目明细列
+            $every_item_hours = $employee_map_2[$curr_id] ?? [];
+            $details = $empty_item_structure;
+            $my_items = [];
+
+            foreach ($every_item_hours as $item_id => $item_minutes) {
+                $details[$item_id] = bcdiv($item_minutes, 60, 2);
+                if (isset($item_map[$item_id])) {
+                    $my_items[] = $item_map[$item_id];
+                }
+            }
+
+            foreach ($details as $it => $d_v) {
+                $employee[$key]['extra_' . $it] = $d_v;
+            }
+
+            $employee[$key]['my_item'] = implode(',', $my_items);
+            $employee[$key]['position'] = "研发人员";
+            $employee[$key]['type_title'] = "专职";
+
+            // 计算比率
+            $rate_val = (floatval($hours) > 0) ? bcdiv($rd_total_hours, $hours, 2) : 0;
+            $rate_percent = bcmul($rate_val, 100, 2) . "%";
+
+            $employee[$key]['rate_one'] = "100%";
+            $employee[$key]['rate_two'] = $rate_percent;
+            $employee[$key]['rate_three'] = $rate_percent;
+        }
+
+        return $employee;
+    }
+
+    public function statisticsDeviceExportCommon($data,$user){
+        if(empty($data['month'])) return [false, '请选择导出的月份'];
+        $month = $data['month'];
+
+        $start_time = Carbon::createFromFormat('Y-m', $month)->startOfMonth()->timestamp;
+        $end_time   = Carbon::createFromFormat('Y-m', $month)->endOfMonth()->timestamp;
+
+        $model = RdDetails::from('rd_details as a')
+            ->leftJoin('rd as b','b.id','a.rd_id')
+            ->where('a.del_time',0)
+            ->where('b.del_time',0)
+            ->where('b.order_time', '>=', $start_time)
+            ->where('b.order_time', '<=', $end_time)
+            ->where('a.type', RdDetails::type_two)
+            ->select('b.item_id','b.total_hours','a.data_id','b.order_time')
+            ->orderby('b.order_time', 'asc');
+
+        $time = $this->getMonthsFirstDayTimestamps([
+            0 => $start_time,
+            1 => $end_time,
+        ]);
+
+        return [true, [$model, $time]];
+    }
+
+    public function statisticsDeviceFillExportData($data, $ergs, $device, $item_total){
+        $device_map = $device_map_2 = $item_id = [];
+        foreach ($data as $value){
+            if(isset($device_map[$value['data_id']])){
+                $total_hours = bcadd($device_map[$value['data_id']], $value['total_hours'],2);
+                $device_map[$value['data_id']] = $total_hours;
+            }else{
+                $device_map[$value['data_id']] = $value['total_hours'];
+            }
+            $key = $value['data_id'];
+            if(isset($device_map_2[$key][$value['item_id']])){
+                $total_hours_2 = bcadd($device_map_2[$key][$value['item_id']], $value['total_hours'],2);
+                $device_map_2[$key][$value['item_id']] = $total_hours_2;
+            }else{
+                $device_map_2[$key][$value['item_id']] = $value['total_hours'];
+            }
+
+            if(! in_array($value['item_id'], $item_id)) $item_id[] = $value['item_id'];
+        }
+        unset($data);
+//        $item_map = array_column($item_total,'code','id');
+
+        $tmp = [];
+        foreach ($item_total as $value){
+            $tmp[$value['id']] = 0;
+        }
+        foreach ($device as $key => $value){
+            $device[$key]['in_time'] = $value['in_time'] ? date("Y-m-d", $value['in_time']) : '';
+
+            //汇总研发工时单里的总工时
+            $rd_total_hours = $device_map[$value['id']] ?? 0;
+            $rd_total_hours = bcdiv($rd_total_hours,60,2);
+            $device[$key]['rd_total_hours'] = $rd_total_hours;
+            $device[$key]['set_total_hours'] = $rd_total_hours;
+            //每个项目的工时
+            $every_item_hours = $device_map_2[$value['id']] ?? [];
+
+            $details = $tmp;
+            $my_item = "";
+            if(! empty($every_item_hours)){
+                foreach ($every_item_hours as $item => $item_hour){
+                    if(isset($details[$item])) {
+                        $details[$item] = bcdiv($item_hour,60,2);
+                    }
+//                    $code = $item_map[$item] ?? "";
+//                    if(! empty($code)) $my_item .= $code . ",";
+                }
+//                $my_item = rtrim($my_item,',');
+            }
+            foreach ($details as $it => $d_v){
+                $device[$key]['extra_' . $it] = $d_v;
+            }
+
+            $device[$key]['my_item'] = $my_item;
+
+            $device[$key]['type_title'] = Device::$type[$value['type']] ?? "";
+            $device[$key]['type_2_title'] = Device::$type_2[$value['type_2']] ?? "";
+
+            $rate_one = "100";
+            $device[$key]['rate_one'] = $rate_one . "%";
+            $rate_two = floatval($rd_total_hours) > 0.0 ? bcdiv($rd_total_hours, $rd_total_hours,2) : 0;
+            $rate_two = bcmul($rate_two, 100,2);
+            $device[$key]['rate_two'] = $rate_two . "%";
+            $device[$key]['rate_three'] = $rate_two . "%";
+        }
+
+        return $device;
+    }
 }

+ 0 - 5
app/Service/SysMenuService.php

@@ -2,11 +2,6 @@
 
 namespace App\Service;
 
-use App\Exports\ExportRule;
-use App\Exports\MultiSheetExport;
-use App\Exports\MyExport;
-use App\Model\Employee;
-use App\Model\Role;
 use App\Model\RoleMenu;
 use App\Model\RoleMenuButton;
 use App\Model\SysMenu;

+ 2 - 2
config/header/73.php

@@ -8,7 +8,7 @@
 return [
     [
         'key' =>'name',
-        'value' => '研发人员名',
+        'value' => '研发人员名',
     ],
     [
         'key' =>'position',
@@ -36,7 +36,7 @@ return [
     ],
     [
         'key' =>'set_total_hours',
-        'value' => '满勤工时(小时)',
+        'value' => '工时(小时)',
     ],
     [
         'key' =>'rd_total_hours',

+ 2 - 2
config/header/74.php

@@ -8,7 +8,7 @@
 return [
     [
         'key' =>'name',
-        'value' => '研发设备',
+        'value' => '研发固定资产名称',
     ],
     [
         'key' =>'code',
@@ -52,7 +52,7 @@ return [
     ],
     [
         'key' =>'set_total_hours',
-        'value' => '满勤工时(小时)',
+        'value' => '工时(小时)',
     ],
     [
         'key' =>'rd_total_hours',