From 9b9adb51436de6e4f778ef5efbe09d7c0bf2e93e Mon Sep 17 00:00:00 2001 From: RUI <298977887@qq.com> Date: Sat, 7 Jun 2025 00:40:53 +0800 Subject: [PATCH] 0607.2 --- src/components/product/ProductImage.tsx | 29 ++- src/hooks/useCustomerTotalIncome.ts | 234 ++++++++++++++++++ src/pages/api/backstage/accounts/[id].ts | 54 ++++ .../backstage/accounts/accountgrowth/index.ts | 41 +++ .../accounts/accountgrowth/register copy.ts | 56 +++++ .../accounts/accountgrowth/register.ts | 65 +++++ .../backstage/accounts/accountgrowth/sse.ts | 38 +++ src/pages/api/backstage/accounts/index.ts | 45 ++++ src/pages/api/backstage/accounts/sse.ts | 37 +++ src/pages/api/backstage/balance/[id].ts | 48 ++++ src/pages/api/backstage/coupons/[id].ts | 50 ++++ .../api/backstage/coupons/assign/[id].ts | 56 +++++ .../api/backstage/coupons/assign/index.ts | 39 +++ src/pages/api/backstage/coupons/index.ts | 51 ++++ src/pages/api/backstage/transactions/[id].ts | 33 +++ src/pages/api/backstage/transactions/index.ts | 59 +++++ src/pages/api/products/images/[id].ts | 11 +- .../sale/components/AddCustomerComponent.tsx | 30 ++- .../sale/components/CustomerInfoComponent.tsx | 26 +- .../team/sale/components/EditCustomerInfo.tsx | 22 +- .../sale/components/ProductInfoComponent.tsx | 55 ---- src/pages/team/sale/components/Recharge.tsx | 40 +-- .../team/sale/components/coupon-modal.tsx | 40 ++- .../team/sale/components/sales-record.tsx | 16 +- src/pages/team/sale/index copy 2.tsx | 228 ----------------- src/pages/team/sale/index copy.tsx | 137 ---------- src/pages/team/sale/index.tsx | 98 +++++--- 27 files changed, 1108 insertions(+), 530 deletions(-) create mode 100644 src/hooks/useCustomerTotalIncome.ts create mode 100644 src/pages/api/backstage/accounts/[id].ts create mode 100644 src/pages/api/backstage/accounts/accountgrowth/index.ts create mode 100644 src/pages/api/backstage/accounts/accountgrowth/register copy.ts create mode 100644 src/pages/api/backstage/accounts/accountgrowth/register.ts create mode 100644 src/pages/api/backstage/accounts/accountgrowth/sse.ts create mode 100644 src/pages/api/backstage/accounts/index.ts create mode 100644 src/pages/api/backstage/accounts/sse.ts create mode 100644 src/pages/api/backstage/balance/[id].ts create mode 100644 src/pages/api/backstage/coupons/[id].ts create mode 100644 src/pages/api/backstage/coupons/assign/[id].ts create mode 100644 src/pages/api/backstage/coupons/assign/index.ts create mode 100644 src/pages/api/backstage/coupons/index.ts create mode 100644 src/pages/api/backstage/transactions/[id].ts create mode 100644 src/pages/api/backstage/transactions/index.ts delete mode 100644 src/pages/team/sale/index copy 2.tsx delete mode 100644 src/pages/team/sale/index copy.tsx diff --git a/src/components/product/ProductImage.tsx b/src/components/product/ProductImage.tsx index 8c53d1a..364e709 100644 --- a/src/components/product/ProductImage.tsx +++ b/src/components/product/ProductImage.tsx @@ -14,10 +14,10 @@ interface ProductImageProps { } // 添加图片缓存机制 -const imageCache: Record = {}; +const imageCache: Record = {}; // 批量获取产品图片的函数 -export const batchFetchProductImages = async (productIds: string[]): Promise> => { +export const batchFetchProductImages = async (productIds: string[]): Promise> => { try { const uniqueIds = [...new Set(productIds)]; // 确保ID不重复 const cachedIds = uniqueIds.filter(id => !imageCache[id]); // 只获取未缓存的ID @@ -36,7 +36,8 @@ export const batchFetchProductImages = async (productIds: string[]): Promise => { +const fetchProductImage = async (productId: string): Promise => { // 检查缓存 if (imageCache[productId]) { return imageCache[productId]; @@ -66,21 +67,20 @@ const fetchProductImage = async (productId: string): Promise => { const response = await fetch(`/api/products/images/${productId}`); if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + console.error(`获取产品图片失败 ID:${productId}, status: ${response.status}`); + return null; // 返回null而不是抛出错误 } const data = await response.json(); const image = data.image; - // 更新缓存 - if (image) { - imageCache[productId] = image; - } + // 更新缓存,即使图片为null也缓存结果 + imageCache[productId] = image; return image; } catch (error: unknown) { - console.error(`获取产品图片失败 ID:${productId}`, error); - throw error; + console.error(`网络错误,获取产品图片失败 ID:${productId}`, error); + return null; // 网络错误时也返回null } }; @@ -122,8 +122,8 @@ const ProductImage: React.FC = React.memo(({ useEffect(() => { if (!productId) return; - // 如果已缓存,直接使用缓存 - if (imageCache[productId]) { + // 如果已缓存,直接使用缓存(包括null值) + if (productId in imageCache) { setImageSrc(imageCache[productId]); return; } @@ -134,7 +134,10 @@ const ProductImage: React.FC = React.memo(({ try { const image = await fetchProductImage(productId); setImageSrc(image); + // fetchProductImage现在不会抛出错误,所以不需要catch } catch (error) { + // 这个catch现在主要处理意外错误 + console.error('意外错误:', error); setIsError(true); } finally { setIsLoading(false); diff --git a/src/hooks/useCustomerTotalIncome.ts b/src/hooks/useCustomerTotalIncome.ts new file mode 100644 index 0000000..dce96ea --- /dev/null +++ b/src/hooks/useCustomerTotalIncome.ts @@ -0,0 +1,234 @@ +// src/hooks/useCustomerTotalIncome.ts +import { useState, useEffect } from 'react'; + +const useCustomerTotalIncome = (phone: string | null) => { + const [totalIncome, setTotalIncome] = useState(0); + const [loading, setLoading] = useState(false); + const [storeAccounts, setStoreAccounts] = useState([]); // 用于存储店铺的账号编号 + + useEffect(() => { + if (!phone) return; // 如果没有提供手机号,直接返回 + + const fetchCustomerIncomeData = async () => { + setLoading(true); + + try { + const response = await fetch(`/api/backstage/customers/sales/${phone}`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + /* + API返回数据结构如下: + { + "message": "查询成功", + "salesRecords": [ + { + "_id": "670f76fe3da0e5c5809b46c7", + "团队": "670f75093da0e5c5809b4584", + "客户": "670f767a3da0e5c5809b46bc", + "产品": [ + { + "成本": { + "成本价": 100, + "包装费": 20, + "运费": 30 + }, + "_id": "670f76e73da0e5c5809b46c4", + "团队": "670f75093da0e5c5809b4584", + "供应商": "670f75ff3da0e5c5809b45ab", + "品牌": "670f75d43da0e5c5809b459f", + "品类": "670f75e43da0e5c5809b45a4", + "名称": "测试产品1", + "描述": "测试产品1", + "编码": "测试产品1", + "货号": "测试产品1", + "别名": [ + "测试产品1" + ], + "售价": 500, + "createdAt": "2024-10-16T08:18:47.363Z", + "updatedAt": "2024-10-16T08:18:47.363Z", + "__v": 0 + } + ], + "订单来源": { + "_id": "670f763c3da0e5c5809b45ba", + "团队": "670f75093da0e5c5809b4584", + "账号负责人": "670f75043da0e5c5809b457d", + "前端引流人员": "670f75043da0e5c5809b457d", + "账号类型": [ + "670f75e43da0e5c5809b45a4" + ], + "账号编号": "1", + "微信号": "1", + "微信昵称": "1", + "手机编号": "1", + "账号状态": 1, + "备注": "1", + "日增长数据": [], + "createdAt": "2024-10-16T08:15:56.632Z", + "updatedAt": "2024-10-16T08:15:56.632Z", + "__v": 0 + }, + "导购": { + "_id": "670f75043da0e5c5809b457d", + "姓名": "999", + "电话": "999", + "邮箱": "999@qq.com", + "密码": "$2b$10$BucUsXpf4rVYgjU.IGVfju2d2OijFBA74EQCSenL5ftsERcSd7uyC", + "角色": "66b99afa3324caa6bd2b57ae", + "createdAt": "2024-10-16T08:10:44.743Z", + "updatedAt": "2024-10-16T08:10:49.908Z", + "__v": 0, + "团队": "670f75093da0e5c5809b4584" + }, + "成交日期": "2024-10-16T08:18:53.700Z", + "应收金额": 500, + "收款金额": 500, + "待收款": 0, + "收款平台": "670f762d3da0e5c5809b45b2", + "订单状态": [ + "正常" + ], + "收款状态": "全款", + "货款状态": "待结算", + "余额抵用": null, + "备注": "备注", + "优惠券": [], + "售后记录": [], + "createdAt": "2024-10-16T08:19:10.775Z", + "updatedAt": "2024-10-16T08:19:10.775Z", + "__v": 0 + }, + { + "_id": "6711cb3694e24dea3c362daf", + "团队": "670f75093da0e5c5809b4584", + "客户": "670f767a3da0e5c5809b46bc", + "产品": [ + { + "成本": { + "成本价": 100, + "包装费": 20, + "运费": 30 + }, + "_id": "670f76e73da0e5c5809b46c4", + "团队": "670f75093da0e5c5809b4584", + "供应商": "670f75ff3da0e5c5809b45ab", + "品牌": "670f75d43da0e5c5809b459f", + "品类": "670f75e43da0e5c5809b45a4", + "名称": "测试产品1", + "描述": "测试产品1", + "编码": "测试产品1", + "货号": "测试产品1", + "别名": [ + "测试产品1" + ], + "售价": 500, + "createdAt": "2024-10-16T08:18:47.363Z", + "updatedAt": "2024-10-16T08:18:47.363Z", + "__v": 0 + } + ], + "订单来源": { + "_id": "6711cac194e24dea3c362d8a", + "团队": "670f75093da0e5c5809b4584", + "账号负责人": "670f75043da0e5c5809b457d", + "前端引流人员": "670f75043da0e5c5809b457d", + "账号类型": [ + "670f75e43da0e5c5809b45a4" + ], + "账号编号": "2", + "微信号": "2", + "微信昵称": "2", + "手机编号": "2", + "账号状态": 1, + "备注": "2", + "日增长数据": [], + "createdAt": "2024-10-18T02:41:05.092Z", + "updatedAt": "2024-10-18T02:41:05.092Z", + "__v": 0 + }, + "导购": { + "_id": "670f75043da0e5c5809b457d", + "姓名": "999", + "电话": "999", + "邮箱": "999@qq.com", + "密码": "$2b$10$BucUsXpf4rVYgjU.IGVfju2d2OijFBA74EQCSenL5ftsERcSd7uyC", + "角色": "66b99afa3324caa6bd2b57ae", + "createdAt": "2024-10-16T08:10:44.743Z", + "updatedAt": "2024-10-16T08:10:49.908Z", + "__v": 0, + "团队": "670f75093da0e5c5809b4584" + }, + "成交日期": "2024-10-18T02:42:26.500Z", + "应收金额": 500, + "收款金额": 100, + "待收款": 300, + "收款平台": "670f762d3da0e5c5809b45b2", + "订单状态": [ + "正常" + ], + "收款状态": "定金", + "货款状态": "待结算", + "余额抵用": null, + "备注": "测试2", + "优惠券": [], + "售后记录": [], + "createdAt": "2024-10-18T02:43:02.441Z", + "updatedAt": "2024-10-18T02:43:02.441Z", + "__v": 0 + } + ], + "afterSalesRecords": [] +} + */ + + let totalSalesIncome = 0; // 收款金额总和 + let totalPendingIncome = 0; // 待收款总和 + let totalAfterSalesIncome = 0; // 售后收入总和 + let totalAfterSalesExpenditure = 0; // 售后支出总和 + const accountsSet = new Set(); // 用于去重保存账号编号 + + // 计算销售记录中的收款金额和待收款 + data.salesRecords.forEach((record: any) => { + totalSalesIncome += record.收款金额 || 0; + totalPendingIncome += record.待收款 || 0; + + if (record.订单来源?.账号编号) { + accountsSet.add(record.订单来源.账号编号); // 保存账号编号 + } + + }); + + // 计算售后记录中的收入和支出 + data.afterSalesRecords.forEach((record: any) => { + if (record.收支类型 === '收入') { + totalAfterSalesIncome += record.收支金额 || 0; + } else if (record.收支类型 === '支出') { + totalAfterSalesExpenditure += record.收支金额 || 0; + } + }); + + // 本单收入 = 收款金额总和 + 待收款总和 + 售后收入 - 售后支出 + const calculatedIncome = totalSalesIncome + totalPendingIncome + totalAfterSalesIncome - totalAfterSalesExpenditure; + setTotalIncome(calculatedIncome); + setStoreAccounts(Array.from(accountsSet)); // 转换 Set 为数组并保存 + } catch (error) { + console.error('获取客户消费总额时出错:', error); + } finally { + setLoading(false); + } + }; + + fetchCustomerIncomeData(); + }, [phone]); + + //return { totalIncome, loading }; + return { totalIncome, storeAccounts, loading }; +}; + +export default useCustomerTotalIncome; diff --git a/src/pages/api/backstage/accounts/[id].ts b/src/pages/api/backstage/accounts/[id].ts new file mode 100644 index 0000000..b09fd9f --- /dev/null +++ b/src/pages/api/backstage/accounts/[id].ts @@ -0,0 +1,54 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { Account } from '@/models'; +import connectDB from '@/utils/connectDB'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { query: { id }, method } = req; + + switch (method) { + case 'GET': + try { + const account = await Account.findById(id) + .populate('账号负责人') + .populate('前端引流人员') + .populate('账号类型'); + if (!account) { + return res.status(404).json({ message: '未找到账号' }); + } + res.status(200).json(account); + } catch (error) { + res.status(500).json({ message: '服务器错误' }); + } + break; + case 'PUT': + try { + const { 团队, 账号负责人, 前端引流人员, 账号类型, unionid, openid, 账号编号, 微信号, 头像, 微信昵称, 手机编号, 项目分配, 账号状态, 备注 } = req.body; + const updatedAccount = await Account.findByIdAndUpdate(id, { + 团队, 账号负责人, 前端引流人员, 账号类型, unionid, openid, 账号编号, 微信号, 头像, 微信昵称, 手机编号, 项目分配, 账号状态, 备注 + }, { new: true }); + if (!updatedAccount) { + return res.status(404).json({ message: '未找到账号' }); + } + res.status(200).json({ message: '店铺账号更新成功', account: updatedAccount }); + } catch (error) { + res.status(400).json({ message: '更新店铺账号失败' }); + } + break; + case 'DELETE': + try { + const deletedAccount = await Account.findByIdAndDelete(id); + if (!deletedAccount) { + return res.status(404).json({ message: '未找到账号' }); + } + res.status(200).json({ message: '店铺账号删除成功' }); + } catch (error) { + res.status(500).json({ message: '服务器错误' }); + } + break; + default: + res.setHeader('Allow', ['GET', 'PUT', 'DELETE']); + res.status(405).end(`不允许 ${method} 方法`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/accounts/accountgrowth/index.ts b/src/pages/api/backstage/accounts/accountgrowth/index.ts new file mode 100644 index 0000000..43d4c75 --- /dev/null +++ b/src/pages/api/backstage/accounts/accountgrowth/index.ts @@ -0,0 +1,41 @@ +//src\pages\api\backstage\accounts\accountgrowth\index.ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import connectDB from '@/utils/connectDB'; // 使用高阶函数 +import { Account } from '@/models'; // 使用之前的 AccountSchema 模型 + +interface AccountData { + _id: string; + 账号编号: string; + 微信号: string; + 日增长数据: DailyGrowth[]; +} + +interface DailyGrowth { + 日期: string; + 总人数: number; + 扣除人数: number; + 日增长人数: number; +} + +type Data = { + accounts?: AccountData[]; + error?: string; +}; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { teamId } = req.query; + + try { + const accounts = await Account.find({ 团队: teamId }).select('_id 账号编号 微信号 日增长数据'); + + if (!accounts) { + return res.status(404).json({ error: '未找到账号数据' }); + } + + res.status(200).json({ accounts }); + } catch (error) { + res.status(500).json({ error: '获取增长记录失败' }); + } +}; + +export default connectDB(handler); // 使用 connectDB 包装 API 处理函数 diff --git a/src/pages/api/backstage/accounts/accountgrowth/register copy.ts b/src/pages/api/backstage/accounts/accountgrowth/register copy.ts new file mode 100644 index 0000000..31eabc4 --- /dev/null +++ b/src/pages/api/backstage/accounts/accountgrowth/register copy.ts @@ -0,0 +1,56 @@ +//src\pages\api\backstage\accounts\accountgrowth\register.ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import connectDB from '@/utils/connectDB'; // 使用高阶函数确保数据库连接 +import { Account } from '@/models'; // 使用 Account 模型 +import dayjs from 'dayjs'; + +interface DailyGrowth { + 日期: Date; + 总人数: number; + 扣除人数: number; + 日增长人数: number; +} + +type Data = { + success?: boolean; + error?: string; +}; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { accountId, date, totalPeople, deductedPeople } = req.body; + + try { + // 找到目标账号 + const account = await Account.findById(accountId); + if (!account) { + return res.status(404).json({ error: '账号未找到' }); + } + + // 查找是否已经存在当天的记录 + const existingRecord = account.日增长数据.find((record: DailyGrowth) => + dayjs(record.日期).isSame(dayjs(date), 'day') + ); + + if (existingRecord) { + // 更新已有记录 + existingRecord.总人数 = totalPeople; + existingRecord.扣除人数 = deductedPeople; + existingRecord.日增长人数 = totalPeople - deductedPeople; + } else { + // 添加新记录 + account.日增长数据.push({ + 日期: dayjs(date).toDate(), + 总人数: totalPeople, + 扣除人数: deductedPeople, + 日增长人数: totalPeople - deductedPeople, + }); + } + + await account.save(); + res.status(200).json({ success: true }); + } catch (error) { + res.status(500).json({ error: '保存增长记录失败' }); + } +}; + +export default connectDB(handler); // 使用 connectDB 包装 API 处理函数 diff --git a/src/pages/api/backstage/accounts/accountgrowth/register.ts b/src/pages/api/backstage/accounts/accountgrowth/register.ts new file mode 100644 index 0000000..b3408a5 --- /dev/null +++ b/src/pages/api/backstage/accounts/accountgrowth/register.ts @@ -0,0 +1,65 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { broadcastUpdate } from './sse'; // 引入广播功能 +import { Account } from '@/models'; +import dayjs from 'dayjs'; +import { IDailyGrowth } from '@/models/types'; // 引入 DailyGrowth 类型 + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { accountId, date, totalPeople, deductedPeople } = req.body; + + try { + const account = await Account.findById(accountId); + if (!account) { + return res.status(404).json({ error: '账号未找到' }); + } + + const selectedDate = dayjs(date); + + // 找到当前日期的记录 + const existingRecord = account.日增长数据.find((record: IDailyGrowth) => + dayjs(record.日期).isSame(selectedDate, 'day') + ); + + // 找到前一天的记录 + const previousRecord = account.日增长数据 + .filter((record: IDailyGrowth) => dayjs(record.日期).isBefore(selectedDate)) + .sort((a: IDailyGrowth, b: IDailyGrowth) => dayjs(b.日期).unix() - dayjs(a.日期).unix())[0]; // 获取最近的一条记录 + + let previousTotalPeople = 0; + let growthPeople = 0; + + // 如果存在前一天的记录,计算增长,否则增长为 0 + if (previousRecord) { + previousTotalPeople = previousRecord.总人数; + growthPeople = totalPeople - deductedPeople - previousTotalPeople; + } else { + // 如果是首条记录,增长应为 0 + growthPeople = 0; + } + + // 更新现有记录或添加新记录 + if (existingRecord) { + existingRecord.总人数 = totalPeople; + existingRecord.扣除人数 = deductedPeople; + existingRecord.日增长人数 = growthPeople; + } else { + account.日增长数据.push({ + 日期: selectedDate.toDate(), + 总人数: totalPeople, + 扣除人数: deductedPeople, + 日增长人数: growthPeople, + }); + } + + await account.save(); + + // 广播更新 + broadcastUpdate(account.团队); + + res.status(200).json({ success: true }); + } catch (error) { + res.status(500).json({ error: '保存增长记录失败' }); + } +}; + +export default handler; diff --git a/src/pages/api/backstage/accounts/accountgrowth/sse.ts b/src/pages/api/backstage/accounts/accountgrowth/sse.ts new file mode 100644 index 0000000..b6357d1 --- /dev/null +++ b/src/pages/api/backstage/accounts/accountgrowth/sse.ts @@ -0,0 +1,38 @@ +//src\pages\api\backstage\accounts\accountgrowth\sse.ts +import { NextApiRequest, NextApiResponse } from 'next'; +import { Account } from '@/models'; + +let clients: NextApiResponse[] = []; + +const handler = (req: NextApiRequest, res: NextApiResponse) => { + const { teamId } = req.query; + + // 设置 SSE 头 + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + + // 添加客户端 + clients.push(res); + + // 监听连接关闭事件,移除客户端 + req.on('close', () => { + clients = clients.filter(client => client !== res); + }); + + // 每次新连接时发送当前数据 + Account.find({ 团队: teamId }).select('_id 账号编号 微信号 日增长数据').then((accounts) => { + res.write(`data: ${JSON.stringify({ accounts })}\n\n`); + }); +}; + +// 广播更新 +export const broadcastUpdate = (teamId: string) => { + Account.find({ 团队: teamId }).select('_id 账号编号 微信号 日增长数据').then((accounts) => { + clients.forEach((client) => { + client.write(`data: ${JSON.stringify({ accounts })}\n\n`); + }); + }); +}; + +export default handler; diff --git a/src/pages/api/backstage/accounts/index.ts b/src/pages/api/backstage/accounts/index.ts new file mode 100644 index 0000000..abe5216 --- /dev/null +++ b/src/pages/api/backstage/accounts/index.ts @@ -0,0 +1,45 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { Account } from '@/models'; +import connectDB from '@/utils/connectDB'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === 'GET') { + try { + const { teamId } = req.query; + const accounts = await Account.find({ 团队: teamId }) + .select('账号负责人 前端引流人员 账号类型 账号编号 手机编号 微信昵称 微信号 账号状态 备注') + .populate({ + path: '账号负责人', + select: '姓名', + }) + .populate({ + path: '前端引流人员', + select: '姓名', + }) + .populate({ + path: '账号类型', + select: 'name icon', + }); + res.status(200).json({ accounts }); + } catch (error) { + res.status(500).json({ message: '服务器错误' }); + } + } else if (req.method === 'POST') { + try { + const { 团队, 账号负责人, 前端引流人员, 账号类型, unionid, openid, 账号编号, 微信号, 头像, 微信昵称, 手机编号, 项目分配, 账号状态, 备注 } = req.body; + const newAccount = new Account({ + 团队, 账号负责人, 前端引流人员, 账号类型, unionid, openid, 账号编号, 微信号, 头像, 微信昵称, 手机编号, 项目分配, 账号状态, 备注 + }); + await newAccount.save(); + res.status(201).json({ message: '店铺账号创建成功', account: newAccount }); + } catch (error) { + console.error(error); + res.status(400).json({ message: '创建店铺账号失败' }); + } + } else { + res.setHeader('Allow', ['GET', 'POST']); + res.status(405).end(`不允许 ${req.method} 方法`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/accounts/sse.ts b/src/pages/api/backstage/accounts/sse.ts new file mode 100644 index 0000000..8815e30 --- /dev/null +++ b/src/pages/api/backstage/accounts/sse.ts @@ -0,0 +1,37 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { Account } from '@/models'; + +let clients: NextApiResponse[] = []; + +const handler = (req: NextApiRequest, res: NextApiResponse) => { + const { teamId } = req.query; + + // 设置 SSE 头 + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + + // 添加客户端 + clients.push(res); + + // 监听连接关闭事件,移除客户端 + req.on('close', () => { + clients = clients.filter(client => client !== res); + }); + + // 每次新连接时发送当前数据 + Account.find({ 团队: teamId }).select('_id 账号编号 微信号 日增长数据').then((accounts) => { + res.write(`data: ${JSON.stringify({ accounts })}\n\n`); + }); +}; + +// 广播更新 +export const broadcastUpdate = (teamId: string) => { + Account.find({ 团队: teamId }).select('_id 账号编号 微信号 日增长数据').then((accounts) => { + clients.forEach((client) => { + client.write(`data: ${JSON.stringify({ accounts })}\n\n`); + }); + }); +}; + +export default handler; diff --git a/src/pages/api/backstage/balance/[id].ts b/src/pages/api/backstage/balance/[id].ts new file mode 100644 index 0000000..2f8ced7 --- /dev/null +++ b/src/pages/api/backstage/balance/[id].ts @@ -0,0 +1,48 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import connectDB from '@/utils/connectDB'; +import { Transaction } from '@/models'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { method } = req; + const { id } = req.query; // 获取 URL 中的客户 ID + + switch (method) { + case 'GET': + try { + // 验证客户 ID 是否存在 + if (!id || typeof id !== 'string') { + return res.status(400).json({ message: '客户 ID 无效' }); + } + + // 查询客户的所有交易记录,并计算余额 + const transactions = await Transaction.find({ 客户: id }).select('金额 类型'); + + // 如果没有找到交易记录,返回余额为 0 + if (!transactions || transactions.length === 0) { + return res.status(200).json({ balance: 0 }); + } + + // 计算余额,假设充值增加余额,消费减少余额 + const balance = transactions.reduce((acc, transaction) => { + if (transaction.类型 === '充值' || transaction.类型 === '优惠') { + return acc + transaction.金额; + } else if (transaction.类型 === '消费' || transaction.类型 === '退款') { + return acc - transaction.金额; + } + return acc; + }, 0); + + res.status(200).json({ balance }); + } catch (error) { + console.error('服务器错误:', error); + res.status(500).json({ message: '服务器错误,请稍后重试' }); + } + break; + default: + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${method} Not Allowed`); + } +}; + +// 使用 connectDB 高阶函数来确保在处理请求之前数据库已连接 +export default connectDB(handler); diff --git a/src/pages/api/backstage/coupons/[id].ts b/src/pages/api/backstage/coupons/[id].ts new file mode 100644 index 0000000..d201753 --- /dev/null +++ b/src/pages/api/backstage/coupons/[id].ts @@ -0,0 +1,50 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { Coupon } from '@/models'; +import connectDB from '@/utils/connectDB'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { query: { id }, method } = req; + + switch (method) { + case 'GET': + try { + const coupon = await Coupon.findById(id); + if (!coupon) { + return res.status(404).json({ message: '未找到优惠券' }); + } + res.status(200).json(coupon); + } catch (error) { + res.status(500).json({ message: '服务器错误' }); + } + break; + case 'PUT': + try { + const { 名称, 描述, 优惠券类型, 券码, 金额, 折扣, 最低消费, 生效日期, 失效日期, 团队 } = req.body; + const updateData = { 名称, 描述, 优惠券类型, 券码, 金额, 折扣, 最低消费, 生效日期, 失效日期, 团队 }; + const updatedCoupon = await Coupon.findByIdAndUpdate(id, updateData, { new: true }); + if (!updatedCoupon) { + return res.status(404).json({ message: '未找到优惠券' }); + } + res.status(200).json({ message: '优惠券更新成功', coupon: updatedCoupon }); + } catch (error) { + res.status(400).json({ message: '更新优惠券失败' }); + } + break; + case 'DELETE': + try { + const deletedCoupon = await Coupon.findByIdAndDelete(id); + if (!deletedCoupon) { + return res.status(404).json({ message: '未找到优惠券' }); + } + res.status(200).json({ message: '优惠券删除成功' }); + } catch (error) { + res.status(500).json({ message: '服务器错误' }); + } + break; + default: + res.setHeader('Allow', ['GET', 'PUT', 'DELETE']); + res.status(405).end(`不允许 ${method} 方法`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/coupons/assign/[id].ts b/src/pages/api/backstage/coupons/assign/[id].ts new file mode 100644 index 0000000..cc897c7 --- /dev/null +++ b/src/pages/api/backstage/coupons/assign/[id].ts @@ -0,0 +1,56 @@ +//src\pages\api\backstage\coupons\assign\[id].ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import { Customer, Coupon } from '@/models'; +import connectDB from '@/utils/connectDB'; +import { v4 as uuidv4 } from 'uuid'; // 用于生成唯一券码 + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === 'POST') { + try { + const { id } = req.query; // 客户的 ID + const { couponIds } = req.body; + + const customer = await Customer.findById(id); + if (!customer) { + return res.status(404).json({ message: '客户未找到' }); + } + + if (!Array.isArray(customer.优惠券)) { + customer.优惠券 = []; + } + + for (const couponId of couponIds) { + const coupon = await Coupon.findById(couponId); + if (!coupon) { + return res.status(404).json({ message: `优惠券未找到: ${couponId}` }); + } + + // 生成唯一的券码 + const uniqueCouponCode = uuidv4(); + + // 使用符合 CouponUsageSchema 的结构 + const couponUsage = { + _id: coupon._id, // Coupon的ObjectId + 券码: uniqueCouponCode, + 已使用: false, + 使用日期: null, // 目前未使用,所以设置为 null + }; + + // 将优惠券分配给客户 + customer.优惠券.push(couponUsage); + } + + await customer.save(); + + res.status(200).json({ message: '优惠券分配成功' }); + } catch (error) { + console.error('Assign Coupon Failed:', error); + res.status(500).json({ message: '服务器错误' }); + } + } else { + res.setHeader('Allow', ['POST']); + res.status(405).end(`不允许 ${req.method} 方法`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/coupons/assign/index.ts b/src/pages/api/backstage/coupons/assign/index.ts new file mode 100644 index 0000000..9641dc9 --- /dev/null +++ b/src/pages/api/backstage/coupons/assign/index.ts @@ -0,0 +1,39 @@ +//src\pages\api\backstage\coupons\assign\index.ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import { Customer } from '@/models'; +import connectDB from '@/utils/connectDB'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === 'GET') { + try { + const { customerId } = req.query; + + if (!customerId) { + return res.status(400).json({ message: '缺少客户ID' }); + } + + // 查询客户信息,并填充其关联的优惠券信息 + const customer = await Customer.findById(customerId) + .populate({ + path: '优惠券._id', + model: 'Coupon', // 确保关联的是 Coupon 模型 + select: '名称 描述 优惠券类型 金额 折扣 最低消费 生效日期 失效日期' // 选择需要的字段 + }); + + if (!customer) { + return res.status(404).json({ message: '客户未找到' }); + } + + // 返回客户的优惠券信息 + res.status(200).json({ coupons: customer.优惠券 }); + } catch (error) { + console.error('查询客户优惠券信息失败:', error); + res.status(500).json({ message: '服务器错误' }); + } + } else { + res.setHeader('Allow', ['GET']); + res.status(405).end(`不允许 ${req.method} 方法`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/coupons/index.ts b/src/pages/api/backstage/coupons/index.ts new file mode 100644 index 0000000..5b73aa3 --- /dev/null +++ b/src/pages/api/backstage/coupons/index.ts @@ -0,0 +1,51 @@ +//src\pages\api\backstage\coupons\index.ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import { Coupon } from '@/models'; +import connectDB from '@/utils/connectDB'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === 'GET') { + try { + const { teamId } = req.query; + const coupons = await Coupon.find({ 团队: teamId }); + res.status(200).json({ coupons }); + } catch (error) { + res.status(500).json({ message: '服务器错误' }); + } + } else if (req.method === 'POST') { + try { + const { + //客户, + 券码, + 名称, 优惠券类型, 折扣, + 描述, + 金额, + 最低消费, + 生效日期, + 失效日期, + 团队 + } = req.body; + const newCoupon = new Coupon({ + //客户, + 券码, + 名称, 优惠券类型, 折扣, + 描述, + 金额, + 最低消费, + 生效日期, + 失效日期, + 团队 + }); + await newCoupon.save(); + res.status(201).json({ message: '优惠券创建成功', coupon: newCoupon }); + } catch (error) { + console.error('Create Coupon Failed:', error); + res.status(400).json({ message: '创建优惠券失败' }); + } + } else { + res.setHeader('Allow', ['GET', 'POST']); + res.status(405).end(`不允许 ${req.method} 方法`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/transactions/[id].ts b/src/pages/api/backstage/transactions/[id].ts new file mode 100644 index 0000000..f649ed6 --- /dev/null +++ b/src/pages/api/backstage/transactions/[id].ts @@ -0,0 +1,33 @@ +//src\pages\api\backstage\transactions\[id].ts +import { NextApiRequest, NextApiResponse } from 'next'; +import connectDB from '@/utils/connectDB'; +import { Transaction } from '@/models'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { method } = req; + const { id } = req.query; // 获取客户 ID + + switch (method) { + case 'GET': + try { + // 查询指定客户的所有交易记录 + const transactions = await Transaction.find({ 客户: id }).sort({ 日期: -1 }); // 按日期倒序排列 + + if (!transactions || transactions.length === 0) { + return res.status(404).json({ message: '未找到任何交易记录' }); + } + + // 返回交易记录 + res.status(200).json({ transactions }); + } catch (error) { + console.error('获取交易记录时出错:', error); + res.status(500).json({ message: '服务器错误,请稍后重试' }); + } + break; + default: + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${method} Not Allowed`); + } +}; + +export default connectDB(handler); diff --git a/src/pages/api/backstage/transactions/index.ts b/src/pages/api/backstage/transactions/index.ts new file mode 100644 index 0000000..6d0a515 --- /dev/null +++ b/src/pages/api/backstage/transactions/index.ts @@ -0,0 +1,59 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import connectDB from '@/utils/connectDB'; +import { Transaction, Customer } from '@/models'; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { method } = req; + + switch (method) { + case 'POST': + try { + const { 团队, 客户, 金额, 描述, 类型 } = req.body; + + // 输出接收到的参数 + console.log('接收到的参数:', { 团队, 客户, 金额, 描述, 类型 }); + + if (!团队 || !客户 || !金额 || !类型) { + return res.status(400).json({ message: '缺少必要的参数' }); + } + + // 创建新的交易记录 + const newTransaction = new Transaction({ + 团队, + 客户, + 类型, + 金额, + 余额: 金额, // 首次充值即为余额 + 描述, + }); + + await newTransaction.save(); + + // 根据交易类型更新客户余额 + const updateAmount = 类型 === '充值' ? 金额 : -金额; + + // 更新客户余额 + const updatedCustomer = await Customer.findByIdAndUpdate(客户, { + $inc: { 余额: updateAmount }, + }, { new: true }); + + // 更新交易记录的余额字段为客户的最新余额 + newTransaction.余额 = updatedCustomer?.余额; + await newTransaction.save(); + + //res.status(200).json({ message: '充值成功', transaction: newTransaction }); + // 返回创建的交易记录,包括生成的ID + res.status(200).json({ message: '交易成功', transaction: newTransaction, _id: newTransaction._id }); + } catch (error) { + console.error('服务器错误:', error); + res.status(500).json({ message: '服务器错误,请稍后重试' }); + } + break; + default: + res.setHeader('Allow', ['POST']); + res.status(405).end(`Method ${method} Not Allowed`); + } +}; + +// 使用 connectDB 高阶函数来确保在处理请求之前数据库已连接 +export default connectDB(handler); diff --git a/src/pages/api/products/images/[id].ts b/src/pages/api/products/images/[id].ts index e1cf559..d983d56 100644 --- a/src/pages/api/products/images/[id].ts +++ b/src/pages/api/products/images/[id].ts @@ -29,14 +29,11 @@ const handler = connectDB(async (req: NextApiRequest, res: NextApiResponse) => { // 查询产品图片,使用lean()提高性能 const product = await Product.findById(id, '图片').lean().exec() as { 图片?: string } | null; - if (!product) { - return res.status(404).json({ message: '产品不存在' }); - } - - // 返回图片数据 + // 始终返回200状态码,产品不存在或无图片时返回null return res.status(200).json({ - image: product.图片 || null, - productId: id + image: product?.图片 || null, + productId: id, + exists: !!product }); } catch (error) { diff --git a/src/pages/team/sale/components/AddCustomerComponent.tsx b/src/pages/team/sale/components/AddCustomerComponent.tsx index dbf16a5..29eb3ef 100644 --- a/src/pages/team/sale/components/AddCustomerComponent.tsx +++ b/src/pages/team/sale/components/AddCustomerComponent.tsx @@ -13,15 +13,14 @@ import { DatePicker, Row, Col, - message, Popover, Switch, Space, Typography, Divider, - Card + Card, + App } from 'antd'; -import axios from 'axios'; import { ICustomer } from '@/models/types'; import { useUserInfo } from '@/store/userStore'; import { @@ -32,6 +31,7 @@ import { } from '@ant-design/icons'; const { Text, Title } = Typography; +const { useApp } = App; interface AddCustomerComponentProps { visible: boolean; @@ -64,6 +64,7 @@ const AddCustomerComponent: React.FC = ({ const [loading, setLoading] = useState(false); const [address, setAddress] = useState(''); const [useKuaidi100, setUseKuaidi100] = useState(true); // 默认使用快递100 + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 // 表单提交处理 const handleSubmit = async (values: any) => { @@ -75,17 +76,26 @@ const AddCustomerComponent: React.FC = ({ }; try { - const response = await axios.post('/api/backstage/customers', formattedValues); - const newCustomer = response.data.customer; + const response = await fetch('/api/backstage/customers', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formattedValues) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || `HTTP error! status: ${response.status}`); + } + + const responseData = await response.json(); + const newCustomer = responseData.customer; message.success('客户添加成功'); onSuccess(newCustomer); form.resetFields(); } catch (error: any) { - if (error.response && error.response.data && error.response.data.message) { - message.error(error.response.data.message); - } else { - message.error('添加客户失败,请检查输入的数据'); - } + message.error(error.message || '添加客户失败,请检查输入的数据'); console.error('请求处理失败', error); } }; diff --git a/src/pages/team/sale/components/CustomerInfoComponent.tsx b/src/pages/team/sale/components/CustomerInfoComponent.tsx index 55f3855..cbf7f67 100644 --- a/src/pages/team/sale/components/CustomerInfoComponent.tsx +++ b/src/pages/team/sale/components/CustomerInfoComponent.tsx @@ -1,11 +1,12 @@ // src/pages/team/sale/components/CustomerInfoComponent.tsx import React, { useEffect, useState } from 'react'; -import { Button, Card, Avatar, Descriptions, Spin, message, Typography, Modal, Tag } from 'antd'; +import { Button, Card, Avatar, Descriptions, Spin, Typography, Modal, Tag, App } from 'antd'; import { ICustomer } from '@/models/types'; import dayjs from 'dayjs'; import Recharge from './Recharge'; -import axios from 'axios'; import CouponModal from './coupon-modal'; + +const { useApp } = App; import { UserOutlined, PhoneOutlined, @@ -41,6 +42,7 @@ const CustomerInfoComponent: React.FC = ({ const [coupons, setCoupons] = useState([]); // 存储客户的优惠券 const [loading, setLoading] = useState(false); // 数据加载状态 const [salesModalVisible, setSalesModalVisible] = useState(false); // 控制消费详情模态框的显示 + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 //const { totalIncome, loading: incomeLoading } = useCustomerTotalIncome(customer?._id || null); // 使用自定义 Hook 获取消费总额 //const { totalIncome, loading: incomeLoading } = useCustomerTotalIncome(customer?.电话 || null); // 使用自定义 Hook 获取消费总额 @@ -71,24 +73,24 @@ const CustomerInfoComponent: React.FC = ({ setLoading(true); try { const [balanceResponse, couponsResponse] = await Promise.all([ - axios.get(`/api/backstage/balance/${customer._id}`), - axios.get(`/api/backstage/coupons/assign`, { - params: { customerId: customer._id }, - }), + fetch(`/api/backstage/balance/${customer._id}`), + fetch(`/api/backstage/coupons/assign?customerId=${customer._id}`), ]); - if (balanceResponse.status === 200) { - setBalance(balanceResponse.data.balance ?? 0); + if (balanceResponse.ok) { + const balanceData = await balanceResponse.json(); + setBalance(balanceData.balance ?? 0); } else { setBalance(0); } - if (couponsResponse.status === 200) { - setCoupons(couponsResponse.data.coupons ?? []); + if (couponsResponse.ok) { + const couponsData = await couponsResponse.json(); + setCoupons(couponsData.coupons ?? []); } else { setCoupons([]); } - } catch (error) { + } catch (error: unknown) { console.error('获取数据时出错:', error); message.error('获取客户数据失败,请稍后重试'); setBalance(0); @@ -199,7 +201,7 @@ const CustomerInfoComponent: React.FC = ({ >{customer.加粉日期 ? dayjs(customer.加粉日期).format('YYYY-MM-DD') : 'null'}
- {storeAccounts.length > 0 ? storeAccounts.map((storeAccount) => + {storeAccounts.length > 0 ? storeAccounts.map((storeAccount: string) => }>{storeAccount} ) : ''} diff --git a/src/pages/team/sale/components/EditCustomerInfo.tsx b/src/pages/team/sale/components/EditCustomerInfo.tsx index a085922..eb8cdee 100644 --- a/src/pages/team/sale/components/EditCustomerInfo.tsx +++ b/src/pages/team/sale/components/EditCustomerInfo.tsx @@ -1,10 +1,11 @@ // src/pages/team/sale/components/EditCustomerInfo.tsx import React, { useEffect } from 'react'; -import { Modal, Form, Input, DatePicker, message } from 'antd'; +import { Modal, Form, Input, DatePicker, App } from 'antd'; import { ICustomer } from '@/models/types'; -import axios from 'axios'; import dayjs from 'dayjs'; +const { useApp } = App; + interface EditCustomerInfoProps { visible: boolean; onCancel: () => void; @@ -14,6 +15,7 @@ interface EditCustomerInfoProps { const EditCustomerInfo: React.FC = ({ visible, onOk, onCancel, customer }) => { const [form] = Form.useForm(); + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 // 当传入客户数据时,将其设置到表单中 useEffect(() => { @@ -38,17 +40,19 @@ const EditCustomerInfo: React.FC = ({ visible, onOk, onCa const method = customer ? 'PUT' : 'POST'; const url = customer ? `/api/backstage/customers/${customer._id}` : '/api/backstage/customers'; - const response = await axios({ + const response = await fetch(url, { method, - url, - data: { - ...values, + headers: { + 'Content-Type': 'application/json', }, + body: JSON.stringify({ + ...values, + }), }); - if (response.status === 200 || response.status === 201) { - //message.success('客户信息更新成功'); - onOk(response.data.customer); // 假设 API 返回更新后的客户数据 + if (response.ok) { + const responseData = await response.json(); + onOk(responseData.customer); // 假设 API 返回更新后的客户数据 } else { message.error('客户信息更新失败'); } diff --git a/src/pages/team/sale/components/ProductInfoComponent.tsx b/src/pages/team/sale/components/ProductInfoComponent.tsx index fd1d5d0..3ad0456 100644 --- a/src/pages/team/sale/components/ProductInfoComponent.tsx +++ b/src/pages/team/sale/components/ProductInfoComponent.tsx @@ -11,61 +11,6 @@ interface ProductInfoComponentProps { onAddProductClick: () => void; // 新增产品按钮的点击事件回调 } -/*数据模型 -// src/models/index.ts -//产品模型定义 -const ProductSchema: Schema = new Schema({ - 团队: { type: Schema.Types.ObjectId, ref: 'Team' }, // 将团队字段定义为引用 Team 模型的 ObjectId 表示产品所属团队 - 供应商: { type: Schema.Types.ObjectId, ref: 'Supplier' }, //关联供应商模型 - 品牌: { type: Schema.Types.ObjectId, ref: 'Brand' }, //关联品牌模型 - 品类: { type: Schema.Types.ObjectId, ref: 'Category' }, //关联品类模型 - 名称: String, - 描述: String, - 编码: String, - 图片: String, - 货号: String, - 别名: Array, //别名字段 Array表示数组 - 级别: String, - //成本,包含成本、包装费、运费 - 成本: { - 成本价: Number, - 包装费: Number, - 运费: Number, - }, - 售价: Number, - 库存: Number, -}, { timestamps: true }); // 自动添加创建时间和更新时间 -ProductSchema.index({ 团队: 1 }); // 对团队字段建立索引 - -// Supplier 供应商模型 -const SupplierSchema = new Schema( - { - 团队: { type: Schema.Types.ObjectId, ref: 'Team' }, //将团队字段定义为引用 Team 模型的 ObjectId - order: Number, //排序 - 供应商名称: String, - 联系方式: { - 电话: String, - 联系人: String, - 地址: String, - }, - 供应品类: [ - { - type: Schema.Types.ObjectId, - ref: 'Category', - }, - ], - // 供应商状态: 0 停用 1 正常 2 异常 3 备用,默认为 1 - status: { type: Number, default: 1 }, - 供应商等级: String, - 供应商类型: String, - 供应商备注: String, - }, - { timestamps: true }, -); - - -*/ - const ProductInfoComponent: React.FC = ({ products, onAddProductClick }) => { const defaultProduct = { _id: 'default', diff --git a/src/pages/team/sale/components/Recharge.tsx b/src/pages/team/sale/components/Recharge.tsx index f64fcbf..0403029 100644 --- a/src/pages/team/sale/components/Recharge.tsx +++ b/src/pages/team/sale/components/Recharge.tsx @@ -1,8 +1,9 @@ import React, { useState, useEffect } from 'react'; -import { Modal, Button, Form, Input, InputNumber, message, List } from 'antd'; +import { Modal, Button, Form, Input, InputNumber, List, App } from 'antd'; import { useUserInfo } from '@/store/userStore'; import { ICustomer } from '@/models/types'; -import axios from 'axios'; + +const { useApp } = App; interface RechargeProps { visible: boolean; @@ -17,6 +18,7 @@ const Recharge: React.FC = ({ visible, onCancel, customer, onRech const [transactions, setTransactions] = useState([]); // 存储交易记录 const [form] = Form.useForm(); const { 团队 } = useUserInfo(); + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 useEffect(() => { const fetchBalanceAndTransactions = async () => { @@ -28,17 +30,19 @@ const Recharge: React.FC = ({ visible, onCancel, customer, onRech form.resetFields(); // 获取客户余额 - const balanceResponse = await axios.get(`/api/backstage/balance/${customer._id}`); - if (balanceResponse.status === 200) { - setBalance(balanceResponse.data.balance ?? 0); // 默认值为 0 + const balanceResponse = await fetch(`/api/backstage/balance/${customer._id}`); + if (balanceResponse.ok) { + const balanceData = await balanceResponse.json(); + setBalance(balanceData.balance ?? 0); // 默认值为 0 } else { setBalance(0); // 无法获取余额时设为 0 } // 获取客户交易记录 - const transactionsResponse = await axios.get(`/api/backstage/transactions/${customer._id}`); - if (transactionsResponse.status === 200) { - setTransactions(transactionsResponse.data.transactions); + const transactionsResponse = await fetch(`/api/backstage/transactions/${customer._id}`); + if (transactionsResponse.ok) { + const transactionsData = await transactionsResponse.json(); + setTransactions(transactionsData.transactions); } else { message.error('无法获取交易记录'); } @@ -65,15 +69,21 @@ const Recharge: React.FC = ({ visible, onCancel, customer, onRech const values = await form.validateFields(); const { 金额, 描述 } = values; - const response = await axios.post('/api/backstage/transactions', { - 团队: 团队?._id, - 客户: customer?._id, - 类型: '充值', - 金额, - 描述, + const response = await fetch('/api/backstage/transactions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + 团队: 团队?._id, + 客户: customer?._id, + 类型: '充值', + 金额, + 描述, + }) }); - if (response.status === 200) { + if (response.ok) { message.success('充值成功'); onRechargeSuccess(); onCancel(); diff --git a/src/pages/team/sale/components/coupon-modal.tsx b/src/pages/team/sale/components/coupon-modal.tsx index c0ec35b..a79fe65 100644 --- a/src/pages/team/sale/components/coupon-modal.tsx +++ b/src/pages/team/sale/components/coupon-modal.tsx @@ -1,9 +1,10 @@ //src\pages\team\sale\components\coupon-modal.tsx import React, { useEffect, useState } from 'react'; -import { Modal, Form, message, Select, List, Typography, Tag } from 'antd'; -import axios from 'axios'; +import { Modal, Form, Select, List, Typography, Tag, App } from 'antd'; import { useUserInfo } from '@/store/userStore'; // 使用 Zustand 获取用户信息 +const { useApp } = App; + interface CouponModalProps { visible: boolean; onCancel: () => void; @@ -36,6 +37,7 @@ const CouponModal: React.FC = ({ visible, onOk, onCancel, cust const [teamCoupons, setTeamCoupons] = useState([]); const [customerCoupons, setCustomerCoupons] = useState([]); const [defaultCouponIds, setDefaultCouponIds] = useState([]); // 默认选中的优惠券ID数组 + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 useEffect(() => { if (userInfo.团队?._id) { @@ -48,9 +50,13 @@ const CouponModal: React.FC = ({ visible, onOk, onCancel, cust const fetchTeamCoupons = async (teamId: string) => { try { - const { data } = await axios.get(`/api/backstage/coupons?teamId=${teamId}`); + const response = await fetch(`/api/backstage/coupons?teamId=${teamId}`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); setTeamCoupons(data.coupons); - } catch (error) { + } catch (error: unknown) { console.error('Failed to load team coupons:', error); message.error('加载团队优惠券失败'); } @@ -58,16 +64,18 @@ const CouponModal: React.FC = ({ visible, onOk, onCancel, cust const fetchCustomerCoupons = async (customerId: string) => { try { - const { data } = await axios.get(`/api/backstage/coupons/assign`, { - params: { customerId }, - }); + const response = await fetch(`/api/backstage/coupons/assign?customerId=${customerId}`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); setCustomerCoupons(data.coupons); if (data.coupons.length > 0) { const couponIds = data.coupons.map((coupon: ICustomerCoupon) => coupon._id._id); // 使用嵌套的 _id setDefaultCouponIds(couponIds); form.setFieldsValue({ couponIds }); } - } catch (error) { + } catch (error: unknown) { console.error('Failed to load customer coupons:', error); message.error('加载客户优惠券失败'); } @@ -76,14 +84,24 @@ const CouponModal: React.FC = ({ visible, onOk, onCancel, cust const handleSubmit = async () => { try { const values = await form.validateFields(); - await axios.post(`/api/backstage/coupons/assign/${customerId}`, { - couponIds: values.couponIds, + const response = await fetch(`/api/backstage/coupons/assign/${customerId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + couponIds: values.couponIds, + }) }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + message.success('优惠券分配成功'); form.resetFields(); onOk(); - } catch (error) { + } catch (error: unknown) { console.error('Failed to assign coupons:', error); message.error('分配优惠券失败'); } diff --git a/src/pages/team/sale/components/sales-record.tsx b/src/pages/team/sale/components/sales-record.tsx index d5785c5..f7ce5ef 100644 --- a/src/pages/team/sale/components/sales-record.tsx +++ b/src/pages/team/sale/components/sales-record.tsx @@ -1,7 +1,8 @@ // src/pages/customer/sales-record.tsx import React, { useState, useEffect } from 'react'; -import { Card, List, Descriptions, message } from 'antd'; -import axios from 'axios'; +import { Card, List, Descriptions, App } from 'antd'; + +const { useApp } = App; interface SalesRecordPageProps { initialPhone?: string; // 用于传递初始手机号 @@ -12,6 +13,7 @@ const SalesRecordPage: React.FC = ({ initialPhone }) => { const [salesRecords, setSalesRecords] = useState([]); const [afterSalesRecords, setAfterSalesRecords] = useState([]); const [totalIncome, setTotalIncome] = useState(0); // 新增的本单收入状态 + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 const handleSearch = async () => { if (!initialPhone) { @@ -22,7 +24,13 @@ const SalesRecordPage: React.FC = ({ initialPhone }) => { setLoading(true); try { - const { data } = await axios.get(`/api/backstage/customers/sales/${initialPhone}`); + const response = await fetch(`/api/backstage/customers/sales/${initialPhone}`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); if (data.salesRecords) { setSalesRecords(data.salesRecords); @@ -33,7 +41,7 @@ const SalesRecordPage: React.FC = ({ initialPhone }) => { } message.success('查询成功'); - } catch (error) { + } catch (error: unknown) { console.error(error); message.error('查询失败,请稍后重试'); } finally { diff --git a/src/pages/team/sale/index copy 2.tsx b/src/pages/team/sale/index copy 2.tsx deleted file mode 100644 index 6eed0d1..0000000 --- a/src/pages/team/sale/index copy 2.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Form, Input, Select, message, DatePicker, Button } from 'antd'; -import { ISalesRecord } from '@/models/types'; -import axios from 'axios'; -import { useUserInfo } from '@/store/userStore'; - -interface SalesRecordPageProps { - salesRecord: ISalesRecord | null; - onCancel: () => void; - onOk: () => void; -} - -const SalesRecordPage: React.FC = ({ salesRecord, onCancel }) => { - const [form] = Form.useForm(); - const [customers, setCustomers] = useState([]); // 客户 - const [products, setProducts] = useState([]); // 产品 - const [accounts, setAccounts] = useState([]); //支付平台 - const [payPlatforms, setPayPlatforms] = useState([]); //支付平台 - const [users, setUsers] = useState([]); - const userInfo = useUserInfo(); // 获取当前用户信息 - - useEffect(() => { - if (userInfo.团队?._id) { - const teamId = userInfo.团队._id; - fetchCustomers(teamId); - fetchProducts(teamId); - fetchAccounts(teamId); - fetchUsers(teamId); - fetchPayPlatforms(teamId); - } - }, [userInfo]); - // 获取客户数据 - const fetchCustomers = async (teamId: string) => { - try { - const { data } = await axios.get(`/api/backstage/customers?teamId=${teamId}`); - setCustomers(data.customers); - } catch (error) { - message.error('加载客户数据失败'); - } - }; - // 获取产品数据 - const fetchProducts = async (teamId: string) => { - try { - const { data } = await axios.get(`/api/backstage/products?teamId=${teamId}`); - setProducts(data.products); - } catch (error) { - message.error('加载产品数据失败'); - } - }; - // 获取账户数据 - const fetchAccounts = async (teamId: string) => { - try { - const { data } = await axios.get(`/api/backstage/accounts?teamId=${teamId}`); - setAccounts(data.accounts); - } catch (error) { - message.error('加载账户数据失败'); - } - }; - // 获取用户数据 - const fetchUsers = async (teamId: string) => { - try { - const { data } = await axios.get(`/api/backstage/users?teamId=${teamId}`); - setUsers(data.users); - } catch (error) { - message.error('加载用户数据失败'); - } - }; - //获取支付平台数据 - const fetchPayPlatforms = async (teamId: string) => { - try { - const { data } = await axios.get(`/api/backstage/payment-platforms?teamId=${teamId}`); - setPayPlatforms(data.platforms || []); // 使用 data.platforms 而不是 data.paymentPlatforms - } catch (error) { - message.error('加载支付平台数据失败'); - setPayPlatforms([]); // 确保失败时也设置为一个空数组 - } - }; - - const handleSubmit = async () => { - try { - const values = await form.validateFields(); - const method = salesRecord ? 'PUT' : 'POST'; - const url = salesRecord ? `/api/backstage/sales/${salesRecord._id}` : '/api/backstage/sales'; - await axios({ - method: method, - url: url, - data: { - ...values, - 团队: userInfo.团队?._id, // 将团队ID包含在请求数据中 - }, - }); - message.success('销售记录操作成功'); - } catch (info) { - console.error('Validate Failed:', info); - message.error('销售记录操作失败'); - } - }; - - return ( -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- ); -}; - -export default SalesRecordPage; diff --git a/src/pages/team/sale/index copy.tsx b/src/pages/team/sale/index copy.tsx deleted file mode 100644 index 3bd5ff7..0000000 --- a/src/pages/team/sale/index copy.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Table, Button, Space, message, Card } from 'antd'; -import axios from 'axios'; -import { ISalesRecord } from '@/models/types'; // 确保有正确的类型定义 -import { useUserInfo } from '@/store/userStore'; // 使用 Zustand 获取用户信息 -import SalesRecordModal from './sales-record-modal'; // 引入销售记录模态框组件 -import dayjs from 'dayjs'; - -const SalesPage = () => { - const [salesRecords, setSalesRecords] = useState([]); - const [isModalVisible, setIsModalVisible] = useState(false); - const [currentSalesRecord, setCurrentSalesRecord] = useState(null); - const userInfo = useUserInfo(); // 获取当前用户信息 - - useEffect(() => { - if (userInfo.团队?._id) { - fetchSalesRecords(userInfo.团队._id); - } - }, [userInfo]); - - const fetchSalesRecords = async (teamId: string) => { - try { - const { data } = await axios.get(`/api/backstage/sales?teamId=${teamId}`); - setSalesRecords(data.salesRecords); - } catch (error) { - message.error('加载销售数据失败'); - } - }; - - const handleModalOk = () => { - setIsModalVisible(false); - if (userInfo.团队?._id) { - fetchSalesRecords(userInfo.团队._id); - } - }; - - const handleCreate = () => { - setCurrentSalesRecord(null); - setIsModalVisible(true); - }; - - const handleDelete = async (id: string) => { - try { - await axios.delete(`/api/backstage/sales/${id}`); - if (userInfo.团队?._id) { - fetchSalesRecords(userInfo.团队._id); - } - message.success('销售记录删除成功'); - } catch (error) { - message.error('删除销售记录失败'); - } - }; - - const columns = [ - { - title: '成交日期', - dataIndex: '成交日期', - key: 'dealDate', - render: (date: Date) => dayjs(date).format('YYYY-MM-DD'), - }, - { - title: '客户', - dataIndex: '客户', - key: 'customer', - render: (customer: any) => customer ? customer.姓名 : '无' - }, - { - title: '产品', - dataIndex: '产品', - key: 'products', - render: (products: any[]) => products.map(p => p.名称).join(', ') - }, - { - title: '订单来源', - dataIndex: '订单来源', - key: 'orderSource', - render: (source: any) => source ? source.账号编号 : '无' - }, - { - title: '导购', - dataIndex: '导购', - key: 'salesperson', - render: (user: any) => user ? user.姓名 : '无' - }, - { - title: '收款状态', - dataIndex: '收款状态', - key: 'paymentStatus', - }, - { - title: '货款状态', - dataIndex: '货款状态', - key: 'paymentPlatform', - }, - { - title: '备注', - dataIndex: '备注', - key: 'remark', - }, - { - title: '操作', - key: 'action', - render: (_: any, record: ISalesRecord) => ( - - - - ), - }, - ]; - - return ( - - 添加销售记录 - - } - > - - {isModalVisible && ( - setIsModalVisible(false)} - salesRecord={currentSalesRecord} - /> - )} - - ); -}; - -export default SalesPage; diff --git a/src/pages/team/sale/index.tsx b/src/pages/team/sale/index.tsx index cd00c83..0852e0f 100644 --- a/src/pages/team/sale/index.tsx +++ b/src/pages/team/sale/index.tsx @@ -1,9 +1,10 @@ //src\pages\team\sale\index.tsx import React, { useEffect, useState } from 'react'; -import { Form, Input, Select, message, DatePicker, Button, Row, Col, Card, Divider, Tag } from 'antd'; +import { Form, Input, Select, DatePicker, Button, Row, Col, Card, Divider, Tag, App } from 'antd'; import { ICustomer, ICustomerCoupon, IProduct, ISalesRecord } from '@/models/types'; -import axios from 'axios'; import { useUserInfo } from '@/store/userStore'; + +const { useApp } = App; import CustomerInfoComponent from './components/CustomerInfoComponent'; import ProductInfoComponent from './components/ProductInfoComponent'; import AddProductComponent from './components/AddProductComponent'; @@ -22,6 +23,7 @@ const SalesRecordPage: React.FC = ({ salesRecord, onCancel const [accounts, setAccounts] = useState([]); //支付平台 const [payPlatforms, setPayPlatforms] = useState([]); //支付平台 const userInfo = useUserInfo(); // 获取当前用户信息 + const { message } = useApp(); // 使用 useApp hook 获取 message 实例 const [selectedCustomer, setSelectedCustomer] = useState(null);// 选中的客户 const [selectedProducts, setSelectedProducts] = useState([]);// 选中的产品,是一个数组 const [customerBalance, setCustomerBalance] = useState(0); // 客户余额 @@ -74,19 +76,19 @@ const SalesRecordPage: React.FC = ({ salesRecord, onCancel setSelectedCustomer(customer); if (customer) { try { - const response = await axios.get(`/api/backstage/balance/${customer._id}`); - if (response.status === 200) { - setCustomerBalance(response.data.balance ?? 0); + const response = await fetch(`/api/backstage/balance/${customer._id}`); + if (response.ok) { + const balanceData = await response.json(); + setCustomerBalance(balanceData.balance ?? 0); setDeductionAmount(0); // 重置抵扣金额 } else { setCustomerBalance(0); } // 获取客户优惠券 - const couponsResponse = await axios.get(`/api/backstage/coupons/assign`, { - params: { customerId: customer._id }, - }); - if (couponsResponse.status === 200) { - const customerCoupons = couponsResponse.data.coupons; + const couponsResponse = await fetch(`/api/backstage/coupons/assign?customerId=${customer._id}`); + if (couponsResponse.ok) { + const couponsData = await couponsResponse.json(); + const customerCoupons = couponsData.coupons; setCustomerCoupons(customerCoupons); if (customerCoupons.length > 0) { @@ -96,7 +98,7 @@ const SalesRecordPage: React.FC = ({ salesRecord, onCancel } else { setCustomerCoupons([]); } - } catch (error) { + } catch (error: unknown) { console.error('获取客户余额时出错:', error); setCustomerBalance(0); } @@ -120,38 +122,58 @@ const SalesRecordPage: React.FC = ({ salesRecord, onCancel // 获取客户数据 const fetchCustomers = async (teamId: string) => { try { - const { data } = await axios.get(`/api/backstage/customers?teamId=${teamId}`); + const response = await fetch(`/api/backstage/customers?teamId=${teamId}`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); setCustomers(data.customers); - } catch (error) { + } catch (error: unknown) { + console.error('加载客户数据失败:', error); message.error('加载客户数据失败'); } }; // 获取产品数据 const fetchProducts = async (teamId: string) => { try { - const { data } = await axios.get(`/api/backstage/products?teamId=${teamId}`); + 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) { + } catch (error: unknown) { + console.error('加载产品数据失败:', error); message.error('加载产品数据失败'); } }; // 获取账户数据 const fetchAccounts = async (teamId: string) => { try { - const { data } = await axios.get(`/api/backstage/accounts?teamId=${teamId}`); + const response = await fetch(`/api/backstage/accounts?teamId=${teamId}`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); //按账号编号排序 data.accounts.sort((a: any, b: any) => a.账号编号 - b.账号编号); setAccounts(data.accounts); - } catch (error) { + } catch (error: unknown) { + console.error('加载账户数据失败:', error); message.error('加载账户数据失败'); } }; //获取支付平台数据 const fetchPayPlatforms = async (teamId: string) => { try { - const { data } = await axios.get(`/api/backstage/payment-platforms?teamId=${teamId}`); + 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(); setPayPlatforms(data.platforms || []); // 使用 data.platforms 而不是 data.paymentPlatforms - } catch (error) { + } catch (error: unknown) { + console.error('加载支付平台数据失败:', error); message.error('加载支付平台数据失败'); setPayPlatforms([]); // 确保失败时也设置为一个空数组 } @@ -165,14 +187,26 @@ const SalesRecordPage: React.FC = ({ salesRecord, onCancel let transactionId = null; // 初始化交易记录ID if (deductionAmount > 0) { // 创建消费记录来扣除余额,并获取交易记录ID - const transactionResponse = await axios.post('/api/backstage/transactions', { - 团队: userInfo.团队?._id, - 客户: selectedCustomer ? selectedCustomer._id : null, - 类型: '消费', - 金额: deductionAmount, - 描述: '销售余额抵扣', + const transactionResponse = await fetch('/api/backstage/transactions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + 团队: userInfo.团队?._id, + 客户: selectedCustomer ? selectedCustomer._id : null, + 类型: '消费', + 金额: deductionAmount, + 描述: '销售余额抵扣', + }) }); - transactionId = transactionResponse.data._id; // 获取创建的交易记录的ID + + if (!transactionResponse.ok) { + throw new Error(`Transaction failed! status: ${transactionResponse.status}`); + } + + const transactionData = await transactionResponse.json(); + transactionId = transactionData._id; // 获取创建的交易记录的ID console.log('生成的transactionId:', transactionId); // 打印transactionId } @@ -187,11 +221,17 @@ const SalesRecordPage: React.FC = ({ salesRecord, onCancel }; // 将交易记录ID传递到后端 - await axios({ + const salesResponse = await fetch(url, { method: method, - url: url, - data: requestData, + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestData) }); + + if (!salesResponse.ok) { + throw new Error(`Sales record failed! status: ${salesResponse.status}`); + } message.success('销售记录操作成功'); // 清空表单