[] = [
+ {
+ title: "来源",
+ width: 40,
+ key: "订单来源信息",
+ align: "center",
+ // 添加账号编号筛选功能
+ filterDropdown: ({
+ setSelectedKeys,
+ selectedKeys,
+ confirm,
+ clearFilters,
+ }) => (
+
+
+ setSelectedKeys(e.target.value ? [e.target.value] : [])
+ }
+ onPressEnter={() => confirm()}
+ style={{ marginBottom: 8, display: "block" }}
+ />
+
+
+
+
+
+ ),
+ filterIcon: (filtered) => (
+
+ ),
+ onFilter: (value, record) => {
+ const accountNumber = record.订单来源?.账号编号 ?? "";
+ // 将 value 转换为字符串类型
+ const searchVal = String(value);
+ return accountNumber.includes(searchVal);
+ },
+ render: (record: any) => {
+ const wechatId = record.订单来源?.微信号 ?? "未知";
+ const accountNumber = record.订单来源?.账号编号 ?? "未知";
+ return (
+
+ }
+ color="blue"
+ style={{ marginBottom: 4 }}
+ >
+ {accountNumber}
+
+ } color="green">
+ {wechatId}
+
+
+ );
+ },
+ },
+
+ {
+ title: () => (
+
+ 日期/导购
+ setDateSort(e.target.value)}
+ size="small"
+ buttonStyle="solid"
+ style={{ margin: '0 2px' }}
+ >
+
+ 成交
+
+
+ 创建
+
+
+ 降}
+ unCheckedChildren={升}
+ checked={sortDirection === 'descend'}
+ onChange={(checked) => setSortDirection(checked ? 'descend' : 'ascend')}
+ size="small"
+ style={{
+ marginLeft: 2,
+ minWidth: '32px',
+ height: '20px'
+ }}
+ />
+
+ ),
+ key: "日期和导购信息",
+ align: "center",
+ width: 160, // 减小宽度因为我们优化了控件大小
+ render: (record: any) => {
+ // 格式化日期为更简洁的形式 (年-月-日)
+ const formatDate = (dateString: string) => {
+ if (!dateString) return "未知";
+ const date = new Date(dateString);
+ return date.toLocaleDateString('zh-CN', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit'
+ }).replace(/\//g, '-');
+ };
+ const transactionDate = formatDate(record.成交日期);
+ const createdDate = formatDate(record.createdAt);
+ const guideName = record.导购?.姓名 ?? "未知";
+
+ // 为完整信息创建tooltip内容
+ return (
+
+ {/* 导购信息 */}
+ }
+ color="green"
+ style={{
+ marginBottom: 4,
+ textAlign: 'center',
+ fontWeight: 'bold'
+ }}
+ >
+ {guideName}
+
+
+ {/* 成交日期 */}
+ }
+ color={dateSort === '成交日期' ? 'blue' : 'default'}
+ style={{
+ margin: 0,
+ textAlign: 'center',
+ fontSize: '11px',
+ fontWeight: dateSort === '成交日期' ? 'bold' : 'normal',
+ padding: dateSort === '成交日期' ? '0 5px' : '0 4px'
+ }}
+ >
+ 成交: {transactionDate}
+
+
+ {/* 创建日期 */}
+ }
+ color={dateSort === '创建日期' ? 'blue' : 'default'}
+ style={{
+ margin: 0,
+ textAlign: 'center',
+ fontSize: '11px',
+ fontWeight: dateSort === '创建日期' ? 'bold' : 'normal',
+ padding: dateSort === '创建日期' ? '0 5px' : '0 4px'
+ }}
+ >
+ 创建: {createdDate}
+
+
+ );
+ },
+ },
+ {
+ title: "客户信息",
+ width: 60,
+ align: "center",
+ key: "客户信息",
+ // 增加filterDropdown用于尾号查询
+ filterDropdown: ({
+ setSelectedKeys,
+ selectedKeys,
+ confirm,
+ clearFilters,
+ }) => (
+
+
+ setSelectedKeys(e.target.value ? [e.target.value] : [])
+ }
+ onPressEnter={() => confirm()}
+ style={{ marginBottom: 8, display: "block" }}
+ />
+
+
+
+
+
+ ),
+ filterIcon: (filtered) => (
+
+ ),
+ onFilter: (value, record) => {
+ const phoneTail = record.客户?.电话 ? record.客户.电话.slice(-4) : "";
+ // 将 value 转换为字符串类型
+ const searchVal = String(value);
+ return phoneTail.includes(searchVal);
+ },
+ render: (record: any) => {
+ // 拼接地址信息
+ const address = record.客户?.地址
+ ? `${record.客户.地址.省份 ?? ""} ${record.客户.地址.城市 ?? ""} ${record.客户.地址.区县 ?? ""
+ } ${record.客户.地址.详细地址 ?? ""}`
+ : "未知";
+
+ const customerName = record.客户?.姓名 ?? "未知";
+
+ // 计算成交周期
+ const transactionDate = record.成交日期 ? new Date(record.成交日期) : new Date();
+ const addFansDate = record.客户?.加粉日期 ? new Date(record.客户.加粉日期) : null;
+ const diffDays = addFansDate ? Math.ceil(
+ Math.abs(transactionDate.getTime() - addFansDate.getTime()) / (1000 * 60 * 60 * 24)
+ ) : '未知';
+
+ // 从 record 获取待收金额
+ const unreceivedAmount = record.待收款
+ ? parseFloat(record.待收款.toFixed(2))
+ : 0;
+ // 如果待收金额大于0,使用红色,否则使用蓝色
+ const customerTagColor = unreceivedAmount > 0 ? "#cd201f" : "blue";
+
+ return (
+
+ {/* 左侧复制按钮 */}
+
+ {isAdmin && (
+ {
+ try {
+ if (
+ typeof window !== "undefined" &&
+ window.navigator.clipboard &&
+ window.navigator.clipboard.writeText
+ ) {
+ // 将客户信息转换为纯文本
+ const customerText = `姓名:${record.客户?.姓名 ?? "未知"
+ }\n电话:${record.客户?.电话 ?? "未知"
+ }\n地址:${address}`;
+ await window.navigator.clipboard.writeText(
+ customerText
+ );
+ message.success(`客户 ${customerName} 信息复制成功!`);
+ } else {
+ message.error("当前浏览器不支持复制功能");
+ }
+ } catch (error) {
+ console.error("客户信息复制失败:", error);
+ message.error("客户信息复制失败");
+ }
+ }}
+ >
+
+
+ )}
+
+
+ {/* 右侧标签信息,每行一个标签 */}
+
+
+ {/* 第一行:客户姓名 */}
+ }
+ color={customerTagColor}
+ style={{ margin: 0 }}
+ >
+ {record.客户?.姓名 ?? "未知"}
+
+
+ {/* 第二行:电话尾号 */}
+ } color="green" style={{ margin: 0 }}>
+ {record.客户?.电话
+ ? `${record.客户.电话.slice(-4)}`
+ : "未知"}
+
+
+ {/* 第三行:加粉日期 */}
+ }
+ color="purple"
+ style={{ margin: 0 }}
+ >
+ {record.客户?.加粉日期
+ ? new Date(record.客户.加粉日期).toLocaleDateString()
+ : "未知"}
+
+
+ {/* 第四行:成交周期 */}
+
+ 成交周期:{diffDays}天
+
+
+
+
+ );
+ },
+ },
+ //备注
+ {
+ title: "备注",
+ key: "备注",
+ dataIndex: "备注",
+ width: 260,
+ render: (text: string, record: any) => {
+ // 获取产品列表
+ const products = record.产品 || [];
+ const fetchBase64ImageAsBlob = async (
+ productId: string
+ ): Promise => {
+ const response = await fetch(`/api/products/images/${productId}`);
+ const data = await response.json();
+
+ if (!data || !data.image) {
+ throw new Error(`未找到有效的 image 数据,产品ID: ${productId}`);
+ }
+
+ const base64Data = data.image;
+ if (!base64Data.includes(",")) {
+ throw new Error(`无效的 Base64 数据,产品ID: ${productId}`);
+ }
+
+ const byteCharacters = atob(base64Data.split(",")[1]);
+ const byteNumbers = new Array(byteCharacters.length);
+ for (let i = 0; i < byteCharacters.length; i++) {
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
+ }
+ const byteArray = new Uint8Array(byteNumbers);
+ return new Blob([byteArray], { type: "image/png" });
+ };
+
+ const handleCopyAll = async () => {
+ try {
+ const customerName = record.客户?.姓名 ?? "未知";
+ const tail = record.客户?.电话
+ ? record.客户.电话.slice(-4)
+ : "****";
+ const remark = text || "无备注";
+
+ // 生成所有产品的文本内容
+ let productText = "";
+ if (products.length > 0) {
+ products.forEach((product: any, index: number) => {
+ productText += `【产品${index + 1}】${product.名称 || "未知产品"}\n`;
+ });
+ } else {
+ productText = "【产品】无产品\n";
+ }
+
+ // 改为多行格式,包含所有产品
+ const combinedText = `【客户】${customerName}-${tail}\n${productText}【备注】${remark}`;
+
+ if (products.length > 0) {
+ try {
+ // 仅复制第一款产品的图片,避免复制太多内容导致剪贴板问题
+ const productId = products[0]._id;
+ const blob = await fetchBase64ImageAsBlob(productId);
+
+ const clipboardItems: Record = {
+ "text/plain": new Blob([combinedText], {
+ type: "text/plain",
+ }),
+ };
+ clipboardItems[blob.type] = blob;
+
+ const clipboardItem = new ClipboardItem(clipboardItems);
+ await navigator.clipboard.write([clipboardItem]);
+ message.success("客户信息、所有产品名称、备注和图片已复制");
+ } catch (imageError) {
+ // 如果图片复制失败,至少保证文本信息被复制
+ console.error("图片复制失败,仅复制文本信息:", imageError);
+ await navigator.clipboard.writeText(combinedText);
+ message.success("客户信息、所有产品名称和备注已复制");
+ }
+ } else {
+ // 没有产品时只复制文本
+ await navigator.clipboard.writeText(combinedText);
+ message.success("客户信息、备注已复制");
+ }
+ } catch (err) {
+ console.error("复制失败:", err);
+ message.error("复制信息失败");
+ }
+ };
+
+ return (
+
+ {/* 按钮区:上下排列 */}
+
+ {/* 一键复制按钮(活力橙色) */}
+
+
+
+
+
+ {/* 备注内容 */}
+
+
+ {text || "无备注"}
+
+
+
+ );
+ },
+ },
+ //使用替换,显示产品信息
+ {
+ title: "产品信息",
+ width: 420, // 增加宽度以适应3个产品 (130px * 3 + 间距)
+ dataIndex: "产品",
+ key: "productImage",
+ render: (products: any[], record: ISalesRecord) => {
+ return (
+
+ );
+ },
+ },
+ {
+ title: "财务信息",
+ key: "financialInfo",
+ width: 180, // 适当调整宽度
+ render: (record: any) => {
+ const paymentPlatform = record.收款平台?.名称 ?? "未知";
+ const receivedAmount = record.收款金额
+ ? record.收款金额.toFixed(2)
+ : "0.00";
+ const paymentStatus = record.收款状态 ?? "未知";
+ const receivableAmount = record.应收金额
+ ? record.应收金额.toFixed(2)
+ : "0.00";
+ const unreceivedAmount = record.待收款
+ ? record.待收款.toFixed(2)
+ : "0.00";
+ const receivedPendingAmount = record.待收已收
+ ? record.待收已收.toFixed(2)
+ : "0.00"; // 待收已收字段
+
+ return (
+
+
+ {paymentPlatform}
+
+ 应收: ¥{receivableAmount}
+
+
+
+ {paymentStatus}
+
+ 收款: ¥{receivedAmount}
+
+
+
+ 余额抵用:
+
+ ¥{record.余额抵用?.金额 ?? 0}
+
+
+
+
优惠券:
+
+ {record.优惠券?.map((coupon: ICoupon) => (
+
+ 类型:{coupon._id.优惠券类型}
+
+ 金额:{coupon._id.金额}
+
+ 折扣:{coupon._id.折扣}
+ >
+ }
+ >
+
+ {coupon._id.优惠券类型}
+
+
+ ))}
+
+
+
+
+
+ 0
+ ? "#ff4d4f"
+ : "inherit", // 只有大于0时才显示红色
+ }}
+ >
+ 待收:¥{unreceivedAmount}
+
+
+
+ 已收:
+
+ ¥{receivedPendingAmount}
+
+
+
+
+ );
+ },
+ },
+ ];
+
+ // 如果用户不是财务角色,添加操作列
+ if (isNotFinanceRole) {
+ baseColumns.push({
+ title: "操作",
+ key: "action",
+ align: "center",
+ fixed: "right",
+ width: 160,
+ render: (_: any, record: ISalesRecord) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+ });
+ }
+
+ return baseColumns;
+ }, [isNotFinanceRole, isAdmin, userInfo, dateSort, sortDirection]);
+
+ // 导出Excel的函数
+ const exportToExcel = () => {
+ if (filteredRecords.length === 0) {
+ message.warning('没有可导出的数据');
+ return;
+ }
+
+ message.loading({ content: '正在准备导出数据...', key: 'exporting', duration: 0 });
+
+ try {
+ // 准备导出数据
+ const exportData = filteredRecords.map((record, index) => {
+ // 处理产品信息 - 合并成一个字符串
+ const productsText = record.产品?.map(product =>
+ `${product.名称 || '未知'}`
+ ).join(', ') || '无产品';
+
+ // 计算成交周期
+ const transactionDate = record.成交日期 ? new Date(record.成交日期) : new Date();
+ const addFansDate = record.客户?.加粉日期 ? new Date(record.客户.加粉日期) : null;
+ const diffDays = addFansDate ? Math.ceil(
+ Math.abs(transactionDate.getTime() - addFansDate.getTime()) / (1000 * 60 * 60 * 24)
+ ) : '未知';
+
+ // 格式化日期
+ const formatDate = (dateStr: string | undefined | Date): string => {
+ if (!dateStr) return '未知';
+ try {
+ const date = dateStr instanceof Date ? dateStr : new Date(dateStr);
+ if (isNaN(date.getTime())) return '无效日期';
+ return date.toLocaleDateString('zh-CN');
+ } catch {
+ return '无效日期';
+ }
+ };
+
+ // 处理电话号码 - 只保留尾号
+ const phoneNumber = record.客户?.电话
+ ? `${record.客户.电话.slice(-4)}`
+ : '未知';
+
+ // 计算售后产品金额
+ let afterSalesAmount = 0;
+ if (record.售后记录 && record.售后记录.length > 0 && record.产品 && record.产品.length > 0) {
+ const afterSalesProductIds = new Set(
+ record.售后记录.flatMap(afterSales =>
+ afterSales.原产品.map(productId => String(productId))
+ )
+ );
+
+ record.产品.forEach(product => {
+ if (afterSalesProductIds.has(String(product._id))) {
+ afterSalesAmount += (product.售价 || 0);
+ }
+ });
+ }
+
+ // 计算产品总成本(成本价+包装费+运费)
+ let totalProductCost = 0;
+ if (record.产品 && record.产品.length > 0) {
+ totalProductCost = record.产品.reduce((sum, product) => {
+ const costPrice = product.成本?.成本价 || 0;
+ const packagingFee = product.成本?.包装费 || 0;
+ const shippingFee = product.成本?.运费 || 0;
+ return sum + costPrice + packagingFee + shippingFee;
+ }, 0);
+ }
+
+ // 返回导出的行数据
+ return {
+ '序号': index + 1,
+ '客户姓名': record.客户?.姓名 || '未知',
+ '电话尾号': phoneNumber,
+ '成交日期': formatDate(record.成交日期),
+ '加粉日期': formatDate(record.客户?.加粉日期),
+ '成交周期(天)': diffDays,
+ '导购': record.导购?.姓名 || '未知',
+ '账号编号': record.订单来源?.账号编号 || '未知',
+ '产品信息': productsText,
+ '应收金额': record.应收金额 || 0,
+ '收款金额': record.收款金额 || 0,
+ '待收金额': record.待收款 || 0,
+ '待收已收': record.待收已收 || 0,
+ '收款平台': record.收款平台?.名称 || '未知',
+ '收款状态': record.收款状态 || '未知',
+ '总成本': totalProductCost,
+ '售后金额': afterSalesAmount,
+ '备注': record.备注 || '无',
+ '创建时间': record.createdAt ? formatDate(new Date(record.createdAt)) : '未知',
+ };
+ });
+
+ // 创建工作簿和工作表
+ const ws = XLSX.utils.json_to_sheet(exportData);
+ const wb = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(wb, ws, '销售记录');
+
+ // 设置列宽 (大致估计,可能需要调整)
+ const colWidths = [
+ { wch: 5 }, // 序号
+ { wch: 10 }, // 客户姓名
+ { wch: 10 }, // 电话尾号
+ { wch: 10 }, // 成交日期
+ { wch: 10 }, // 加粉日期
+ { wch: 8 }, // 成交周期
+ { wch: 10 }, // 导购
+ { wch: 10 }, // 账号编号
+ { wch: 40 }, // 产品信息
+ { wch: 10 }, // 应收金额
+ { wch: 10 }, // 收款金额
+ { wch: 10 }, // 待收金额
+ { wch: 10 }, // 待收已收
+ { wch: 10 }, // 收款平台
+ { wch: 10 }, // 收款状态
+ { wch: 10 }, // 总成本
+ { wch: 10 }, // 售后金额
+ { wch: 20 }, // 备注
+ { wch: 20 }, // 创建时间
+ ];
+ ws['!cols'] = colWidths;
+
+ // 生成Excel文件并下载
+ const now = new Date();
+ const dateStr = `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}`;
+ const fileName = `销售记录导出_${dateStr}.xlsx`;
+
+ // 使用file-saver保存文件
+ const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
+ const blob = new Blob([wbout], { type: 'application/octet-stream' });
+ saveAs(blob, fileName);
+
+ message.success({ content: '导出成功!', key: 'exporting' });
+ } catch (error) {
+ console.error('导出Excel失败:', error);
+ message.error({ content: '导出失败,请重试', key: 'exporting' });
+ }
+ };
+
+ return (
+
+
{
+ // 计算统计数据
+ const calculateStats = () => {
+ // 总记录数量
+ const recordCount = filteredRecords.length;
+
+ // 总出单数量(所有产品数量的总和)
+ const orderCount = filteredRecords.reduce((sum, record) => {
+ // 如果产品数组存在且有长度,就累加产品数量
+ // 如果产品数组不存在或为空,就按1计算
+ if (record.产品 && record.产品.length > 0) {
+ return sum + record.产品.length;
+ }
+ return sum + 1; // 如果没有产品信息,按一单计算
+ }, 0);
+
+ // 计算不重复的客户数量(根据客户电话号码去重)
+ const uniqueCustomers = new Set();
+ filteredRecords.forEach(record => {
+ if (record.客户?.电话) {
+ uniqueCustomers.add(record.客户.电话);
+ }
+ });
+ const customerCount = uniqueCustomers.size;
+
+ // 计算总销售额(应收金额)
+ const totalSales = filteredRecords.reduce((sum, record) => {
+ return sum + (record.应收金额 || 0);
+ }, 0);
+
+ // 计算总成本
+ const totalCost = filteredRecords.reduce((sum, record) => {
+ if (record.产品 && record.产品.length > 0) {
+ // 累加每个产品的总成本(成本价+包装费+运费)
+ const productsCost = record.产品.reduce((costSum, product) => {
+ // 计算单个产品的总成本 = 成本价 + 包装费 + 运费
+ const costPrice = product.成本?.成本价 || 0;
+ const packagingFee = product.成本?.包装费 || 0;
+ const shippingFee = product.成本?.运费 || 0;
+ const totalProductCost = costPrice + packagingFee + shippingFee;
+
+ // 假设每个产品数量为1,如果有产品数量字段,应该乘以数量
+ return costSum + totalProductCost;
+ }, 0);
+ return sum + productsCost;
+ }
+ return sum;
+ }, 0);
+
+ // 计算毛利率 = (销售额-成本)/销售额 * 100%
+ const profitRate = totalSales > 0
+ ? ((totalSales - totalCost) / totalSales * 100).toFixed(1)
+ : '0.0';
+
+ // 计算客单价 = 总销售额/客户数量
+ const customerPrice = customerCount > 0
+ ? (totalSales / customerCount).toFixed(2)
+ : '0.00';
+
+ // 计算件单价 = 总销售额/总出单数量
+ const unitPrice = orderCount > 0
+ ? (totalSales / orderCount).toFixed(2)
+ : '0.00';
+
+ // 计算售后产品金额
+ let afterSalesAmount = 0;
+ filteredRecords.forEach(record => {
+ if (record.售后记录 && record.售后记录.length > 0 && record.产品 && record.产品.length > 0) {
+ const afterSalesProductIds = new Set(
+ record.售后记录.flatMap(afterSales =>
+ afterSales.原产品.map(productId => String(productId))
+ )
+ );
+
+ record.产品.forEach(product => {
+ if (afterSalesProductIds.has(String(product._id))) {
+ afterSalesAmount += (product.售价 || 0);
+ }
+ });
+ }
+ });
+
+ return {
+ orderCount,
+ customerCount,
+ totalSales,
+ totalCost,
+ profitRate,
+ recordCount,
+ customerPrice,
+ unitPrice,
+ afterSalesAmount
+ };
+ };
+
+ // 获取统计数据
+ const stats = calculateStats();
+
+ // 渲染筛选状态标签
+ const renderFilterStatus = () => {
+ if (transactionDateRange[0] || addFansDateRange[0]) {
+ return (
+
+
+ 共: {stats.recordCount} / {salesRecords.length} 条记录
+
+
+
+ );
+ }
+ return null;
+ };
+
+ return (
+
+
+
+ 成交日期:
+ {
+ if (dateStrings[0] && dateStrings[1]) {
+ // 设置开始日期为当天的00:00:00
+ const startDate = new Date(dateStrings[0]);
+ startDate.setHours(0, 0, 0, 0);
+
+ // 设置结束日期为当天的23:59:59,确保包含当天的所有记录
+ const endDate = new Date(dateStrings[1]);
+ endDate.setHours(23, 59, 59, 999);
+
+ setTransactionDateRange([startDate, endDate]);
+ } else {
+ setTransactionDateRange([null, null]);
+ }
+ }}
+ />
+
+
+ 加粉日期:
+ {
+ if (dateStrings[0] && dateStrings[1]) {
+ // 设置开始日期为当天的00:00:00
+ const startDate = new Date(dateStrings[0]);
+ startDate.setHours(0, 0, 0, 0);
+
+ // 设置结束日期为当天的23:59:59,确保包含当天的所有记录
+ const endDate = new Date(dateStrings[1]);
+ endDate.setHours(23, 59, 59, 999);
+
+ setAddFansDateRange([startDate, endDate]);
+ } else {
+ setAddFansDateRange([null, null]);
+ }
+ }}
+ />
+
+
+ {renderFilterStatus()}
+
+ {/* 添加统计信息 */}
+
+
+ }>
+ 客户: {stats.customerCount}
+
+
+
+ }>
+ 记录/出单: {stats.recordCount}/{stats.orderCount}
+
+
+
+
+ 销售额: ¥{stats.totalSales.toFixed(2)}
+
+
+
+
+ 成本: ¥{stats.totalCost.toFixed(2)}
+
+
+
+
+ 毛利: ¥{(stats.totalSales - stats.totalCost).toFixed(2)}
+
+
+
+
+ 毛利率: {stats.profitRate}%
+
+
+
+ }>
+ 客单价: ¥{stats.customerPrice}
+
+
+
+ }>
+ 件单价: ¥{stats.unitPrice}
+
+
+
+
+ 售后金额: ¥{stats.afterSalesAmount.toFixed(2)}
+
+
+
+ {/* 添加导出按钮 */}
+ }
+ onClick={exportToExcel}
+ style={{ marginLeft: 8 }}
+ >
+ 导出筛选
+
+
+
+
+ );
+ }}
+ scroll={{
+ y: `calc(100vh - 250px)`, // 适当调整高度
+ x: 'max-content'
+ }}
+ pagination={{
+ position: ['bottomRight'],
+ pageSize: 100,
+ showSizeChanger: true,
+ pageSizeOptions: ["10", "20", "50", "100"],
+ showTotal: (total) => `共 ${total} 条记录`,
+ size: 'small'
+ }}
+ size="small"
+ loading={loading}
+ columns={columns}
+ dataSource={filteredRecords} // 使用筛选后的记录
+ rowKey="_id"
+ // 添加行渲染优化
+ rowClassName={(_, index) => (index % 2 === 0 ? 'even-row' : 'odd-row')}
+ />
+ }>
+ {isModalVisible && (
+ setIsModalVisible(false)}
+ record={currentRecord}
+ />
+ )}
+ {afterSalesVisible && (
+ setAfterSalesVisible(false)}
+ record={currentRecord}
+ type={afterSalesType!}
+ />
+ )}
+ {isShipModalVisible && (
+
+ )}
+
+
+ );
+};
+
+export default SalesPage;
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 5cdab48..244657f 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -18,14 +18,7 @@
/* ==================== 防闪烁优化 + 分层过渡策略 ==================== */
/* 关键代码行注释:防止SSR水合过程中的主题闪烁,采用分层过渡策略 */
-html {
- background-color: #f6f9fc;
- transition: background-color 0.4s ease;
-}
-
-html.dark {
- background-color: #0a1128;
-}
+/* 注意:主要的背景设置已移动到下方的html选择器中,这里仅保留基础设置 */
/* ==================== 全局变量系统 ==================== */
/* 关键代码行注释:统一在一个:root中定义所有全局变量,提高可维护性 */
@@ -154,7 +147,17 @@ body {
transition: var(--transition-slow);
}
-/* 关键代码行注释:正确的背景应用方式,将背景设置在body元素上 */
+/* 关键代码行注释:将背景设置在html元素上,确保背景完全固定不随滚动 */
+html {
+ /* 关键代码行注释:使用变量方式应用背景,确保背景图片能正确显示 */
+ background-color: var(--bg-primary);
+ background-image: var(--bg-gradient);
+ background-attachment: fixed;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+ transition: var(--transition-slow);
+}
+
body {
min-height: 100vh;
line-height: 1.6;
@@ -162,13 +165,10 @@ body {
font-weight: 400;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
- /* 关键代码行注释:使用变量方式应用背景,确保背景图片能正确显示 */
+ /* 关键代码行注释:body元素只设置文字颜色,不设置背景 */
color: var(--text-primary);
- background-color: var(--bg-primary);
- background-image: var(--bg-gradient);
- background-attachment: fixed;
- background-size: 100% 100%;
- background-repeat: no-repeat;
+ /* 关键代码行注释:移除body的背景设置,让html的背景透过 */
+ background: transparent;
}
a {
diff --git a/src/utils/getAccessToken.ts b/src/utils/getAccessToken.ts
new file mode 100644
index 0000000..850b8cd
--- /dev/null
+++ b/src/utils/getAccessToken.ts
@@ -0,0 +1,35 @@
+// src\utils\getAccessToken.ts
+import querystring from 'querystring';
+
+export async function getAccessToken() {
+ // 从环境变量获取 API URL、partnerID 和 secret
+ const url = process.env.NEXT_PUBLIC_API_URL_OAUTH || 'https://sfapi.sf-express.com/oauth2/accessToken';
+ const partnerID = process.env.PARTNER_ID || 'defaultPartnerID';
+ const secret = process.env.SECRET || 'defaultSecret';
+
+ const data = querystring.stringify({
+ partnerID,
+ secret,
+ grantType: 'password'
+ });
+
+ try {
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: data
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP Error: ${response.status}`);
+ }
+
+ const responseData = await response.json();
+ // 在 getAccessToken 中
+ //console.log("Returning accessToken:", responseData.accessToken);
+ return responseData.accessToken;
+ } catch (error) {
+ console.error('Error getting access token:', error);
+ throw error; // 抛出错误让调用者处理
+ }
+}
diff --git a/src/utils/querySFExpress.ts b/src/utils/querySFExpress.ts
new file mode 100644
index 0000000..9b2b9a5
--- /dev/null
+++ b/src/utils/querySFExpress.ts
@@ -0,0 +1,48 @@
+// src\utils\querySFExpress.ts
+import querystring from 'querystring';
+import { getAccessToken } from './getAccessToken'; // 确保路径正确
+
+export async function querySFExpress(trackingNumber: string, phoneLast4Digits: string) {
+ // 在 querySFExpress 中
+ const accessToken = await getAccessToken();
+ //console.log("Received accessToken in querySFExpress:", accessToken);
+ const partnerID = 'YPD607MO';
+ const reqUrl = 'https://bspgw.sf-express.com/std/service';
+ const requestID = crypto.randomUUID();
+ const serviceCode = "EXP_RECE_SEARCH_ROUTES";
+ const timestamp = Date.now().toString();
+ const msgData = {
+ language: "zh-CN",
+ trackingType: "1",
+ trackingNumber: [trackingNumber],
+ methodType: "1",
+ checkPhoneNo: phoneLast4Digits
+ };
+ //console.log(`Querying SF Express with: accessToken=${accessToken}, trackingNumber=${trackingNumber}, phoneLast4Digits=${phoneLast4Digits}`);
+ const data = querystring.stringify({
+ partnerID,
+ requestID,
+ serviceCode,
+ timestamp,
+ accessToken,
+ msgData: JSON.stringify(msgData)
+ });
+
+ try {
+ const response = await fetch(reqUrl, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: data
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP Error: ${response.status}`);
+ }
+
+ const responseData = await response.json();
+ return responseData;
+ } catch (error) {
+ console.error('Error querying SF Express:', error);
+ throw error;
+ }
+}