Compare commits
2 Commits
ca4f1c2a77
...
f4c2a64f8d
| Author | SHA1 | Date | |
|---|---|---|---|
| f4c2a64f8d | |||
| 812df56f8c |
@@ -3,7 +3,7 @@ import {Loading, CellGroup, Cell, Input, Form, Calendar, Popup, SearchBar} from
|
||||
import {Edit, Calendar as CalendarIcon, ArrowRight, Del} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {View, Text, ScrollView} from '@tarojs/components'
|
||||
import FixedButton from "@/components/FixedButton";
|
||||
import {ShopDealerApply} from "@/api/shop/shopDealerApply/model";
|
||||
import {
|
||||
@@ -699,9 +699,9 @@ const AddShopDealerApply = () => {
|
||||
position="bottom"
|
||||
round
|
||||
onClose={() => setShowReceptionistPicker(false)}
|
||||
style={{height: '70%'}}
|
||||
style={{height: '70vh'}}
|
||||
>
|
||||
<View className="flex flex-col h-full">
|
||||
<View style={{height: '100%', display: 'flex', flexDirection: 'column'}}>
|
||||
{/* 标题栏 */}
|
||||
<View className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
|
||||
<Text className="text-base font-semibold text-gray-800">选择接待人员</Text>
|
||||
@@ -718,7 +718,10 @@ const AddShopDealerApply = () => {
|
||||
/>
|
||||
</View>
|
||||
{/* 列表 */}
|
||||
<View className="flex-1 overflow-y-auto">
|
||||
<ScrollView
|
||||
scrollY
|
||||
style={{flex: 1, minHeight: 0, height: 'calc(70vh - 112px)'}}
|
||||
>
|
||||
{receptionistLoading ? (
|
||||
<View className="flex justify-center items-center py-8">
|
||||
<Loading>加载中</Loading>
|
||||
@@ -742,7 +745,7 @@ const AddShopDealerApply = () => {
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</Popup>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useState, useEffect, useCallback} from 'react'
|
||||
import {useState, useEffect, useCallback, useMemo, useRef} from 'react'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import Taro, {useDidShow} from '@tarojs/taro'
|
||||
import {Loading, InfiniteLoading, Empty, Space, Tabs, TabPane, Tag, Button, SearchBar} from '@nutui/nutui-react-taro'
|
||||
@@ -24,6 +24,10 @@ interface CustomerUser extends UserType {
|
||||
protectDays?: number; // 剩余保护天数
|
||||
}
|
||||
|
||||
const PAGE_LIMIT = 10;
|
||||
const COUNT_LIMIT = 1;
|
||||
const COUNT_REFRESH_DELAY = 300;
|
||||
|
||||
const CustomerIndex = () => {
|
||||
const [list, setList] = useState<CustomerUser[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
@@ -32,33 +36,58 @@ const CustomerIndex = () => {
|
||||
const [displaySearchValue, setDisplaySearchValue] = useState<string>('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [statusCounts, setStatusCounts] = useState({
|
||||
all: 0,
|
||||
pending: 0,
|
||||
signed: 0,
|
||||
cancelled: 0
|
||||
});
|
||||
const requestSeqRef = useRef(0)
|
||||
const didShowReadyRef = useRef(false)
|
||||
const countRefreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
|
||||
// 权限检查:只要登录就能查看客户列表
|
||||
const {user, loading: userLoading} = useUser()
|
||||
const roleCheckFinished = !userLoading
|
||||
const isLoggedIn = roleCheckFinished && user !== null
|
||||
const isSuperAdmin = user?.isSuperAdmin === true
|
||||
const isAdminRole = user?.isAdmin === true || user?.roles?.some(role => role.roleCode === 'admin') === true
|
||||
const canViewAllCustomers = isSuperAdmin || isAdminRole
|
||||
const currentUserId = Number(Taro.getStorageSync('UserId')) || user?.userId || 0
|
||||
|
||||
// Tab配置
|
||||
const tabList = getStatusOptions();
|
||||
|
||||
const buildCustomerQueryParams = useCallback((currentPage?: number) => {
|
||||
const currentUserId = Number(Taro.getStorageSync('UserId')) || user?.userId || 0;
|
||||
const buildCustomerQueryParams = useCallback((currentPage?: number, limit?: number) => {
|
||||
const params: any = {
|
||||
type: 4
|
||||
};
|
||||
|
||||
if (currentPage !== undefined) {
|
||||
params.page = currentPage;
|
||||
params.limit = limit || PAGE_LIMIT;
|
||||
}
|
||||
|
||||
if (!isSuperAdmin) {
|
||||
if (!canViewAllCustomers) {
|
||||
params.userId = currentUserId;
|
||||
params.receptionistId = currentUserId;
|
||||
}
|
||||
|
||||
return params;
|
||||
}, [isSuperAdmin, user?.userId]);
|
||||
}, [canViewAllCustomers, currentUserId]);
|
||||
|
||||
const canHandleCustomer = useCallback((customer: ShopDealerApply) => {
|
||||
if (isSuperAdmin) return true;
|
||||
return Number(customer.receptionistId) === currentUserId;
|
||||
}, [currentUserId, isSuperAdmin]);
|
||||
|
||||
const updateStatusCount = useCallback((status: CustomerStatus, count?: number) => {
|
||||
if (count === undefined) return;
|
||||
setStatusCounts(prev => ({
|
||||
...prev,
|
||||
[status]: count
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// 复制手机号
|
||||
const copyPhone = (phone: string) => {
|
||||
@@ -148,12 +177,6 @@ const CustomerIndex = () => {
|
||||
// 转换为天数,向上取整
|
||||
const remainingDays = Math.ceil(remainingMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
console.log('=== 基于过期时间计算 ===');
|
||||
console.log('过期时间:', expirationTime);
|
||||
console.log('当前时间:', now.toLocaleString());
|
||||
console.log('剩余天数:', remainingDays);
|
||||
console.log('======================');
|
||||
|
||||
return Math.max(0, remainingDays);
|
||||
}
|
||||
|
||||
@@ -184,12 +207,6 @@ const CustomerIndex = () => {
|
||||
// 计算剩余保护天数
|
||||
const remainingDays = protectionPeriod - daysPassed;
|
||||
|
||||
console.log('=== 基于申请时间计算 ===');
|
||||
console.log('申请时间:', applyTime);
|
||||
console.log('已过去天数:', daysPassed);
|
||||
console.log('剩余保护天数:', remainingDays);
|
||||
console.log('======================');
|
||||
|
||||
return Math.max(0, remainingDays);
|
||||
} catch (error) {
|
||||
console.error('日期计算错误:', error);
|
||||
@@ -200,19 +217,24 @@ const CustomerIndex = () => {
|
||||
|
||||
// 获取客户数据
|
||||
const fetchCustomerData = useCallback(async (statusFilter?: CustomerStatus, resetPage = false, targetPage?: number) => {
|
||||
const requestSeq = ++requestSeqRef.current;
|
||||
setLoading(true);
|
||||
try {
|
||||
const currentPage = resetPage ? 1 : (targetPage || 1);
|
||||
|
||||
// 构建API参数,根据状态筛选
|
||||
// 非超管查看自己提交的(userId)或分配给自己的(receptionistId)的客户;超管查询全部
|
||||
const params = buildCustomerQueryParams(currentPage);
|
||||
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
|
||||
// 非管理员查看自己提交的(userId)或分配给自己的(receptionistId)的客户;管理员查询全部
|
||||
const currentStatus = statusFilter || activeTab;
|
||||
const params = buildCustomerQueryParams(currentPage, PAGE_LIMIT);
|
||||
const applyStatus = mapCustomerStatusToApplyStatus(currentStatus);
|
||||
if (applyStatus !== undefined) {
|
||||
params.applyStatus = applyStatus;
|
||||
}
|
||||
|
||||
const res = await pageShopDealerApply(params);
|
||||
if (requestSeq !== requestSeqRef.current) return;
|
||||
|
||||
updateStatusCount(currentStatus, res?.count);
|
||||
|
||||
if (res?.list && res.list.length > 0) {
|
||||
// 正确映射状态并计算保护天数
|
||||
@@ -230,7 +252,7 @@ const CustomerIndex = () => {
|
||||
}
|
||||
|
||||
// 正确判断是否还有更多数据
|
||||
const hasMoreData = res.list.length >= 10; // 假设每页10条数据
|
||||
const hasMoreData = currentPage * PAGE_LIMIT < (res.count || 0);
|
||||
setHasMore(hasMoreData);
|
||||
} else {
|
||||
if (resetPage || currentPage === 1) {
|
||||
@@ -247,9 +269,11 @@ const CustomerIndex = () => {
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (requestSeq === requestSeqRef.current) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [activeTab, buildCustomerQueryParams]);
|
||||
}, [activeTab, buildCustomerQueryParams, updateStatusCount]);
|
||||
|
||||
const reloadMore = async () => {
|
||||
if (loading || !hasMore) return; // 防止重复加载
|
||||
@@ -268,56 +292,63 @@ const CustomerIndex = () => {
|
||||
}, [searchValue]);
|
||||
|
||||
// 根据搜索条件筛选数据(状态筛选已在API层面处理)
|
||||
const getFilteredList = () => {
|
||||
let filteredList = list;
|
||||
const filteredList = useMemo(() => {
|
||||
const keyword = displaySearchValue.trim().toLowerCase();
|
||||
if (!keyword) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 按搜索关键词筛选
|
||||
if (displaySearchValue.trim()) {
|
||||
const keyword = displaySearchValue.trim().toLowerCase();
|
||||
filteredList = filteredList.filter(customer =>
|
||||
return list.filter(customer =>
|
||||
(customer.realName && customer.realName.toLowerCase().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))
|
||||
);
|
||||
}
|
||||
|
||||
return filteredList;
|
||||
};
|
||||
|
||||
// 获取各状态的统计数量
|
||||
const [statusCounts, setStatusCounts] = useState({
|
||||
all: 0,
|
||||
pending: 0,
|
||||
signed: 0,
|
||||
cancelled: 0
|
||||
});
|
||||
);
|
||||
}, [displaySearchValue, list]);
|
||||
|
||||
// 获取所有状态的统计数量
|
||||
const fetchStatusCounts = useCallback(async () => {
|
||||
const fetchStatusCounts = useCallback(async (skipStatus?: CustomerStatus) => {
|
||||
try {
|
||||
const baseParams = buildCustomerQueryParams();
|
||||
const countParams = {
|
||||
...baseParams,
|
||||
page: 1,
|
||||
limit: COUNT_LIMIT
|
||||
};
|
||||
const countRequestConfigs = [
|
||||
{status: 'all' as CustomerStatus, params: {...countParams}},
|
||||
{status: 'pending' as CustomerStatus, params: {...countParams, applyStatus: 10}},
|
||||
{status: 'signed' as CustomerStatus, params: {...countParams, applyStatus: 20}},
|
||||
{status: 'cancelled' as CustomerStatus, params: {...countParams, applyStatus: 30}}
|
||||
].filter(item => item.status !== skipStatus);
|
||||
|
||||
// 并行获取各状态的数量
|
||||
const [allRes, pendingRes, signedRes, cancelledRes] = await Promise.all([
|
||||
pageShopDealerApply({...baseParams}), // 全部
|
||||
pageShopDealerApply({...baseParams, applyStatus: 10}), // 跟进中
|
||||
pageShopDealerApply({...baseParams, applyStatus: 20}), // 已签约
|
||||
pageShopDealerApply({...baseParams, applyStatus: 30}) // 已取消
|
||||
]);
|
||||
const countResponses = await Promise.all(
|
||||
countRequestConfigs.map(item => pageShopDealerApply(item.params))
|
||||
);
|
||||
|
||||
setStatusCounts({
|
||||
all: allRes?.count || 0,
|
||||
pending: pendingRes?.count || 0,
|
||||
signed: signedRes?.count || 0,
|
||||
cancelled: cancelledRes?.count || 0
|
||||
setStatusCounts(prev => {
|
||||
const next = {...prev};
|
||||
countRequestConfigs.forEach((item, index) => {
|
||||
next[item.status] = countResponses[index]?.count || 0;
|
||||
});
|
||||
return next;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取状态统计失败:', error);
|
||||
}
|
||||
}, [buildCustomerQueryParams]);
|
||||
|
||||
const scheduleStatusCountsRefresh = useCallback((skipStatus?: CustomerStatus) => {
|
||||
if (countRefreshTimerRef.current) {
|
||||
clearTimeout(countRefreshTimerRef.current);
|
||||
}
|
||||
|
||||
countRefreshTimerRef.current = setTimeout(() => {
|
||||
fetchStatusCounts(skipStatus).then();
|
||||
}, COUNT_REFRESH_DELAY);
|
||||
}, [fetchStatusCounts]);
|
||||
|
||||
const getStatusCounts = () => statusCounts;
|
||||
|
||||
// 取消操作
|
||||
@@ -335,7 +366,7 @@ const CustomerIndex = () => {
|
||||
setPage(1);
|
||||
setHasMore(true);
|
||||
fetchCustomerData(activeTab, true).then();
|
||||
fetchStatusCounts().then();
|
||||
scheduleStatusCountsRefresh(activeTab);
|
||||
})
|
||||
};
|
||||
|
||||
@@ -351,15 +382,18 @@ const CustomerIndex = () => {
|
||||
setPage(1);
|
||||
setHasMore(true);
|
||||
fetchCustomerData(activeTab, true).then();
|
||||
fetchStatusCounts().then();
|
||||
scheduleStatusCountsRefresh(activeTab);
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化统计数据
|
||||
// 清理延迟统计刷新定时器
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn) return;
|
||||
fetchStatusCounts().then();
|
||||
}, [fetchStatusCounts, isLoggedIn]);
|
||||
return () => {
|
||||
if (countRefreshTimerRef.current) {
|
||||
clearTimeout(countRefreshTimerRef.current);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 当activeTab变化时重新获取数据
|
||||
useEffect(() => {
|
||||
@@ -368,155 +402,166 @@ const CustomerIndex = () => {
|
||||
setPage(1); // 重置页码
|
||||
setHasMore(true); // 重置加载状态
|
||||
fetchCustomerData(activeTab, true);
|
||||
}, [activeTab, fetchCustomerData, isLoggedIn]);
|
||||
scheduleStatusCountsRefresh(activeTab);
|
||||
}, [activeTab, fetchCustomerData, isLoggedIn, scheduleStatusCountsRefresh]);
|
||||
|
||||
// 监听页面显示,当从其他页面返回时刷新数据
|
||||
useDidShow(() => {
|
||||
if (!isLoggedIn) return;
|
||||
if (!didShowReadyRef.current) {
|
||||
didShowReadyRef.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新当前tab的数据和统计信息
|
||||
setList([]);
|
||||
setPage(1);
|
||||
setHasMore(true);
|
||||
fetchCustomerData(activeTab, true);
|
||||
fetchStatusCounts();
|
||||
scheduleStatusCountsRefresh(activeTab);
|
||||
});
|
||||
|
||||
// 渲染客户项
|
||||
const renderCustomerItem = (customer: CustomerUser) => (
|
||||
<View key={customer.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex items-center mb-3">
|
||||
<View className="flex-1">
|
||||
<View className="flex items-center justify-between mb-1">
|
||||
<Text className="font-semibold text-gray-800 mr-2">
|
||||
{customer.dealerName}
|
||||
</Text>
|
||||
{customer.customerStatus && (
|
||||
<Tag type={getStatusTagType(customer.customerStatus)}>
|
||||
{getStatusText(customer.customerStatus)}
|
||||
</Tag>
|
||||
)}
|
||||
</View>
|
||||
<View className="flex items-center mb-1">
|
||||
<Space direction="vertical">
|
||||
<Text className="text-xs text-gray-500">联系人:{customer.realName}</Text>
|
||||
<View className="flex items-center">
|
||||
<Text className="text-xs text-gray-500" onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
makePhoneCall(customer.mobile || '');
|
||||
}}>联系电话:{customer.mobile}</Text>
|
||||
<View className="flex items-center ml-2">
|
||||
<Phone
|
||||
size={12}
|
||||
className="text-green-500 mr-2"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
makePhoneCall(customer.mobile || '');
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
className="text-xs text-blue-500 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
copyPhone(customer.mobile || '');
|
||||
}}
|
||||
>
|
||||
复制
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text className="text-xs text-gray-500">
|
||||
添加时间:{customer.createTime}
|
||||
</Text>
|
||||
</Space>
|
||||
</View>
|
||||
const renderCustomerItem = (customer: CustomerUser) => {
|
||||
const canHandle = canHandleCustomer(customer);
|
||||
|
||||
{/* 保护天数显示 */}
|
||||
{customer.applyStatus === 10 && (
|
||||
<View className="flex items-center my-1">
|
||||
<Text className="text-xs text-gray-500 mr-2">保护期:</Text>
|
||||
{customer.protectDays && customer.protectDays > 0 ? (
|
||||
<Text className={`text-xs px-2 py-1 rounded ${
|
||||
customer.protectDays <= 2
|
||||
? 'bg-red-100 text-red-600'
|
||||
: customer.protectDays <= 4
|
||||
? 'bg-orange-100 text-orange-600'
|
||||
: 'bg-green-100 text-green-600'
|
||||
}`}>
|
||||
剩余{customer.protectDays}天
|
||||
</Text>
|
||||
) : (
|
||||
<Text className="text-xs px-2 py-1 rounded bg-gray-100 text-gray-500">
|
||||
已过期
|
||||
</Text>
|
||||
return (
|
||||
<View key={customer.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex items-center mb-3">
|
||||
<View className="flex-1">
|
||||
<View className="flex items-center justify-between mb-1">
|
||||
<Text className="font-semibold text-gray-800 mr-2">
|
||||
{customer.dealerName}
|
||||
</Text>
|
||||
{customer.customerStatus && (
|
||||
<Tag type={getStatusTagType(customer.customerStatus)}>
|
||||
{getStatusText(customer.customerStatus)}
|
||||
</Tag>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View className={'flex items-center gap-2'}>
|
||||
<Text className="text-xs text-gray-500">报备人:{customer?.nickName}</Text>
|
||||
<AngleDoubleLeft size={12} className={'text-blue-500'} />
|
||||
<Text className={'text-xs text-gray-500'}>{customer?.refereeName}</Text>
|
||||
</View>
|
||||
|
||||
{/* 接待人员 */}
|
||||
{customer.receptionistName && (
|
||||
<View className="flex items-center my-1">
|
||||
<Text className="text-xs text-gray-500">接待人员:{customer.receptionistName}</Text>
|
||||
<View className="flex items-center mb-1">
|
||||
<Space direction="vertical">
|
||||
<Text className="text-xs text-gray-500">联系人:{customer.realName}</Text>
|
||||
<View className="flex items-center">
|
||||
<Text className="text-xs text-gray-500" onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
makePhoneCall(customer.mobile || '');
|
||||
}}>联系电话:{customer.mobile}</Text>
|
||||
<View className="flex items-center ml-2">
|
||||
<Phone
|
||||
size={12}
|
||||
className="text-green-500 mr-2"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
makePhoneCall(customer.mobile || '');
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
className="text-xs text-blue-500 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
copyPhone(customer.mobile || '');
|
||||
}}
|
||||
>
|
||||
复制
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text className="text-xs text-gray-500">
|
||||
添加时间:{customer.createTime}
|
||||
</Text>
|
||||
</Space>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 显示 comments 字段 */}
|
||||
<Space className="flex items-center">
|
||||
<Text className="text-xs text-gray-500">跟进情况:{customer.comments || '暂无'}</Text>
|
||||
<Text
|
||||
className="text-xs text-blue-500 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
editComments(customer);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Text>
|
||||
</Space>
|
||||
{/* 保护天数显示 */}
|
||||
{customer.applyStatus === 10 && (
|
||||
<View className="flex items-center my-1">
|
||||
<Text className="text-xs text-gray-500 mr-2">保护期:</Text>
|
||||
{customer.protectDays && customer.protectDays > 0 ? (
|
||||
<Text className={`text-xs px-2 py-1 rounded ${
|
||||
customer.protectDays <= 2
|
||||
? 'bg-red-100 text-red-600'
|
||||
: customer.protectDays <= 4
|
||||
? 'bg-orange-100 text-orange-600'
|
||||
: 'bg-green-100 text-green-600'
|
||||
}`}>
|
||||
剩余{customer.protectDays}天
|
||||
</Text>
|
||||
) : (
|
||||
<Text className="text-xs px-2 py-1 rounded bg-gray-100 text-gray-500">
|
||||
已过期
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View className={'flex items-center gap-2'}>
|
||||
<Text className="text-xs text-gray-500">报备人:{customer?.nickName}</Text>
|
||||
<AngleDoubleLeft size={12} className={'text-blue-500'} />
|
||||
<Text className={'text-xs text-gray-500'}>{customer?.refereeName}</Text>
|
||||
</View>
|
||||
|
||||
{/* 接待人员 */}
|
||||
{customer.receptionistName && (
|
||||
<View className="flex items-center my-1">
|
||||
<Text className="text-xs text-gray-500">接待人员:{customer.receptionistName}</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 显示 comments 字段 */}
|
||||
<Space className="flex items-center">
|
||||
<Text className="text-xs text-gray-500">跟进情况:{customer.comments || '暂无'}</Text>
|
||||
{canHandle && (
|
||||
<Text
|
||||
className="text-xs text-blue-500 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
editComments(customer);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Text>
|
||||
)}
|
||||
</Space>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 跟进中状态显示操作按钮 */}
|
||||
{(customer.applyStatus === 10 && customer.userId == Taro.getStorageSync('UserId')) && (
|
||||
<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>
|
||||
)}
|
||||
{(customer.applyStatus === 30 && customer.userId == Taro.getStorageSync('UserId')) && (
|
||||
<Space className="flex justify-end">
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleDelete(customer)}
|
||||
style={{backgroundColor: '#ff4d4f', color: 'white'}}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
{/* 跟进中状态仅接待人员/超管显示操作按钮 */}
|
||||
{(customer.applyStatus === 10 && canHandle) && (
|
||||
<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>
|
||||
)}
|
||||
{(customer.applyStatus === 30 && canHandle) && (
|
||||
<Space className="flex justify-end">
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleDelete(customer)}
|
||||
style={{backgroundColor: '#ff4d4f', color: 'white'}}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染客户列表
|
||||
const renderCustomerList = () => {
|
||||
const filteredList = getFilteredList();
|
||||
const isSearching = displaySearchValue.trim().length > 0;
|
||||
|
||||
return (
|
||||
|
||||
@@ -151,15 +151,14 @@ function UserCard() {
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={'header-bg pt-20'}>
|
||||
<View className={'p-4'}>
|
||||
<View className={'header-bg'}>
|
||||
<View className={'user-card-wrap'}>
|
||||
<View
|
||||
className={'user-card w-full flex flex-col justify-around rounded-xl shadow-sm'}
|
||||
className={'w-full flex flex-col justify-around'}
|
||||
style={{
|
||||
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', // 这种情况建议使用类名来控制样式(引入外联样式)
|
||||
// width: '720rpx',
|
||||
// margin: '10px auto 0px auto',
|
||||
height: '120px',
|
||||
height: '124px',
|
||||
// borderRadius: '22px 22px 0 0',
|
||||
}}
|
||||
>
|
||||
@@ -174,8 +173,8 @@ function UserCard() {
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<View className={'user-info flex flex-col px-2'}>
|
||||
<View className={'py-1 text-black font-bold max-w-28'}>{getDisplayName()}</View>
|
||||
<View className={'user-info flex flex-col px-3'}>
|
||||
<View className={'user-card__name py-1 font-bold'}>{getDisplayName()}</View>
|
||||
{isLoggedIn ? (
|
||||
<View className={'grade text-xs py-0'}>
|
||||
<Tag type="success">{getRoleName()}</Tag>
|
||||
@@ -184,8 +183,8 @@ function UserCard() {
|
||||
</View>
|
||||
</View>
|
||||
<View className={'gap-2 flex items-center'}>
|
||||
{isAdmin() && <Scan className={'text-gray-900'} size={24} onClick={() => navTo('/user/store/verification', true)} />}
|
||||
<View className={'mr-4 text-sm px-3 py-1 text-black border-gray-400 border-solid border-2 rounded-3xl'}
|
||||
{isAdmin() && <Scan className={'user-card__scan'} size={24} onClick={() => navTo('/user/store/verification', true)} />}
|
||||
<View className={'user-card__profile mr-4 text-sm px-3 py-1'}
|
||||
onClick={() => navTo('/user/profile/profile', true)}>
|
||||
{'个人资料'}
|
||||
</View>
|
||||
|
||||
@@ -2,7 +2,7 @@ import {Cell} from '@nutui/nutui-react-taro'
|
||||
import navTo from "@/utils/common";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {ArrowRight, LogisticsError, Tips, Ask} from '@nutui/icons-react-taro'
|
||||
import {ArrowRight, LogisticsError} from '@nutui/icons-react-taro'
|
||||
import {useUser} from '@/hooks/useUser'
|
||||
|
||||
const UserCell = () => {
|
||||
@@ -26,13 +26,36 @@ const UserCell = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<View className={'px-4'}>
|
||||
<View className={'p-4'}>
|
||||
|
||||
<Cell.Group divider={true} description={
|
||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||
<Text style={{marginTop: '12px'}}>我的服务</Text>
|
||||
</View>
|
||||
}>
|
||||
<Cell.Group divider={true}>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title="后台管理"
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={() => Taro.reLaunch({url: '/pages/index/index'})}
|
||||
/>
|
||||
</Cell.Group>
|
||||
|
||||
<Cell.Group divider={true}>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title="个人资料"
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={() => navTo('/user/profile/profile', true)}
|
||||
/>
|
||||
</Cell.Group>
|
||||
<Cell.Group divider={true}>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title="退出登录"
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={onLogout}
|
||||
/>
|
||||
</Cell.Group>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
style={{
|
||||
@@ -81,62 +104,42 @@ const UserCell = () => {
|
||||
{/* navTo('/user/userVerify/index', true)*/}
|
||||
{/* }}*/}
|
||||
{/*/>*/}
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title={
|
||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||
<Ask size={16}/>
|
||||
<Text className={'pl-3 text-sm'}>常见问题</Text>
|
||||
</View>
|
||||
}
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={() => {
|
||||
navTo('/user/help/index')
|
||||
}}
|
||||
/>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title={
|
||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||
<Tips size={16}/>
|
||||
<Text className={'pl-3 text-sm'}>关于我们</Text>
|
||||
</View>
|
||||
}
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={() => {
|
||||
navTo('/user/about/index')
|
||||
}}
|
||||
/>
|
||||
</Cell.Group>
|
||||
<Cell.Group divider={true} description={
|
||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||
<Text style={{marginTop: '12px'}}>账号管理</Text>
|
||||
</View>
|
||||
}>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title="个人资料"
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={() => navTo('/user/profile/profile', true)}
|
||||
/>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title="返回首页"
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={() => Taro.reLaunch({url: '/pages/index/index'})}
|
||||
/>
|
||||
<Cell
|
||||
className="nutui-cell-clickable"
|
||||
title="退出登录"
|
||||
align="center"
|
||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||
onClick={onLogout}
|
||||
/>
|
||||
</Cell.Group>
|
||||
{/* <Cell*/}
|
||||
{/* className="nutui-cell-clickable"*/}
|
||||
{/* title={*/}
|
||||
{/* <View style={{display: 'inline-flex', alignItems: 'center'}}>*/}
|
||||
{/* <Ask size={16}/>*/}
|
||||
{/* <Text className={'pl-3 text-sm'}>常见问题</Text>*/}
|
||||
{/* </View>*/}
|
||||
{/* }*/}
|
||||
{/* align="center"*/}
|
||||
{/* extra={<ArrowRight color="#cccccc" size={18}/>}*/}
|
||||
{/* onClick={() => {*/}
|
||||
{/* navTo('/user/help/index')*/}
|
||||
{/* }}*/}
|
||||
{/* />*/}
|
||||
{/* <Cell*/}
|
||||
{/* className="nutui-cell-clickable"*/}
|
||||
{/* title={*/}
|
||||
{/* <View style={{display: 'inline-flex', alignItems: 'center'}}>*/}
|
||||
{/* <Tips size={16}/>*/}
|
||||
{/* <Text className={'pl-3 text-sm'}>关于我们</Text>*/}
|
||||
{/* </View>*/}
|
||||
{/* }*/}
|
||||
{/* align="center"*/}
|
||||
{/* extra={<ArrowRight color="#cccccc" size={18}/>}*/}
|
||||
{/* onClick={() => {*/}
|
||||
{/* navTo('/user/about/index')*/}
|
||||
{/* }}*/}
|
||||
{/* />*/}
|
||||
{/*</Cell.Group>*/}
|
||||
{/*<Cell.Group divider={true} description={*/}
|
||||
{/* <View style={{display: 'inline-flex', alignItems: 'center'}}>*/}
|
||||
{/* <Text style={{marginTop: '12px'}}>账号管理</Text>*/}
|
||||
{/* </View>*/}
|
||||
{/*}>*/}
|
||||
|
||||
{/*</Cell.Group>*/}
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ const UserFooter = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'text-center py-4 w-full text-gray-300'} onClick={onLoginByPhone}>
|
||||
<div className={'text-center py-4 w-full text-gray-300 fixed bottom-2'} onClick={onLoginByPhone}>
|
||||
<div className={'text-xs text-gray-400 py-1'}>当前版本:{Version}</div>
|
||||
<div className={'text-xs text-gray-400 py-1'}>Copyright © { new Date().getFullYear() } {Copyright}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '我的',
|
||||
navigationStyle: 'custom',
|
||||
navigationBarBackgroundColor: '#e9fff2'
|
||||
navigationBarTitleText: '我的'
|
||||
})
|
||||
|
||||
@@ -1,4 +1,74 @@
|
||||
.header-bg{
|
||||
background: url('https://oss.wsdns.cn/20250621/edb5d4da976b4d97ba185cb7077d2858.jpg') no-repeat top center;
|
||||
background-size: 100%;
|
||||
.user-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #f2fbf6 0%, #f8fbff 38%, #f9fafb 100%);
|
||||
}
|
||||
|
||||
.header-bg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding-top: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* 右上角光晕点缀 */
|
||||
.header-bg::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
right: -40px;
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(59, 130, 246, 0.3) 0%, rgba(96, 165, 250, 0.1) 55%, transparent 75%);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.user-card-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
border-radius: 18px !important;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(247, 252, 255, 0.94) 100%);
|
||||
box-shadow: 0 16px 34px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.user-card-header {
|
||||
height: 100%;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.user-card .nut-avatar {
|
||||
border: 3px solid rgba(255, 255, 255, 0.94);
|
||||
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.user-card__name {
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
color: #ffffff;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.user-card__profile {
|
||||
border: 1px solid rgba(15, 118, 110, 0.22);
|
||||
border-radius: 999px;
|
||||
background: #ecfdf5;
|
||||
color: #0f766e;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.user-card__scan {
|
||||
padding: 6px;
|
||||
border-radius: 999px;
|
||||
background: #ecfeff;
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.grade .nut-tag {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import Taro from '@tarojs/taro';
|
||||
import {useEffect} from 'react'
|
||||
// import Taro from '@tarojs/taro';
|
||||
import UserCard from "./components/UserCard";
|
||||
import UserOrder from "./components/UserOrder";
|
||||
// import UserOrder from "./components/UserOrder";
|
||||
import UserCell from "./components/UserCell";
|
||||
import UserFooter from "./components/UserFooter";
|
||||
import {useUser} from "@/hooks/useUser";
|
||||
import {NavBar} from '@nutui/nutui-react-taro';
|
||||
import {Home} from '@nutui/icons-react-taro'
|
||||
import './user.scss'
|
||||
// import {NavBar} from '@nutui/nutui-react-taro';
|
||||
// import {Home} from '@nutui/icons-react-taro';
|
||||
import './user.scss';
|
||||
|
||||
function User() {
|
||||
const [statusBarHeight, setStatusBarHeight] = useState<number>()
|
||||
// const [statusBarHeight, setStatusBarHeight] = useState<number>()
|
||||
const {
|
||||
isAdmin
|
||||
} = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
Taro.getSystemInfo({
|
||||
success: (res) => {
|
||||
setStatusBarHeight(res.statusBarHeight)
|
||||
},
|
||||
})
|
||||
// Taro.getSystemInfo({
|
||||
// success: (res) => {
|
||||
// setStatusBarHeight(res.statusBarHeight)
|
||||
// },
|
||||
// })
|
||||
}, []);
|
||||
|
||||
/**
|
||||
@@ -28,11 +28,30 @@ function User() {
|
||||
*/
|
||||
if (isAdmin()) {
|
||||
return <>
|
||||
<div className={'w-full'} style={{
|
||||
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)'
|
||||
}}>
|
||||
<div className={'user-page w-full'}>
|
||||
{/* 装饰性背景 - 深蓝色渐变 + 圆点装饰 */}
|
||||
<div className={'h-64 w-full fixed top-0 z-0'}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #0f172a 0%, #1e3a5f 50%, #1e40af 100%)'
|
||||
}}>
|
||||
<div className="absolute w-32 h-32 rounded-full" style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
top: '-16px',
|
||||
right: '-16px'
|
||||
}}></div>
|
||||
<div className="absolute w-24 h-24 rounded-full" style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||
bottom: '-12px',
|
||||
left: '-12px'
|
||||
}}></div>
|
||||
<div className="absolute w-16 h-16 rounded-full" style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||
top: '60px',
|
||||
left: '120px'
|
||||
}}></div>
|
||||
</div>
|
||||
<UserCard/>
|
||||
<UserOrder/>
|
||||
{/*<UserOrder/>*/}
|
||||
<UserCell/>
|
||||
<UserFooter/>
|
||||
</div>
|
||||
@@ -41,31 +60,50 @@ function User() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavBar
|
||||
fixed={true}
|
||||
style={{marginTop: `${statusBarHeight}px`, backgroundColor: 'transparent'}}
|
||||
onBackClick={() => {
|
||||
}}
|
||||
left={
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
backgroundColor: 'white',
|
||||
textAlign: 'center',
|
||||
borderRadius: '50%',
|
||||
{/*<NavBar*/}
|
||||
{/* fixed={true}*/}
|
||||
{/* style={{marginTop: `${statusBarHeight}px`, backgroundColor: 'transparent'}}*/}
|
||||
{/* onBackClick={() => {*/}
|
||||
{/* }}*/}
|
||||
{/* left={*/}
|
||||
{/* <div style={{*/}
|
||||
{/* display: 'flex',*/}
|
||||
{/* alignItems: 'center',*/}
|
||||
{/* justifyContent: 'center',*/}
|
||||
{/* width: '30px',*/}
|
||||
{/* height: '30px',*/}
|
||||
{/* backgroundColor: 'white',*/}
|
||||
{/* textAlign: 'center',*/}
|
||||
{/* borderRadius: '50%',*/}
|
||||
{/* }}>*/}
|
||||
{/* <Home size={15} onClick={() => Taro.reLaunch({url: '/pages/index/index'})} style={{color: 'black', marginBottom: '1px'}} />*/}
|
||||
{/* </div>*/}
|
||||
{/* }*/}
|
||||
{/*>*/}
|
||||
{/* <span>我的</span>*/}
|
||||
{/*</NavBar>*/}
|
||||
<div className={'user-page w-full'}>
|
||||
{/* 装饰性背景 - 深蓝色渐变 + 圆点装饰 */}
|
||||
<div className={'h-40 w-full fixed top-0 z-0'}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #0f172a 0%, #1e3a5f 50%, #1e40af 100%)'
|
||||
}}>
|
||||
<Home size={15} onClick={() => Taro.reLaunch({url: '/pages/index/index'})} style={{color: 'black', marginBottom: '1px'}} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<span>我的</span>
|
||||
</NavBar>
|
||||
<div className={'w-full'} style={{
|
||||
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)'
|
||||
}}>
|
||||
<div className="absolute w-32 h-32 rounded-full" style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
top: '-16px',
|
||||
right: '-16px'
|
||||
}}></div>
|
||||
<div className="absolute w-24 h-24 rounded-full" style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||
bottom: '-12px',
|
||||
left: '-12px'
|
||||
}}></div>
|
||||
<div className="absolute w-16 h-16 rounded-full" style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||
top: '60px',
|
||||
left: '120px'
|
||||
}}></div>
|
||||
</div>
|
||||
<UserCard/>
|
||||
{/*<UserOrder/>*/}
|
||||
<UserCell/>
|
||||
|
||||
BIN
小程序系统使用说明书.docx
Normal file
BIN
小程序系统使用说明书.docx
Normal file
Binary file not shown.
BIN
系统使用说明书.docx
Normal file
BIN
系统使用说明书.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user