Files
template-10579/src/credit/creditMpCustomer/index.tsx
赵忠林 f306e7a9f7 feat(credit): 重构客户管理模块并新增信用客户功能
- 将公司相关页面重命名为客户管理页面
- 新增信用客户详情、编辑、跟进页面
- 实现客户状态跟踪和跟进流程
- 更新应用配置中的页面路由映射
- 优化订单详情页面的时间轴显示逻辑
2026-03-19 23:06:30 +08:00

166 lines
6.1 KiB
TypeScript

import { useCallback, useMemo, useState } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { Button, Cell, CellGroup, ConfigProvider, Empty, Space } from '@nutui/nutui-react-taro'
import { ArrowRight, CheckNormal, Checked } from '@nutui/icons-react-taro'
import type { CreditMpCustomer } from '@/api/credit/creditMpCustomer/model'
import { pageCreditMpCustomer, removeCreditMpCustomer, updateCreditMpCustomer } from '@/api/credit/creditMpCustomer'
export default function CreditMpCustomerListPage() {
const [list, setList] = useState<CreditMpCustomer[]>([])
const [count, setCount] = useState(0)
const [page, setPage] = useState(1)
const limit = 20
const [loading, setLoading] = useState(false)
const [loadingMore, setLoadingMore] = useState(false)
const hasMore = useMemo(() => list.length < count, [count, list.length])
const fetchPage = useCallback(
async (opts: { nextPage: number; replace: boolean }) => {
try {
if (opts.replace) setLoading(true)
else setLoadingMore(true)
const res = await pageCreditMpCustomer({ page: opts.nextPage, limit } as any)
const incoming = (res?.list || []) as CreditMpCustomer[]
const total = Number(res?.count || 0)
setCount(Number.isFinite(total) ? total : 0)
setPage(opts.nextPage)
setList(prev => (opts.replace ? incoming : prev.concat(incoming)))
} catch (e) {
console.error('获取数据失败:', e)
Taro.showToast({ title: (e as any)?.message || '获取数据失败', icon: 'none' })
} finally {
setLoading(false)
setLoadingMore(false)
}
},
[limit]
)
const reload = useCallback(async () => {
await fetchPage({ nextPage: 1, replace: true })
}, [fetchPage])
const loadMore = useCallback(async () => {
if (loading || loadingMore || !hasMore) return
await fetchPage({ nextPage: page + 1, replace: false })
}, [fetchPage, hasMore, loading, loadingMore, page])
useDidShow(() => {
reload()
})
const goAdd = () => Taro.navigateTo({ url: '/credit/creditMpCustomer/add' })
const goEdit = (id?: number) => {
if (!id) return
Taro.navigateTo({ url: `/credit/creditMpCustomer/add?id=${id}` })
}
const onToggleRecommend = async (row: CreditMpCustomer) => {
if (!row?.id) return
const next = row.recommend === 1 ? 0 : 1
try {
await updateCreditMpCustomer({ ...row, recommend: next })
Taro.showToast({ title: next === 1 ? '已设为推荐' : '已取消推荐', icon: 'success' })
reload()
} catch (e) {
console.error('更新失败:', e)
Taro.showToast({ title: (e as any)?.message || '更新失败', icon: 'none' })
}
}
const onDel = async (id?: number) => {
if (!id) return
const res = await Taro.showModal({ title: '提示', content: '确认删除该记录?' })
if (!res.confirm) return
try {
await removeCreditMpCustomer(id)
Taro.showToast({ title: '删除成功', icon: 'success' })
reload()
} catch (e) {
console.error('删除失败:', e)
Taro.showToast({ title: (e as any)?.message || '删除失败', icon: 'none' })
}
}
return (
<View className="bg-gray-50 min-h-screen">
<ConfigProvider>
<View className="px-3 pt-3">
<Space>
<Button type="primary" size="small" onClick={goAdd}>
</Button>
<Button size="small" loading={loading} onClick={reload}>
</Button>
</Space>
</View>
{!list.length ? (
<View className="px-3 pt-10">
<Empty description={loading ? '加载中...' : '暂无数据'} />
</View>
) : (
<View className="px-3 pt-3 pb-6">
<View className="mb-2 text-xs text-gray-500 flex items-center justify-between">
<Text>{count}</Text>
<Text>
{list.length}
</Text>
</View>
<CellGroup>
{list.map(row => {
const recommended = row.recommend === 1
const title = row.toUser || '-'
const price = row.price ? `${row.price}` : '-'
const years = row.years ? `${row.years}` : '-'
const location = [row.province, row.city, row.region].filter(Boolean).join(' ')
const desc = `${price} · ${years}${location ? ` · ${location}` : ''}`
return (
<View key={row.id} onLongPress={() => onDel(row.id)}>
<Cell
title={title}
description={desc}
extra={
<View className="flex items-center gap-3">
<View
className="flex items-center"
onClick={e => {
e.stopPropagation()
onToggleRecommend(row)
}}
>
{recommended ? <Checked size={16} /> : <CheckNormal size={16} />}
<Text className="ml-1 text-xs text-gray-500">{recommended ? '推荐' : '未推荐'}</Text>
</View>
<ArrowRight color="#cccccc" />
</View>
}
onClick={() => goEdit(row.id)}
/>
</View>
)
})}
</CellGroup>
<View className="mt-3 flex justify-center">
<Button fill="none" size="small" style={{ color: '#bdbdbd' }} disabled={!hasMore || loadingMore} onClick={loadMore}>
{hasMore ? (loadingMore ? '加载中...' : '加载更多') : '没有更多了'}
</Button>
</View>
<View className="mt-2 text-xs text-gray-400">
</View>
</View>
)}
</ConfigProvider>
</View>
)
}