Files
template-10559/src/dealer/customer/add.tsx
赵忠林 87d6648989 feat(user): 实现扫码登录和推荐人绑定功能- 添加扫码登录相关API接口,包括生成二维码、检查状态、确认登录等
- 在用户注册时支持从邀请参数中获取推荐人ID并绑定
- 修改管理员面板UI,添加统一扫码功能入口- 更新用户管理相关API地址,统一使用SERVER_API_URL
- 调整优惠券卡片样式,移除小程序不支持的CSS属性
- 添加聊天会话和消息管理相关API模块
- 新增分销商银行卡管理API接口
- 修改系统用户模型,增加推荐人ID字段
- 更新广告位查询接口,支持根据code获取广告位
- 调整邀请绑定接口参数,将refereeId改为dealerId
- 修改环境配置中的应用名称为"时里院子市集"
- 移除分享到朋友圈的相关代码
- 添加管理员面板组件,提供统一扫码等管理功能
-修复用户管理API请求参数传递问题
- 添加聊天消息和会话管理的完整CRUD接口
- 更新系统用户相关接口URL,确保正确调用后端服务
- 添加分销商银行卡管理的完整API接口实现
- 修改邀请绑定接口,使用dealerId替代refereeId参数
- 修复扫码登录相关API的URL拼接问题
- 添加二维码内容解析功能,支持多种格式的token提取
- 更新用户信息模型,增加推荐人ID字段
-优化管理员面板样式和交互逻辑- 调整优惠券组件样式,兼容小程序环境限制- 修复用户管理模块的API调用问题
- 添加聊天相关数据模型和接口定义
- 更新环境配置中的应用名称
-修复邀请绑定相关的参数传递问题- 添加扫码登录状态枚举和相关数据结构定义- 优化管理员功能面板的UI展示和交互体验- 修复系统用户管理接口的请求参数问题
- 添加分销商银行卡管理相关接口实现- 调整聊天消息和会话管理接口的数据结构定义
-修复用户管理模块中的API调用路径问题
- 添加扫码登录相关工具函数,如设备信息获取等
- 更新邀请绑定接口的数据模型定义
-优化管理员面板组件的样式和功能实现
-修复系统用户管理接口中的参数传递问题
- 添加聊天相关模块的完整API接口实现
- 调整分销商银行卡管理模块的数据结构定义- 修复扫码登录相关接口的URL拼接问题- 更新用户管理模块中的API调用方式
- 添加聊天消息批量发送等相关接口实现- 修复邀请绑定接口中的参数名称问题- 优化管理员面板组件的功能和交互逻辑
- 调整系统用户管理接口的请求参数传递方式
- 添加分销商银行卡管理模块的完整接口实现
-修复聊天相关接口中的数据结构问题
- 更新扫码登录相关接口的数据模型定义
- 优化管理员功能面板的展示效果和用户体验
2025-09-24 19:23:56 +08:00

401 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {useEffect, useState, useRef} from "react";
import {Loading, CellGroup, Cell, Input, Form, Calendar} from '@nutui/nutui-react-taro'
import {Edit, Calendar as CalendarIcon} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro'
import {useRouter} from '@tarojs/taro'
import {View, Text} from '@tarojs/components'
import FixedButton from "@/components/FixedButton";
import {useUser} from "@/hooks/useUser";
import {ShopDealerApply} from "@/api/shop/shopDealerApply/model";
import {
addShopDealerApply, getShopDealerApply, pageShopDealerApply,
updateShopDealerApply
} from "@/api/shop/shopDealerApply";
import {
formatDateForDatabase,
extractDateForCalendar, formatDateForDisplay
} from "@/utils/dateUtils";
const AddShopDealerApply = () => {
const {user} = useUser()
const {params} = useRouter();
const [loading, setLoading] = useState<boolean>(true)
const [FormData, setFormData] = useState<ShopDealerApply>()
const formRef = useRef<any>(null)
const [isEditMode, setIsEditMode] = useState<boolean>(false)
const [existingApply, setExistingApply] = useState<ShopDealerApply | null>(null)
// 日期选择器状态
const [showApplyTimePicker, setShowApplyTimePicker] = useState<boolean>(false)
const [showContractTimePicker, setShowContractTimePicker] = useState<boolean>(false)
const [applyTime, setApplyTime] = useState<string>('')
const [contractTime, setContractTime] = useState<string>('')
// 获取审核状态文字
const getApplyStatusText = (status?: number) => {
switch (status) {
case 10:
return '待审核'
case 20:
return '已签约'
case 30:
return '已取消'
default:
return '未知状态'
}
}
console.log(getApplyStatusText)
// 处理签约时间选择
const handleApplyTimeConfirm = (param: string) => {
const selectedDate = param[3] // 选中的日期字符串 (YYYY-M-D)
const formattedDate = formatDateForDatabase(selectedDate) // 转换为数据库格式
setApplyTime(selectedDate) // 保存原始格式用于显示
setShowApplyTimePicker(false)
// 更新表单数据(使用数据库格式)
if (formRef.current) {
formRef.current.setFieldsValue({
applyTime: formattedDate
})
}
}
// 处理合同日期选择
const handleContractTimeConfirm = (param: string) => {
const selectedDate = param[3] // 选中的日期字符串 (YYYY-M-D)
const formattedDate = formatDateForDatabase(selectedDate) // 转换为数据库格式
setContractTime(selectedDate) // 保存原始格式用于显示
setShowContractTimePicker(false)
// 更新表单数据(使用数据库格式)
if (formRef.current) {
formRef.current.setFieldsValue({
contractTime: formattedDate
})
}
}
const reload = async () => {
if (!params.id) {
return false;
}
// 查询当前用户ID是否已有申请记录
try {
const dealerApply = await getShopDealerApply(Number(params.id));
if (dealerApply) {
setFormData(dealerApply)
setIsEditMode(true);
setExistingApply(dealerApply)
// 初始化日期数据从数据库格式转换为Calendar组件格式
if (dealerApply.applyTime) {
setApplyTime(extractDateForCalendar(dealerApply.applyTime))
}
if (dealerApply.contractTime) {
setContractTime(extractDateForCalendar(dealerApply.contractTime))
}
Taro.setNavigationBarTitle({title: '签约'})
}
} catch (error) {
setLoading(true)
console.error('查询申请记录失败:', error);
setIsEditMode(false);
setExistingApply(null);
}
}
// 提交表单
// 计算保护期过期时间7天后
const calculateExpirationTime = (): string => {
const now = new Date();
const expirationDate = new Date(now);
expirationDate.setDate(now.getDate() + 7); // 7天后
// 格式化为数据库需要的格式YYYY-MM-DD HH:mm:ss
const year = expirationDate.getFullYear();
const month = String(expirationDate.getMonth() + 1).padStart(2, '0');
const day = String(expirationDate.getDate()).padStart(2, '0');
const hours = String(expirationDate.getHours()).padStart(2, '0');
const minutes = String(expirationDate.getMinutes()).padStart(2, '0');
const seconds = String(expirationDate.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
const submitSucceed = async (values: any) => {
try {
// 验证必填字段
if (!values.mobile || values.mobile.trim() === '') {
Taro.showToast({
title: '请填写联系方式',
icon: 'error'
});
return;
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(values.mobile)) {
Taro.showToast({
title: '请填写正确的手机号',
icon: 'error'
});
return;
}
// 检查客户是否已存在
const res = await pageShopDealerApply({dealerName: values.dealerName, type: 4, applyStatus: 10});
if (res && res.count > 0) {
const existingCustomer = res.list[0];
// 检查是否在7天保护期内
if (!isEditMode && existingCustomer.applyTime) {
// 将申请时间字符串转换为时间戳进行比较
const applyTimeStamp = new Date(existingCustomer.applyTime).getTime();
const currentTimeStamp = new Date().getTime();
const sevenDaysInMs = 7 * 24 * 60 * 60 * 1000; // 7天的毫秒数
// 如果在7天保护期内不允许重复添加
if (currentTimeStamp - applyTimeStamp < sevenDaysInMs) {
const remainingDays = Math.ceil((sevenDaysInMs - (currentTimeStamp - applyTimeStamp)) / (24 * 60 * 60 * 1000));
Taro.showToast({
title: `该客户还在保护期,还需等待${remainingDays}天后才能重新添加`,
icon: 'none',
duration: 3000
});
return false;
} else {
// 超过7天保护期可以重新添加显示确认对话框
const modalResult = await new Promise<boolean>((resolve) => {
Taro.showModal({
title: '提示',
content: '该客户已超过7天保护期是否重新添加跟进',
showCancel: true,
cancelText: '取消',
confirmText: '确定',
success: (modalRes) => {
resolve(modalRes.confirm);
},
fail: () => {
resolve(false);
}
});
});
if (!modalResult) {
return false; // 用户取消,不继续执行
}
// 用户确认后继续执行添加逻辑
}
}
}
// 计算过期时间
const expirationTime = isEditMode ? existingApply?.expirationTime : calculateExpirationTime();
// 准备提交的数据
const submitData = {
...values,
type: 4,
realName: values.realName || user?.nickname,
mobile: values.mobile,
refereeId: 33534,
applyStatus: isEditMode ? 20 : 10,
auditTime: undefined,
// 设置保护期过期时间7天后
expirationTime: expirationTime,
// 确保日期数据正确提交(使用数据库格式)
applyTime: values.applyTime || (applyTime ? formatDateForDatabase(applyTime) : ''),
contractTime: values.contractTime || (contractTime ? formatDateForDatabase(contractTime) : '')
};
// 调试信息
console.log('=== 提交数据调试 ===');
console.log('是否编辑模式:', isEditMode);
console.log('计算的过期时间:', expirationTime);
console.log('提交的数据:', submitData);
console.log('==================');
// 如果是编辑模式添加现有申请的id
if (isEditMode && existingApply?.applyId) {
submitData.applyId = existingApply.applyId;
}
// 执行新增或更新操作
if (isEditMode) {
await updateShopDealerApply(submitData);
} else {
await addShopDealerApply(submitData);
}
Taro.showToast({
title: `${isEditMode ? '更新' : '提交'}成功`,
icon: 'success'
});
setTimeout(() => {
Taro.navigateBack();
}, 1000);
} catch (error) {
console.error('提交失败:', error);
Taro.showToast({
title: '提交失败,请重试',
icon: 'error'
});
}
}
// 处理固定按钮点击事件
const handleFixedButtonClick = () => {
// 触发表单提交
formRef.current?.submit();
};
const submitFailed = (error: any) => {
console.log(error, 'err...')
}
useEffect(() => {
reload().then(() => {
setLoading(false)
})
}, []); // 依赖用户ID当用户变化时重新加载
if (loading) {
return <Loading className={'px-2'}></Loading>
}
return (
<>
<Form
ref={formRef}
divider
initialValues={FormData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
>
<View className={'bg-gray-100 h-3'}></View>
<CellGroup style={{padding: '4px 0'}}>
<Form.Item name="dealerName" label="公司名称" initialValue={FormData?.dealerName} required>
<Input placeholder="公司名称" maxLength={10} disabled={isEditMode}/>
</Form.Item>
<Form.Item name="realName" label="联系人" initialValue={FormData?.realName} required>
<Input placeholder="请输入联系人" disabled={isEditMode}/>
</Form.Item>
<Form.Item name="mobile" label="联系方式" initialValue={FormData?.mobile} required>
<Input placeholder="请输入手机号" disabled={isEditMode} maxLength={11}/>
</Form.Item>
<Form.Item name="address" label="公司地址" initialValue={FormData?.address} required>
<Input placeholder="请输入详细地址" disabled={isEditMode}/>
</Form.Item>
<Form.Item name="dealerCode" label="户号" initialValue={FormData?.dealerCode} required>
<Input placeholder="请填写户号" disabled={isEditMode}/>
</Form.Item>
<Form.Item name="comments" label="跟进情况" initialValue={FormData?.comments}>
<Input placeholder="请填写跟进情况" disabled={isEditMode}/>
</Form.Item>
{isEditMode && (
<>
<Form.Item name="money" label="签约价格" initialValue={FormData?.money} required>
<Input placeholder="(元/兆瓦时)" disabled={false}/>
</Form.Item>
<Form.Item name="applyTime" label="签约时间" initialValue={FormData?.applyTime}>
<View
className="flex items-center justify-between py-2"
onClick={() => setShowApplyTimePicker(true)}
>
<View className="flex items-center">
<CalendarIcon size={16} color="#999" className="mr-2"/>
<Text style={{color: applyTime ? '#333' : '#999'}}>
{applyTime ? formatDateForDisplay(applyTime) : '请选择签约时间'}
</Text>
</View>
</View>
</Form.Item>
<Form.Item name="contractTime" label="合同日期" initialValue={FormData?.contractTime}>
<View
className="flex items-center justify-between py-2"
onClick={() => setShowContractTimePicker(true)}
>
<View className="flex items-center">
<CalendarIcon size={16} color="#999" className="mr-2"/>
<Text style={{color: contractTime ? '#333' : '#999'}}>
{contractTime ? formatDateForDisplay(contractTime) : '请选择合同生效起止时间'}
</Text>
</View>
</View>
</Form.Item>
{/*<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>*/}
{/* <Input placeholder="邀请人ID"/>*/}
{/*</Form.Item>*/}
</>
)}
<Form.Item name="userId" label="报备人" initialValue={FormData?.userId} required>
</Form.Item>
</CellGroup>
</Form>
{/* 签约时间选择器 */}
<Calendar
visible={showApplyTimePicker}
defaultValue={applyTime}
onClose={() => setShowApplyTimePicker(false)}
onConfirm={handleApplyTimeConfirm}
/>
{/* 合同日期选择器 */}
<Calendar
visible={showContractTimePicker}
defaultValue={contractTime}
onClose={() => setShowContractTimePicker(false)}
onConfirm={handleContractTimeConfirm}
/>
{/* 审核状态显示(仅在编辑模式下显示) */}
{isEditMode && (
<CellGroup>
{/*<Cell*/}
{/* title={'审核状态'}*/}
{/* extra={*/}
{/* <span style={{*/}
{/* color: FormData?.applyStatus === 20 ? '#52c41a' :*/}
{/* FormData?.applyStatus === 30 ? '#ff4d4f' : '#faad14'*/}
{/* }}>*/}
{/* {getApplyStatusText(FormData?.applyStatus)}*/}
{/* </span>*/}
{/* }*/}
{/*/>*/}
{FormData?.applyStatus === 20 && (
<Cell title={'签约时间'} extra={FormData?.auditTime || '无'}/>
)}
{FormData?.applyStatus === 30 && (
<Cell title={'驳回原因'} extra={FormData?.rejectReason || '无'}/>
)}
</CellGroup>
)}
{/* 底部浮动按钮 */}
{(!isEditMode || FormData?.applyStatus === 10) && (
<FixedButton
icon={<Edit/>}
text={'立即提交'}
onClick={handleFixedButtonClick}
/>
)}
</>
);
};
export default AddShopDealerApply;