"use client"; import React, { useState } from "react"; import * as XLSX from "xlsx"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; interface ContactData { mobile: number; from: string; alias: string; } export default function ContactImportPage() { const [parsedData, setParsedData] = useState([]); const [error, setError] = useState(null); const [fileName, setFileName] = useState(""); const [isImportSuccessful, setIsImportSuccessful] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const [detectedColumns, setDetectedColumns] = useState<{ mobile?: string; from?: string; alias?: string; }>({}); const handleFileChange = (e: React.ChangeEvent) => { const files = e.target.files; if (!files || files.length === 0) { return; } // 重置状态 setError(null); setIsImportSuccessful(false); setParsedData([]); setDetectedColumns({}); setIsProcessing(true); const file = files[0]; setFileName(file.name); const reader = new FileReader(); reader.onload = (event) => { try { const data = event.target?.result; if (!data) { setError("文件内容为空,请检查文件是否有效"); setIsProcessing(false); return; } let workbook; try { workbook = XLSX.read(data, { type: "binary" }); } catch (parseErr) { console.error("解析Excel内容失败:", parseErr); setError("无法解析文件内容,请确保上传的是有效的Excel文件(.xlsx或.xls格式)"); setIsProcessing(false); return; } if (!workbook.SheetNames || workbook.SheetNames.length === 0) { setError("Excel文件中没有找到工作表"); setIsProcessing(false); return; } const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; if (!worksheet) { setError(`无法读取工作表 "${sheetName}",请检查文件是否损坏`); setIsProcessing(false); return; } // 转换为JSON const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: "A" }); // 检查是否有数据 if (!jsonData || jsonData.length === 0) { setError("Excel 文件中没有数据"); setIsProcessing(false); return; } // 查找栏位对应的列 let mobileColumn: string | null = null; let fromColumn: string | null = null; let aliasColumn: string | null = null; // 遍历第一行查找栏位 const firstRow = jsonData[0] as Record; if (!firstRow) { setError("Excel 文件的第一行为空,无法识别栏位"); setIsProcessing(false); return; } for (const key in firstRow) { if (!firstRow[key]) continue; // 跳过空值 const value = String(firstRow[key]).toLowerCase(); // 扩展匹配列表,提高识别成功率 if (value.includes("手机") || value.includes("电话") || value.includes("mobile") || value.includes("phone") || value.includes("tel") || value.includes("cell")) { mobileColumn = key; } else if (value.includes("来源") || value.includes("source") || value.includes("from") || value.includes("channel") || value.includes("渠道")) { fromColumn = key; } else if (value.includes("微信") || value.includes("alias") || value.includes("wechat") || value.includes("wx") || value.includes("id") || value.includes("账号")) { aliasColumn = key; } } // 保存检测到的列名 if (mobileColumn && firstRow[mobileColumn]) { setDetectedColumns(prev => ({ ...prev, mobile: String(firstRow[mobileColumn]) })); } if (fromColumn && firstRow[fromColumn]) { setDetectedColumns(prev => ({ ...prev, from: String(firstRow[fromColumn]) })); } if (aliasColumn && firstRow[aliasColumn]) { setDetectedColumns(prev => ({ ...prev, alias: String(firstRow[aliasColumn]) })); } if (!mobileColumn) { setError("未找到手机号码栏位,请确保Excel中包含手机、电话、mobile或phone等栏位名称"); setIsProcessing(false); return; } // 解析数据,跳过首行 const importedData: ContactData[] = []; for (let i = 1; i < jsonData.length; i++) { const row = jsonData[i] as Record; // 检查行是否存在且有手机号列 if (!row || row[mobileColumn] === undefined || row[mobileColumn] === null) { continue; } // 安全地转换并清理手机号 const mobileStr = row[mobileColumn] !== undefined && row[mobileColumn] !== null ? String(row[mobileColumn]).trim() : ""; // 过滤非数字字符,确保手机号是纯数字 const mobile = mobileStr.replace(/\D/g, ""); // 手机号为空的跳过 if (!mobile) continue; // 安全地获取来源和别名 const from = fromColumn && row[fromColumn] !== undefined && row[fromColumn] !== null ? String(row[fromColumn]).trim() : ""; const alias = aliasColumn && row[aliasColumn] !== undefined && row[aliasColumn] !== null ? String(row[aliasColumn]).trim() : ""; importedData.push({ mobile: Number(mobile), from, alias }); } if (importedData.length === 0) { setError("未找到有效数据,请确保Excel中至少有一行有效的手机号码"); setIsProcessing(false); return; } setParsedData(importedData); setIsProcessing(false); } catch (err) { console.error("解析Excel文件出错:", err); setError("解析Excel文件时出错,请确保文件格式正确"); setIsProcessing(false); } }; reader.onerror = () => { setError("读取文件时出错,请重试"); setIsProcessing(false); }; reader.readAsBinaryString(file); }; const handleImport = () => { if (parsedData.length > 0) { // 打印解析数据 console.log("导入的联系人数据:"); console.log(JSON.stringify(parsedData, null, 2)); // 在界面上显示成功消息 setIsImportSuccessful(true); } }; const handleReset = () => { setParsedData([]); setFileName(""); setError(null); setIsImportSuccessful(false); const fileInput = document.getElementById("file-input") as HTMLInputElement; if (fileInput) { fileInput.value = ""; } }; return (

联系人导入

{fileName && (

当前文件: {fileName}

)}
请确保Excel文件包含以下列: 手机号码(必需)、来源(可选)、微信号(可选)
{isProcessing && (

正在处理Excel文件...

)} {error && ( {error} )} {isImportSuccessful && ( 已成功导入 {parsedData.length} 条联系人数据!数据已打印到控制台。 )} {parsedData.length > 0 && !isImportSuccessful && (

已解析 {parsedData.length} 条有效数据,点击下方按钮确认导入。

{Object.keys(detectedColumns).length > 0 && (

检测到的列名:

    {detectedColumns.mobile &&
  • 手机号: {detectedColumns.mobile}
  • } {detectedColumns.from &&
  • 来源: {detectedColumns.from}
  • } {detectedColumns.alias &&
  • 微信号: {detectedColumns.alias}
  • }
)}

数据示例:

{JSON.stringify(parsedData.slice(0, 3), null, 2)}
{parsedData.length > 3 &&

...共 {parsedData.length} 条

}

数据结构:

{`[
  {
    "mobile": 13800000000,
    "from": "小红书",
    "alias": "xxxxxx"
  },
  ...
]`}
)}
); }