仅支持 CSV、XLS、XLSX 格式'; } else { $imported = 0; $errors = []; if ($ext === 'csv') { $handle = fopen($_FILES['file']['tmp_name'], 'r'); if ($handle) { $lineNo = 0; while (($row = fgetcsv($handle)) !== false) { $lineNo++; if ($lineNo === 1) continue; // 跳过表头 if (count($row) < 5) { $errors[] = "第{$lineNo}行: 列数不足(至少需要5列)"; continue; } $name = trim($row[0]); $batchNo = trim($row[1]); $type = (int)trim($row[2]); $code = trim($row[3]); $value = trim($row[4]); $expiredAt = isset($row[5]) ? trim($row[5]) : ''; $price1 = isset($row[6]) && $row[6] !== '' ? (float)trim($row[6]) : null; $price2 = isset($row[7]) && $row[7] !== '' ? (float)trim($row[7]) : null; if (!$name || !$batchNo || !$code) { $errors[] = "第{$lineNo}行: 兑换码名称、批次号、兑换码为必填项"; continue; } if ($type !== 1) { $errors[] = "第{$lineNo}行: 兑换码类型必须为1,当前为{$type}"; continue; } $stmt = $pdo->prepare('INSERT INTO redemption_codes (product_id, created_at, user_id, name, batch_no, type, code, value, expired_at, price_tier1, price_tier2) VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name), batch_no = VALUES(batch_no), type = VALUES(type), value = VALUES(value), expired_at = VALUES(expired_at), price_tier1 = VALUES(price_tier1), price_tier2 = VALUES(price_tier2)'); $stmt->execute([$pid, getCurrentUserId(), $name, $batchNo, $type, $code, $value, $expiredAt ?: null, $price1, $price2]); $imported++; } fclose($handle); } } elseif ($ext === 'xlsx') { $rows = readXlsx($_FILES['file']['tmp_name']); if ($rows === false) { $errors[] = '无法解析 XLSX 文件,请确认文件未损坏'; } else { foreach ($rows as $lineNo => $row) { if ($lineNo === 0) continue; // 跳过表头 $ln = $lineNo + 1; if (count($row) < 5) { $errors[] = "第{$ln}行: 列数不足(至少需要5列)"; continue; } $name = trim($row[0] ?? ''); $batchNo = trim($row[1] ?? ''); $type = (int)($row[2] ?? 0); $code = trim($row[3] ?? ''); $value = trim($row[4] ?? ''); $expiredAt = isset($row[5]) ? trim($row[5]) : ''; $price1 = isset($row[6]) && $row[6] !== '' ? (float)$row[6] : null; $price2 = isset($row[7]) && $row[7] !== '' ? (float)$row[7] : null; if (!$name || !$batchNo || !$code) { $errors[] = "第{$ln}行: 兑换码名称、批次号、兑换码为必填项"; continue; } if ($type !== 1) { $errors[] = "第{$ln}行: 兑换码类型必须为1,当前为{$type}"; continue; } $stmt = $pdo->prepare('INSERT INTO redemption_codes (product_id, created_at, user_id, name, batch_no, type, code, value, expired_at, price_tier1, price_tier2) VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name), batch_no = VALUES(batch_no), type = VALUES(type), value = VALUES(value), expired_at = VALUES(expired_at), price_tier1 = VALUES(price_tier1), price_tier2 = VALUES(price_tier2)'); $stmt->execute([$pid, getCurrentUserId(), $name, $batchNo, $type, $code, $value, $expiredAt ?: null, $price1, $price2]); $imported++; } } } else { $errors[] = 'XLS(旧版Excel格式)不支持,请另存为 XLSX 或 CSV 格式后导入'; } $flashMsg = ''; if ($imported > 0) { $flashMsg = '成功导入 ' . $imported . ' 条记录'; if (!empty($errors)) $flashMsg .= ',' . count($errors) . ' 条失败'; } if (!empty($errors) && $imported > 0) { $flashMsg .= "\n" . implode("\n", array_slice($errors, 0, 20)); $_SESSION['flash_msg'] = $flashMsg; $_SESSION['flash_type'] = 'success'; header('Location: code_manage.php'); exit; } elseif ($imported > 0) { $_SESSION['flash_msg'] = $flashMsg; $_SESSION['flash_type'] = 'success'; header('Location: code_manage.php'); exit; } elseif (!empty($errors)) { $msg = '
' . h(implode("\n", array_slice($errors, 0, 20))) . '
'; } } } else { $msg = '
请选择文件
'; } } // 删除单个兑换码 if (isset($_GET['del']) && is_numeric($_GET['del'])) { if (!verifyCsrf($_GET['csrf_token'] ?? '')) { $_SESSION['flash_msg'] = 'CSRF token无效'; $_SESSION['flash_type'] = 'danger'; header('Location: code_manage.php'); exit; } $id = (int)$_GET['del']; $pdo->prepare('DELETE FROM redemption_codes WHERE id = ?')->execute([$id]); $_SESSION['flash_msg'] = '兑换码已删除'; $_SESSION['flash_type'] = 'success'; header('Location: code_manage.php'); exit; } // 搜索条件 $where = 'WHERE c.product_id = ?'; $searchParams = [$pid]; $joinUser = false; if (!empty($_GET['claim_user'])) { $where .= ' AND u.username LIKE ?'; $searchParams[] = '%' . $_GET['claim_user'] . '%'; $joinUser = true; } foreach (['code' => 'c.code', 'name' => 'c.name', 'batch_no' => 'c.batch_no'] as $key => $col) { if (!empty($_GET[$key])) { $where .= " AND $col LIKE ?"; $searchParams[] = '%' . $_GET[$key] . '%'; } } // 分页 $page = max(1, (int)($_GET['p'] ?? 1)); $perPage = 20; $offset = ($page - 1) * $perPage; $join = 'LEFT JOIN users u ON c.claim_user_id = u.id'; $stmt = $pdo->prepare("SELECT COUNT(*) FROM redemption_codes c $join $where"); $stmt->execute($searchParams); $total = (int)$stmt->fetchColumn(); $totalPages = max(1, ceil($total / $perPage)); $stmt = $pdo->prepare("SELECT c.*, u.username AS claim_username FROM redemption_codes c $join $where ORDER BY c.id DESC LIMIT ? OFFSET ?"); $stmt->execute(array_merge($searchParams, [$perPage, $offset])); $codes = $stmt->fetchAll(); ?>

库存管理

清空
+ Excel导入 下载导入模板
ID 创建时间 名称 批次号 类型 兑换码 面值 状态 过期时间 用户 领取时间 出货价一档 出货价二挡 操作
删除
暂无数据
open($file) !== true) return false; // 读取共享字符串表 $sharedStrings = []; $ssXml = $zip->getFromName('xl/sharedStrings.xml'); if ($ssXml !== false) { $ss = simplexml_load_string($ssXml); if ($ss) { foreach ($ss->si as $si) { $t = $si->t; if ($t) { $sharedStrings[] = (string)$t; } else { // 富文本,拼接所有 t 子元素 $parts = []; foreach ($si->r->t as $rT) { $parts[] = (string)$rT; } $sharedStrings[] = implode('', $parts); } } } } // 读取第一个工作表 $sheetXml = $zip->getFromName('xl/worksheets/sheet1.xml'); $zip->close(); if ($sheetXml === false) return false; $sheet = simplexml_load_string($sheetXml); if (!$sheet) return false; $ns = $sheet->getNamespaces(true); $sheet->registerXPathNamespace('s', $ns[''] ?? ''); $rows = []; $maxCol = 0; $cells = $sheet->xpath('//s:sheetData/s:row/s:c') ?: []; // 第一遍:收集所有数据 $data = []; foreach ($cells as $c) { $ref = (string)$c['r']; if (!preg_match('/^([A-Z]+)(\d+)$/', $ref, $m)) continue; $colStr = $m[1]; $rowNum = (int)$m[2]; // 列字母转数字 $colNum = 0; for ($i = 0; $i < strlen($colStr); $i++) { $colNum = $colNum * 26 + (ord($colStr[$i]) - 64); } $type = (string)$c['t']; $value = (string)$c->v; if ($type === 's') { $idx = (int)$value; $cellValue = $sharedStrings[$idx] ?? ''; } else { $cellValue = $value; } if (!isset($data[$rowNum])) $data[$rowNum] = []; $data[$rowNum][$colNum] = $cellValue; if ($colNum > $maxCol) $maxCol = $colNum; } // 按行排序输出 ksort($data); foreach ($data as $rowNum => $cols) { $row = []; for ($c = 1; $c <= $maxCol; $c++) { $row[] = $cols[$c] ?? ''; } $rows[] = $row; } return $rows; } require __DIR__ . '/includes/footer.php'; ?>