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