feat(dealer): 实现客户报备功能

- 新增客户报备页面,包括表单提交和审核状态显示
- 修改客户列表页面,增加签约和取消操作- 更新客户状态标签样式
- 调整API基础URL
This commit is contained in:
2025-09-03 11:57:23 +08:00
parent 0b43a3bc92
commit 3600c1dfba
7 changed files with 305 additions and 33 deletions

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '客户报备',
navigationBarTextStyle: 'black'
})

209
src/dealer/customer/add.tsx Normal file
View File

@@ -0,0 +1,209 @@
import {useEffect, useState, useRef} from "react";
import {Loading, CellGroup, Cell, Input, Form} from '@nutui/nutui-react-taro'
import {Edit} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro'
import {View} from '@tarojs/components'
import FixedButton from "@/components/FixedButton";
import {useUser} from "@/hooks/useUser";
import {ShopDealerApply} from "@/api/shop/shopDealerApply/model";
import {
addShopDealerApply,
pageShopDealerApply,
updateShopDealerApply
} from "@/api/shop/shopDealerApply";
import {getShopDealerUser} from "@/api/shop/shopDealerUser";
const AddUserAddress = () => {
const {user} = useUser()
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 getApplyStatusText = (status?: number) => {
switch (status) {
case 10:
return '待审核'
case 20:
return '审核通过'
case 30:
return '驳回'
default:
return '未知状态'
}
}
const reload = async () => {
// 判断用户是否登录
if (!user?.userId) {
return false;
}
// 查询当前用户ID是否已有申请记录
try {
const res = await pageShopDealerApply({});
if (res && res.count > 0) {
setIsEditMode(true);
setExistingApply(res.list[0]);
// 如果有记录,填充表单数据
setFormData(res.list[0]);
setLoading(false)
} else {
setIsEditMode(false);
setExistingApply(null);
setLoading(false)
}
} catch (error) {
setLoading(true)
console.error('查询申请记录失败:', error);
setIsEditMode(false);
setExistingApply(null);
}
}
// 提交表单
const submitSucceed = async (values: any) => {
try {
// 准备提交的数据
const submitData = {
...values,
realName: values.realName || user?.nickname,
mobile: user?.phone,
refereeId: values.refereeId || FormData?.refereeId,
applyStatus: 10,
auditTime: undefined
};
await getShopDealerUser(submitData.refereeId);
// 如果是编辑模式添加现有申请的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);
return Taro.showToast({
title: '邀请人ID不存在',
icon: 'error'
});
}
}
// 处理固定按钮点击事件
const handleFixedButtonClick = () => {
// 触发表单提交
formRef.current?.submit();
};
const submitFailed = (error: any) => {
console.log(error, 'err...')
}
useEffect(() => {
reload().then(() => {
setLoading(false)
})
}, [user?.userId]); // 依赖用户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}/>
</Form.Item>
<Form.Item name="realName" label="联系人" initialValue={FormData?.realName} required>
<Input placeholder="请输入联系人" disabled={true}/>
</Form.Item>
<Form.Item name="mobile" label="联系方式" initialValue={FormData?.mobile} required>
<Input placeholder="请输入手机号" disabled={true} maxLength={11}/>
</Form.Item>
<Form.Item name="address" label="公司地址" initialValue={FormData?.address} required>
<Input placeholder="请输入详细地址" disabled={true}/>
</Form.Item>
<Form.Item name="dealerCode" label="户号" initialValue={FormData?.dealerCode} required>
<Input placeholder="请填写户号" disabled={true}/>
</Form.Item>
<Form.Item name="money" label="签约价格" initialValue={FormData?.money} required>
<Input placeholder="请按合同填写签约价格" disabled={true}/>
</Form.Item>
<Form.Item name="applyTime" label="签约时间" initialValue={FormData?.applyTime} required>
<Input placeholder="请选择签约时间" disabled={true}/>
</Form.Item>
<Form.Item name="applyTime" label="合同日期" initialValue={FormData?.applyTime} required>
<Input placeholder="请选择合同生效起止时间" disabled={true}/>
</Form.Item>
{/*<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>*/}
{/* <Input placeholder="邀请人ID"/>*/}
{/*</Form.Item>*/}
</CellGroup>
</Form>
{/* 审核状态显示(仅在编辑模式下显示) */}
{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 || FormData?.applyStatus === 30) && (
<FixedButton
icon={<Edit/>}
text={isEditMode ? '保存修改' : '提交申请'}
disabled={FormData?.applyStatus === 10}
onClick={handleFixedButtonClick}
/>
)}
</>
);
};
export default AddUserAddress;

View File

@@ -1,27 +1,29 @@
import React, {useState, useEffect, useCallback} from 'react'
import {useState, useEffect, useCallback} from 'react'
import {View, Text} from '@tarojs/components'
import {Loading, Tabs, TabPane, Tag} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
import {Loading, Space, Tabs, TabPane, Tag, Button} from '@nutui/nutui-react-taro'
import {Phone} from '@nutui/icons-react-taro'
import {pageUsers} from "@/api/system/user";
import type {User as UserType} from "@/api/system/user/model";
import type {ShopDealerApply, ShopDealerApply as UserType} from "@/api/shop/shopDealerApply/model";
import {
CustomerStatus,
getStatusText,
getStatusTagType,
getRandomStatus,
getStatusOptions
} from '@/utils/customerStatus';
import FixedButton from "@/components/FixedButton";
import navTo from "@/utils/common";
import {pageShopDealerApply, updateShopDealerApply} from "@/api/shop/shopDealerApply";
// 扩展User类型添加客户状态
interface CustomerUser extends UserType {
customerStatus?: CustomerStatus;
}
const CustomerManagement: React.FC = () => {
const CustomerIndex = () => {
const [list, setList] = useState<CustomerUser[]>([])
const [loading, setLoading] = useState<boolean>(false)
const [activeTab, setActiveTab] = useState<CustomerStatus>('all')
const [searchValue, setSearchValue] = useState<string>('')
const [searchValue, _] = useState<string>('')
// Tab配置
const tabList = getStatusOptions();
@@ -30,15 +32,24 @@ const CustomerManagement: React.FC = () => {
const fetchCustomerData = useCallback(async () => {
setLoading(true);
try {
// 获取用户列表status: 0 表示正常状态
const res = await pageUsers({ status: 0 });
const res = await pageShopDealerApply({});
if (res?.list) {
// 为每个用户添加随机的客户状态(实际项目中应该从后端获取真实状态)
const customersWithStatus: CustomerUser[] = res.list.map(user => ({
...user,
customerStatus: getRandomStatus() // 临时使用随机状态,实际应该从数据库获取
}));
setList(customersWithStatus);
list.map(d => {
if(d.applyStatus == 10){
d.customerStatus = 'pending'
}
if(d.applyStatus == 20){
d.customerStatus = 'signed'
}
if(d.applyStatus == 30){
d.customerStatus = 'cancelled'
}
return {
...d,
customerStatus: d.applyStatus == 10 ? 'pending' : 'signed'
}
})
setList(res.list);
}
} catch (error) {
console.error('获取客户数据失败:', error);
@@ -48,13 +59,18 @@ const CustomerManagement: React.FC = () => {
}, []);
// 根据当前Tab和搜索条件筛选数据
const getFilteredList = () => {
console.log('TABS',activeTab)
let filteredList = list;
// 按状态筛选
if (activeTab !== 'all') {
console.log(filteredList,'customerStatus')
filteredList.map(d => {
console.log(d.applyStatus)
})
filteredList = filteredList.filter(customer => customer.customerStatus === activeTab);
}
@@ -63,9 +79,9 @@ const CustomerManagement: React.FC = () => {
const keyword = searchValue.trim().toLowerCase();
filteredList = filteredList.filter(customer =>
(customer.realName && customer.realName.toLowerCase().includes(keyword)) ||
(customer.nickname && customer.nickname.toLowerCase().includes(keyword)) ||
(customer.username && customer.username.toLowerCase().includes(keyword)) ||
(customer.phone && customer.phone.includes(keyword)) ||
(customer.dealerName && customer.dealerName.toLowerCase().includes(keyword)) ||
(customer.dealerCode && customer.dealerCode.toLowerCase().includes(keyword)) ||
(customer.mobile && customer.mobile.includes(keyword)) ||
(customer.userId && customer.userId.toString().includes(keyword))
);
}
@@ -91,6 +107,16 @@ const CustomerManagement: React.FC = () => {
return counts;
};
// 取消操作
const handleCancel = (customer: ShopDealerApply) => {
updateShopDealerApply({
...customer,
applyStatus: 30
}).then(r => {
console.log(r)
})
};
// 初始化数据
useEffect(() => {
fetchCustomerData();
@@ -103,7 +129,7 @@ const CustomerManagement: React.FC = () => {
<View className="flex-1">
<View className="flex items-center justify-between mb-1">
<Text className="font-semibold text-gray-800 mr-2">
{customer.realName || customer.nickname || customer.username}
{customer.realName || customer.dealerName || customer.dealerCode}
</Text>
{customer.customerStatus && (
<Tag type={getStatusTagType(customer.customerStatus)}>
@@ -112,16 +138,37 @@ const CustomerManagement: React.FC = () => {
)}
</View>
<View className="flex items-center mb-1">
<Phone size={12} className="mr-1" />
<Text className="text-xs text-gray-500">
{customer.phone || '未填写'}
</Text>
<Space direction="vertical">
<Text className="text-xs text-gray-500">{customer.realName}</Text>
<Text className="text-xs text-gray-500">{customer.mobile}<Phone size={12}
className="ml-1 text-green-500"/></Text>
</Space>
</View>
<Text className="text-xs text-gray-500">
{customer.createTime}
{customer.createTime}
</Text>
</View>
</View>
{/* 跟进中状态显示操作按钮 */}
{customer.applyStatus === 10 && (
<Space className="flex justify-end">
<Button
size="small"
onClick={() => navTo(`/dealer/customer/add?id=${customer.applyId}`, true)}
style={{marginRight: '8px', backgroundColor: '#52c41a', color: 'white'}}
>
</Button>
<Button
size="small"
onClick={() => handleCancel(customer)}
style={{backgroundColor: '#ff4d4f', color: 'white'}}
>
</Button>
</Space>
)}
</View>
);
@@ -131,10 +178,10 @@ const CustomerManagement: React.FC = () => {
if (loading) {
return (
<View className="flex items-center justify-center py-8">
<Loading />
<Space className="flex items-center justify-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</View>
</Space>
);
}
@@ -155,6 +202,7 @@ const CustomerManagement: React.FC = () => {
return (
<View className="min-h-screen bg-gray-50">
{/* 顶部Tabs */}
<View className="bg-white">
<Tabs
@@ -177,8 +225,10 @@ const CustomerManagement: React.FC = () => {
{/* 客户列表 */}
{renderCustomerList()}
<FixedButton text={'客户报备'} onClick={() => Taro.navigateTo({url: '/dealer/customer/add'})}/>
</View>
);
};
export default CustomerManagement;
export default CustomerIndex;