0607.5
Some checks failed
Next.js CI/CD 流水线 / deploy (push) Failing after 21s

This commit is contained in:
2025-06-07 02:01:27 +08:00
parent eb79e416db
commit 4f6acafb2a
2 changed files with 174 additions and 85 deletions

View File

@@ -0,0 +1,22 @@
import { NextApiRequest, NextApiResponse } from 'next';
import connectDB from '@/utils/connectDB';
import { CustomerPaymentCode } from '@/models';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { id } = req.query;
try {
const paymentCode = await CustomerPaymentCode.findById(id);
if (!paymentCode) {
return res.status(404).json({ message: '收款码未找到' });
}
res.status(200).send(paymentCode.);
} catch (error) {
console.error('Error fetching payment code image:', error);
res.status(500).json({ message: '服务器错误' });
}
};
export default connectDB(handler);

View File

@@ -6,7 +6,7 @@
*/
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { Table, Button, Card, Tag, Tooltip
, Input, Space, DatePicker, App } from 'antd';
, Input, Space, DatePicker, App, Typography } from 'antd';
import { IAfterSalesRecord } from '@/models/types';
import dayjs from 'dayjs';
import { useUserInfo } from '@/store/userStore';
@@ -15,7 +15,7 @@ import { useRouter } from 'next/router';
import { CheckCircleOutlined, ClockCircleOutlined, DownloadOutlined, FieldTimeOutlined, IdcardOutlined, MobileOutlined, SyncOutlined, UserOutlined, WechatOutlined
, SearchOutlined
} from '@ant-design/icons';
import { IconButton, Iconify } from '@/components/icon';
import { Iconify } from '@/components/icon';
import PaymentCodeImageComponent from '@/pages/components/PaymentCodeImage';
import MultiAfterSalesModal from './MultiAfterSalesModal';
import ShipModal from './ship-modal';
@@ -23,6 +23,36 @@ import EditAfterSalesModal from './EditAfterSalesModal'; // 引入编辑售后
import ProductCardList from '@/components/product/ProductCardList'; // 引入产品卡片列表组件
import MyTooltip from '@/components/tooltip/MyTooltip';
const { Paragraph } = Typography;
// 通用样式
const COMMON_STYLES = {
tagContainer: { margin: 0 },
flexColumn: { display: "flex", flexDirection: "column" as const, alignItems: "flex-start", gap: "4px" },
flexRow: { display: "flex", gap: "8px" }
};
// 工具函数
const formatDate = (dateString: string): 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 getCustomerTagColor = (unreceivedAmount: number): string => {
return unreceivedAmount > 0 ? "#cd201f" : "blue";
};
const buildCustomerAddress = (address: any): string => {
if (!address) return "未知";
return `${address. ?? ""} ${address. ?? ""} ${address. ?? ""} ${address. ?? ""}`.trim();
};
// 导出Excel相关库
//import * as XLSX from 'xlsx';
//import { saveAs } from 'file-saver';
@@ -212,91 +242,104 @@ const AfterSaleRecordPage = () => {
{
title: '客户信息',
width: 160,
align: "center",
key: '客户信息',
// 增加filterDropdown用于尾号查询
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="输入手机尾号"
value={selectedKeys[0] ? String(selectedKeys[0]) : ''}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => confirm()}
size="small"
>
</Button>
<Button
onClick={() => {
clearFilters?.(); // 使用可选链,避免类型错误
confirm();
}}
size="small"
>
</Button>
</Space>
</div>
),
filterIcon: filtered => (
<SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
),
onFilter: (value, record: IAfterSalesRecord) => {
const phoneTail = record..?.
? record....slice(-4)
: '';
const searchVal = String(value);
return phoneTail.includes(searchVal);
},
// 增加filterDropdown用于尾号查询
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="输入手机尾号"
value={selectedKeys[0] ? String(selectedKeys[0]) : ''}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => confirm()}
size="small"
>
</Button>
<Button
onClick={() => {
clearFilters?.(); // 使用可选链,避免类型错误
confirm();
}}
size="small"
>
</Button>
</Space>
</div>
),
filterIcon: filtered => (
<SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
),
onFilter: (value, record: IAfterSalesRecord) => {
const phoneTail = record..?.
? record....slice(-4)
: '';
const searchVal = String(value);
return phoneTail.includes(searchVal);
},
render: (record: IAfterSalesRecord) => {
// 将地址的各个部分拼接成一个字符串
const address = record..?.
? `${record.... ?? ''} ${record.... ?? ''} ${record.... ?? ''} ${record.... ?? ''}`
: '未知';
const customerInfo = `姓名:${record..?. ?? '未知'}\n电话${record..?. ?? '未知'}\n地址${address}`;
const customerName = record..?. ?? '未知';
//显示账号编号
const address = buildCustomerAddress(record..?.);
const customerName = record..?. ?? "未知";
const unreceivedAmount = parseFloat((record.. || 0).toString());
const customerTagColor = getCustomerTagColor(unreceivedAmount);
const accountNumber = record..?. ?? '未知';
// 准备复制文本
const customerCopyText = `姓名:${customerName}\n电话${record..?. ?? "未知"}\n地址${address}`;
return (
<div className="flex items-center">
{isAdmin && (
<IconButton
className="text-gray"
onClick={async () => {
try {
await navigator.clipboard.writeText(customerInfo);
message.success(`客户 ${customerName} 信息复制成功!`);
} catch (error) {
console.error('客户信息复制失败:', error);
message.error('客户信息复制失败');
}
}}
>
<Iconify icon="eva:copy-fill" size={20} />
</IconButton>
)}
<MyTooltip
color="white"
title={record..?. ?? '未知'}>
<div>
<Tag icon={<UserOutlined />} color="">{record..?. ?? '未知'}</Tag>
<Tag icon={<FieldTimeOutlined />} color="">{record.. ? new Date(record..).toLocaleDateString() : '未知'}</Tag>
<Tag icon={<MobileOutlined />} color="">{record..?. ? `****${record....slice(-4)}` : '未知'}</Tag>
{/*<div>状态:{record.销售记录?.订单状态?.[0]}</div>*/}
{/* 将最后两个 Tag 放在一个 span 中,并使用 no-wrap 样式避免自动换行 */}
<span style={{ whiteSpace: 'nowrap' }}>
<Tag icon={<IdcardOutlined />} color="">
{record.?.?. ?? '未知'}
</Tag>
<Tag icon={<WechatOutlined />} color="green">
<div style={{ display: "flex" }}>
{/* 左侧复制按钮 */}
<div style={{ marginRight: 8 }}>
{isAdmin && (
<Paragraph
copyable={{
text: customerCopyText,
onCopy: () => message.success(`客户 ${customerName} 信息复制成功!`),
tooltips: ['复制客户信息', '复制成功'],
icon: <Iconify icon="eva:copy-fill" size={16} />
}}
style={{ margin: 0, lineHeight: 0 }}
>
{/* 空内容,只显示复制按钮 */}
</Paragraph>
)}
</div>
{/* 右侧标签信息,按用户要求布局 */}
<MyTooltip color="white" title={customerName} placement="topLeft">
<div style={COMMON_STYLES.flexColumn}>
{/* 客户名字 */}
<Tag icon={<UserOutlined />} color={customerTagColor} style={COMMON_STYLES.tagContainer}>
{customerName}
</Tag>
{/* 客户尾号 */}
<Tag icon={<MobileOutlined />} color="green" style={COMMON_STYLES.tagContainer}>
{record..?. ? `${record....slice(-4)}` : "未知"}
</Tag>
{/* 成交日期 */}
<Tag icon={<FieldTimeOutlined />} color="purple" style={COMMON_STYLES.tagContainer}>
{record.. ? formatDate(record...toString()) : "未知"}
</Tag>
{/* accountNumber+导购姓名显示在一行 */}
<div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
<Tag icon={<WechatOutlined />} color="geekblue" style={COMMON_STYLES.tagContainer}>
{accountNumber}
</Tag>
</span>
<Tag icon={<IdcardOutlined />} color="cyan" style={COMMON_STYLES.tagContainer}>
{record.?.?. ?? '未知'}
</Tag>
</div>
</div>
</MyTooltip>
</div>
@@ -306,10 +349,21 @@ const AfterSaleRecordPage = () => {
{
title: '售后产品',
dataIndex: '原产品',
//width: 260,
width: 260, // 设置固定宽度,防止占用其他列空间
key: 'originalProducts',
render: (products: any[], record: IAfterSalesRecord) => {
return <ProductCardList products={products} record={record} />;
return (
<div style={{
width: '100%',
maxWidth: '270px',
overflowX: 'auto',
overflowY: 'hidden',
scrollbarWidth: 'thin',
scrollbarColor: '#d9d9d9 transparent',
}}>
<ProductCardList products={products} record={record} />
</div>
);
}
},
@@ -333,6 +387,7 @@ const AfterSaleRecordPage = () => {
{
title: '售后信息',
key: 'reasonAndType',
width: 200,
//筛选售后类型
filters: [
{ text: '退货', value: '退货' },
@@ -363,9 +418,21 @@ const AfterSaleRecordPage = () => {
{
title: '替换产品',
dataIndex: '替换产品',
width: 260, // 设置固定宽度,防止占用其他列空间
key: 'replacementProducts',
render: (products: any[], record: IAfterSalesRecord) => {
return <ProductCardList products={products} record={record} />;
return (
<div style={{
width: '100%',
maxWidth: '270px',
overflowX: 'auto',
overflowY: 'hidden',
scrollbarWidth: 'thin',
scrollbarColor: '#d9d9d9 transparent',
}}>
<ProductCardList products={products} record={record} />
</div>
);
}
},
//财务信息
@@ -759,7 +826,7 @@ const AfterSaleRecordPage = () => {
sticky
scroll={{
y: `calc(100vh - 300px)`,
//x: 'max-content'
x: 'max-content' // 启用水平滚动,确保表格内容不会被挤压
}}
//pagination={false} // 禁用分页
pagination={{