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

This commit is contained in:
2025-06-07 01:41:58 +08:00
parent 7c11b0e57f
commit eb79e416db
39 changed files with 3177 additions and 25 deletions

View File

@@ -0,0 +1,339 @@
/**
* 文件: MultiAfterSalesModal.tsx
* 作者: 阿瑞
* 功能: 多重售后操作模态框组件
* 版本: v2.0.0 - 使用 fetch 替换 axios
*/
import React, { useEffect, useState } from 'react';
import { Modal, Form, Select, DatePicker, Button, InputNumber, Input, App } from 'antd';
import { IAfterSalesRecord, IPaymentPlatform, IProduct } from '@/models/types';
import dayjs from 'dayjs';
import { useUserInfo } from '@/store/userStore';
import ProductImage from '@/components/product/ProductImage';
import AddProductComponent from '../sale/components/AddProductComponent';
import { CloseOutlined } from '@ant-design/icons';
interface MultiAfterSalesModalProps {
visible: boolean;
onOk: () => void;
onCancel: () => void;
record: IAfterSalesRecord | null; // 传递当前的售后记录
type: '退货' | '换货' | '补发' | '补差';
}
const { Option } = Select;
const MultiAfterSalesModal: React.FC<MultiAfterSalesModalProps> = ({ visible, onOk, onCancel, record, type }) => {
const { message } = App.useApp(); // 使用 App.useApp 获取 message 实例
const [form] = Form.useForm();
const [paymentPlatforms, setPaymentPlatforms] = useState<IPaymentPlatform[]>([]);
const [selectedProducts, setSelectedProducts] = useState<IProduct[]>([]);
const [products, setProducts] = useState<IProduct[]>([]);
const userInfo = useUserInfo();
const [paymentCode, setPaymentCode] = useState<string | null>(null);
//获取收支平台
const fetchPayPlatforms = async (teamId: string) => {
try {
const response = await fetch(`/api/backstage/payment-platforms?teamId=${teamId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setPaymentPlatforms(data.platforms || []);
} catch (error: unknown) {
console.error('加载平台数据失败:', error);
message.error('加载平台数据失败');
setPaymentPlatforms([]);
}
};
// 获取产品数据
const fetchProducts = async (teamId: string) => {
try {
const response = await fetch(`/api/backstage/products?teamId=${teamId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setProducts(data.products || []);
} catch (error: unknown) {
console.error('加载产品数据失败:', error);
message.error('加载产品数据失败');
}
};
useEffect(() => {
form.resetFields();
setSelectedProducts([]);
if (record && userInfo.?._id) {
const teamId = userInfo.._id;
fetchPayPlatforms(teamId);
fetchProducts(teamId); // 加载产品列表
form.setFieldsValue({
前一次售后: record._id, // 使用当前售后记录的ID作为前一次售后
类型: type,
日期: dayjs(),
});
}
}, [record, type, userInfo.?._id]);
const handleOk = async () => {
try {
const values = await form.validateFields();
if (!record) {
message.error('未选择售后记录,无法创建新的售后记录');
return;
}
const replacementProductIds = selectedProducts.map(product => product._id);
let paymentCodeId = null;
if (paymentCode) {
const response = await fetch('/api/backstage/sales/aftersale/uploadPaymentCode', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ 收款码: paymentCode }),
});
if (!response.ok) {
throw new Error('收款码上传失败');
}
const paymentCodeResponse = await response.json();
paymentCodeId = paymentCodeResponse.paymentCodeId;
}
const afterSalesData = {
销售记录: record.销售记录._id,
前一次售后: record._id, // 传递前一次售后的 ID
类型: type,
原产品: values.原产品, // 传递原产品的 ID 数组
替换产品: replacementProductIds, // 传递替换产品的 ID 数组
团队: userInfo.团队?._id,
日期: values.日期.toISOString(),
收款码: paymentCodeId, // 添加收款码ID字段
原因: values.原因, // 售后原因
收支类型: values.收支类型,
收支平台: values.收支平台,
收支金额: values.收支金额,
待收: values.待收,
备注: values.备注,
};
const response = await fetch('/api/backstage/sales/aftersale', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(afterSalesData),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
message.success(`${type}操作成功`);
onOk();
} catch (error: unknown) {
console.error(`${type}操作失败:`, error);
message.error(`${type}操作失败`);
}
};
const [isProductModalVisible, setIsProductModalVisible] = useState(false);
// 修改:新增产品成功后的回调函数,更新选中的产品状态
const handleAddProductSuccess = (newProduct: IProduct) => {
setSelectedProducts(prevProducts => [...prevProducts, newProduct]); // 更新选中的产品
setIsProductModalVisible(false); // 关闭产品模态框
};
const handleProductSelectChange = (selectedProductIds: string[]) => {
const selected = products.filter(product => selectedProductIds.includes(product._id));
setSelectedProducts(selected);
};
const renderAdditionalFields = () => {
if (type === '换货' || type === '补发') {
return (
<div>
<div style={{ marginTop: 16 }}>
{selectedProducts.map(product => (
<div key={product._id} style={{ marginBottom: 8 }}>
<ProductImage productId={product._id} alt={product.} width={72} height={72} />
<span style={{ marginLeft: 8 }}>{product.}</span>
</div>
))}
</div>
<Button
type="primary"
onClick={() => setIsProductModalVisible(true)}
>
{type}
</Button>
<AddProductComponent
visible={isProductModalVisible}
onClose={() => setIsProductModalVisible(false)}
onSuccess={handleAddProductSuccess}
/>
<Form.Item name="替换产品" label="选择产品">
<Select mode="multiple" placeholder="请选择产品" onChange={handleProductSelectChange}>
{products.map(product => (
<Select.Option key={product._id} value={product._id}>
{product.}{product.}{product.}{product.}{product.}{product.}{product.}
</Select.Option>
))}
</Select>
</Form.Item>
</div>
);
}
return null;
};
const handlePaste = (event: React.ClipboardEvent<HTMLDivElement>) => {
const items = event.clipboardData.items;
for (const item of items) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
if (file) {
const reader = new FileReader();
reader.onload = async (e) => {
const img = new Image();
img.src = e.target?.result as string;
img.onload = async () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = img.width / 2;
const height = img.height / 2;
canvas.width = width;
canvas.height = height;
ctx?.drawImage(img, 0, 0, width, height);
const compressedDataUrl = canvas.toDataURL('image/jpeg', 0.8);
setPaymentCode(compressedDataUrl);
};
};
reader.readAsDataURL(file);
}
}
}
};
return (
<Modal
open={visible}
title={`创建${type}记录`}
onCancel={onCancel}
footer={[
<Button key="cancel" onClick={onCancel}>
</Button>,
<Button key="submit" type="primary" onClick={handleOk}>
</Button>,
]}
width="76vw"
style={{ top: 20 }}
>
<Form form={form} layout="vertical">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="col-span-1">
<h3 className="font-bold"></h3>
<Form.Item name="原产品" label="上一次售后产品" rules={[{ required: true, message: '请选择需要售后的产品' }]}>
<Select mode="multiple" placeholder="请选择原订单中的产品">
{record?..map(product => (
<Select.Option key={product._id} value={product._id}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ProductImage productId={product._id} alt={product.} width={40} height={40} />
<span style={{ marginLeft: 8 }}>{product.}</span>
</div>
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="售后原因" name="原因" rules={[{ required: true, message: '请选择原因' }]}>
<Select>
<Option value="发货原因"></Option>
<Option value="产品质量"></Option>
<Option value="择优选购"></Option>
<Option value="七日退换">退</Option>
<Option value="货不对板"></Option>
<Option value="运输损坏"></Option>
<Option value="配件缺失"></Option>
</Select>
</Form.Item>
</div>
<div className="col-span-1">
<h3 className="font-bold"></h3>
{renderAdditionalFields()}
<br />
<Form.Item name="日期" label="售后日期" rules={[{ required: true, message: '请选择售后日期' }]}>
<DatePicker style={{ width: '100%' }} />
</Form.Item>
<Form.Item name="备注" label="备注">
<Input.TextArea
//默认显示4行最多6行
autoSize={{ minRows: 4, maxRows: 6 }}
placeholder="备注信息" />
</Form.Item>
</div>
<div className="col-span-1">
<h3 className="font-bold"></h3>
<Form.Item name="收支平台" label="收支平台">
<Select placeholder="请选择收支平台">
{paymentPlatforms.map(platform => (
<Select.Option key={platform._id} value={platform._id}>
{platform.}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label="收支类型"
name="收支类型"
>
<Select placeholder="请选择收支类型" allowClear>
<Option value="收入"></Option>
<Option value="支出"></Option>
</Select>
</Form.Item>
<Form.Item name="收支金额" label="收支金额" >
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label="待收"
name="待收"
>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label="收款码"
name="收款码"
>
<div
onPaste={handlePaste}
className="cursor-pointer flex flex-col items-center justify-center p-4 min-h-[260px] min-w-[260px] border-dashed border rounded-lg"
>
{paymentCode ? (
<>
<img src={paymentCode} alt="Payment Code" className="max-w-full max-h-[260px]" />
<button
onClick={() => setPaymentCode(null)}
className="absolute top-2 right-2 p-1 bg-gray-100 hover:bg-gray-200 rounded-full"
style={{ width: '22px', height: '22px', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '50%', outline: 'none', color: 'red' }}
>
<CloseOutlined />
</button>
</>
) : (
<p></p>
)}
</div>
</Form.Item>
</div>
</div>
</Form>
</Modal>
);
};
export default MultiAfterSalesModal;