model = new \app\admin\model\Supplierservicescore; } public function index() { $this->request->filter(['strip_tags', 'trim']); if ($this->request->isAjax()) { $ym = $this->resolveRequestYm(); $keyword = $this->resolveSearchKeyword(); $offset = max(0, (int)$this->request->get('offset', 0)); $limit = (int)$this->request->get('limit', 50); if ($limit <= 0) { $limit = 50; } // 有搜索词:跨月查该供应商;无搜索:按查询月份展示当月 $list = $keyword !== '' ? $this->loadSupplierScoreSearchList($keyword) : $this->loadMonthlySupplierScoreList($ym, ''); $total = count($list); $pageRows = array_slice($list, $offset, $limit); $seqBase = $offset; $rows = []; foreach ($pageRows as $i => $item) { $rowYm = (string)($item['ym'] ?? $ym); $rows[] = [ 'id' => $seqBase + $i + 1, 'seq_no' => $seqBase + $i + 1, 'company_name' => (string)($item['company_name'] ?? ''), 'quality_score' => $item['quality_score'] ?? null, 'quality_score_text' => (string)($item['quality_score_text'] ?? ''), 'price_score' => $item['price_score'] ?? null, 'price_score_text' => (string)($item['price_score_text'] ?? ''), 'delivery_score' => $item['delivery_score'] ?? null, 'delivery_score_text' => (string)($item['delivery_score_text'] ?? ''), 'final_score' => $item['final_score'] ?? null, 'final_score_text' => (string)($item['final_score_text'] ?? ''), 'final_score_saved' => !empty($item['final_score_saved']) ? 1 : 0, 'score_grade' => (string)($item['score_grade'] ?? ''), 'score_date' => (string)(($item['score_date'] ?? '') !== '' ? $item['score_date'] : $rowYm), 'ym' => $rowYm, ]; } return json(['total' => $total, 'rows' => $rows]); } return $this->view->fetch(); } /** * 批量保存当月最终得分与评分等级 * POST: ym, rows=[{company_name, final_score, score_grade}, ...] 或 rows_json */ public function savefinalbatch() { if (!$this->auth->check('supplierservicescore/index') && !$this->auth->isSuperAdmin()) { $this->error(__('You have no permission')); } ProcuremenSupplierScore::ensureFinalScoreTable(); $ym = trim((string)$this->request->post('ym', '')); if ($ym === '') { $ym = trim((string)$this->request->param('ym', '')); } if (!preg_match('/^\d{4}-\d{2}$/', $ym)) { $this->error('请选择查询月份'); } $rows = $this->request->post('rows/a', []); if (!is_array($rows) || $rows === []) { $rowsJson = trim((string)$this->request->post('rows_json', '')); if ($rowsJson !== '') { $decoded = json_decode($rowsJson, true); if (is_array($decoded)) { $rows = $decoded; } } } if (!is_array($rows) || $rows === []) { $this->error('没有可保存的数据'); } // 按行自带 ym 分组(跨月搜索结果可同页保存) $byYm = []; foreach ($rows as $row) { if (!is_array($row)) { continue; } $rowYm = trim((string)($row['ym'] ?? '')); if (!preg_match('/^\d{4}-\d{2}$/', $rowYm)) { $rowYm = $ym; } if (!isset($byYm[$rowYm])) { $byYm[$rowYm] = []; } $byYm[$rowYm][] = $row; } $done = 0; $err = ''; foreach ($byYm as $saveYm => $chunk) { $ret = ProcuremenSupplierScore::saveFinalScoresForYm($saveYm, $chunk); $done += (int)($ret['done'] ?? 0); if ($err === '') { $err = trim((string)($ret['error'] ?? '')); } } if ($done < 1) { $this->error($err !== '' ? $err : '保存失败,请检查填写的分数'); } $this->success('操作成功'); } /** * 导出供应商评审表 * 列:序号、供应商、最终得分、评分等级 */ public function export() { if (!$this->auth->check('supplierservicescore/index') && !$this->auth->isSuperAdmin()) { $this->error(__('You have no permission')); } $ym = trim((string)$this->request->param('ym', '')); if (!preg_match('/^(\d{4})-(\d{2})$/', $ym, $m)) { $this->error('请选择查询月份'); } $year = (int)$m[1]; $month = (int)$m[2]; if ($month < 1 || $month > 12) { $this->error('查询月份无效'); } $list = $this->loadMonthlySupplierScoreList($ym, ''); $title = $year . '年' . $month . '月外协加工供应商评审表'; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('供应商评审表'); $fontName = '宋体'; $borderStyle = [ 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], ]; $centerAlign = [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true, ]; $sheet->mergeCells('A1:D1'); $sheet->setCellValue('A1', $title); $sheet->getRowDimension(1)->setRowHeight(36); $sheet->getStyle('A1')->getFont()->setName($fontName)->setBold(true)->setSize(20); $sheet->getStyle('A1')->getAlignment()->applyFromArray($centerAlign); $sheet->setCellValue('A2', '序号'); $sheet->setCellValue('B2', '供应商'); $sheet->setCellValue('C2', '最终得分'); $sheet->setCellValue('D2', '评分等级'); $sheet->getRowDimension(2)->setRowHeight(26); $sheet->getStyle('A2:D2')->getFont()->setName($fontName)->setBold(true)->setSize(14); $sheet->getStyle('A2:D2')->getAlignment()->applyFromArray($centerAlign); $rowNum = 3; if ($list === []) { $sheet->mergeCells('A3:D3'); $sheet->setCellValue('A3', '(该月暂无供应商评审记录)'); $sheet->getRowDimension(3)->setRowHeight(26); $sheet->getStyle('A3')->getFont()->setName($fontName)->setSize(12); $sheet->getStyle('A3')->getAlignment()->applyFromArray($centerAlign); $rowNum = 4; } else { $i = 1; foreach ($list as $item) { $sheet->setCellValue('A' . $rowNum, $i); $sheet->setCellValue('B' . $rowNum, (string)($item['company_name'] ?? '')); $sheet->setCellValue('C' . $rowNum, (string)($item['final_score_text'] ?? '')); $sheet->setCellValue('D' . $rowNum, (string)($item['score_grade'] ?? '')); $sheet->getRowDimension($rowNum)->setRowHeight(24); $rowNum++; $i++; } } $lastRow = max(2, $rowNum - 1); $sheet->getStyle('A1:D' . $lastRow)->applyFromArray($borderStyle); $sheet->getStyle('A3:D' . $lastRow)->getFont()->setName($fontName)->setSize(12); $sheet->getStyle('A3:D' . $lastRow)->getAlignment()->applyFromArray($centerAlign); $sheet->getColumnDimension('A')->setWidth(12); $sheet->getColumnDimension('B')->setWidth(48); $sheet->getColumnDimension('C')->setWidth(14); $sheet->getColumnDimension('D')->setWidth(12); $sheet->getPageSetup()->setFitToPage(true)->setFitToWidth(1)->setFitToHeight(0); $filename = $title . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . rawurlencode($filename) . '"'); header('Cache-Control: max-age=0'); $writer = new Xlsx($spreadsheet); $writer->save('php://output'); $spreadsheet->disconnectWorksheets(); unset($spreadsheet); exit; } /** * 月度评审记录列表(仅展示,最终得分可人工填写) * * @return array> */ protected function loadMonthlySupplierScoreList(string $ym, string $keyword = ''): array { $ym = trim($ym); if (!preg_match('/^\d{4}-\d{2}$/', $ym)) { return []; } ProcuremenSupplierScore::ensureSchema(); $monthlyMap = ProcuremenSupplierScore::loadMonthlyMapByYm($ym); $keyword = trim($keyword); $list = []; foreach ($monthlyMap as $name => $item) { if ($keyword !== '' && mb_stripos($name, $keyword) === false) { continue; } $list[] = $this->buildSupplierScoreListItem($name, $item, $ym); } usort($list, [$this, 'sortSupplierScoreListItems']); return $list; } /** * 按供应商关键词跨月搜索(不限查询月份) * * @return array> */ protected function loadSupplierScoreSearchList(string $keyword): array { $keyword = trim($keyword); if ($keyword === '') { return []; } ProcuremenSupplierScore::ensureSchema(); ProcuremenSupplierScore::ensureFinalScoreTable(); try { $rows = \think\Db::table(ProcuremenSupplierScore::TABLE_FINAL) ->where('company_name', 'like', '%' . addcslashes($keyword, '%_\\') . '%') ->field('ym,company_name,score,quality_score,price_score,delivery_score,final_score,score_grade') ->order('ym', 'desc') ->order('company_name', 'asc') ->select(); } catch (\Throwable $e) { return []; } if (!is_array($rows)) { return []; } $list = []; foreach ($rows as $r) { if (!is_array($r)) { continue; } $name = trim((string)($r['company_name'] ?? '')); $rowYm = ProcuremenSupplierScore::formatScoreYm($r['ym'] ?? ''); if ($name === '' || $rowYm === '') { continue; } $quality = ProcuremenSupplierScore::readOptionalScoreValue($r['quality_score'] ?? null); $price = ProcuremenSupplierScore::readOptionalScoreValue($r['price_score'] ?? null); $delivery = ProcuremenSupplierScore::readOptionalScoreValue($r['delivery_score'] ?? null); $final = ProcuremenSupplierScore::readOptionalScoreValue($r['final_score'] ?? null); $item = [ 'quality_score' => $quality, 'price_score' => $price, 'delivery_score' => $delivery, 'final_score' => $final, 'final_saved' => $final !== null ? 1 : 0, 'score_grade' => ProcuremenSupplierScore::normalizeScoreGrade($r['score_grade'] ?? ''), 'score_date' => $rowYm, ]; $list[] = $this->buildSupplierScoreListItem($name, $item, $rowYm); } usort($list, [$this, 'sortSupplierScoreListItems']); return $list; } /** * @param array $item * @return array */ protected function buildSupplierScoreListItem(string $name, array $item, string $ym): array { $quality = array_key_exists('quality_score', $item) ? $item['quality_score'] : null; $price = array_key_exists('price_score', $item) ? $item['price_score'] : null; $delivery = array_key_exists('delivery_score', $item) ? $item['delivery_score'] : null; if ($quality !== null) { $quality = (float)$quality; } if ($price !== null) { $price = (float)$price; } if ($delivery !== null) { $delivery = (float)$delivery; } $hasFinal = !empty($item['final_saved']); $final = $hasFinal ? (float)($item['final_score'] ?? 0) : null; $sortScore = $hasFinal ? (float)$final : (($quality ?? 0) + ($price ?? 0) + ($delivery ?? 0)); $scoreDate = (string)($item['score_date'] ?? ''); if ($scoreDate === '') { $scoreDate = ProcuremenSupplierScore::formatScoreYm($ym); } return [ 'company_name' => $name, 'ym' => ProcuremenSupplierScore::formatScoreYm($ym), 'quality_score' => $quality, 'quality_score_text' => ProcuremenSupplierScore::formatOptionalScore($quality), 'price_score' => $price, 'price_score_text' => ProcuremenSupplierScore::formatOptionalScore($price), 'delivery_score' => $delivery, 'delivery_score_text' => ProcuremenSupplierScore::formatOptionalScore($delivery), 'final_score' => $final, 'final_score_text' => $hasFinal ? ProcuremenSupplierScore::formatOptionalScore($final) : '', 'final_score_saved' => $hasFinal ? 1 : 0, 'score_grade' => ProcuremenSupplierScore::normalizeScoreGrade($item['score_grade'] ?? ''), 'score_date' => $scoreDate, 'sort_score' => $sortScore, ]; } /** * @param array $a * @param array $b */ protected function sortSupplierScoreListItems(array $a, array $b): int { $ya = (string)($a['ym'] ?? ''); $yb = (string)($b['ym'] ?? ''); if ($ya !== $yb) { return strcmp($yb, $ya); } $sa = (float)($a['sort_score'] ?? 0); $sb = (float)($b['sort_score'] ?? 0); if ($sa !== $sb) { return $sb <=> $sa; } return strcmp((string)($a['company_name'] ?? ''), (string)($b['company_name'] ?? '')); } protected function resolveRequestYm(): string { $ym = trim((string)$this->request->get('ym', '')); if (preg_match('/^\d{4}-\d{2}$/', $ym)) { return $ym; } $filterRaw = $this->request->get('filter', ''); if (is_string($filterRaw) && $filterRaw !== '') { $filter = json_decode($filterRaw, true); if (is_array($filter)) { $fy = trim((string)($filter['ym'] ?? '')); if (preg_match('/^\d{4}-\d{2}$/', $fy)) { return $fy; } } } if (preg_match('/^(\d{4})\D+(\d{1,2})/', $ym, $m)) { return sprintf('%04d-%02d', (int)$m[1], (int)$m[2]); } return date('Y-m'); } protected function resolveSearchKeyword(): string { $keyword = trim((string)$this->request->get('search', '')); if ($keyword !== '') { return $keyword; } $filterRaw = $this->request->get('filter', ''); if (is_string($filterRaw) && $filterRaw !== '') { $filter = json_decode($filterRaw, true); if (is_array($filter)) { return trim((string)($filter['company_name'] ?? '')); } } return ''; } }