refactor(credit): 将客户跟进状态改为步骤状态
- 在 CreditMpCustomerParam 接口中添加 step 字段 - 替换原有的跟进状态相关常量和选项为步骤状态配置 - 新增 STEP_STATUS_TEXT 映射对象定义五个步骤状态 - 移除废弃的 getRowIdKey 函数和 FOLLOW_MAP_STORAGE_KEY 相关逻辑 - 添加 getStepCode、getStepText 和 getStepTagType 工具函数处理步骤状态 - 将 followStatus 相关状态管理替换为 stepCode 状态管理 - 更新 applyFilters 函数使用步骤状态进行筛选 - 修改界面渲染逻辑使用新的步骤状态替代原有跟进状态 - 更新弹窗组件从 followVisible 切换为 stepVisible 控制 - 移除状态选择按钮的注释并调整步骤状态选择逻辑
This commit is contained in:
@@ -55,5 +55,6 @@ export interface CreditMpCustomer {
|
||||
*/
|
||||
export interface CreditMpCustomerParam extends PageParam {
|
||||
id?: number;
|
||||
step?: number;
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
@@ -27,11 +27,16 @@ import { hasRole } from '@/utils/permission'
|
||||
|
||||
const PAGE_SIZE = 10
|
||||
|
||||
type FollowStatus = '全部' | '未联系' | '加微前沟通' | '跟进中' | '已成交' | '无意向'
|
||||
const STEP_STATUS_TEXT: Record<number, string> = {
|
||||
0: '未受理',
|
||||
1: '已受理',
|
||||
2: '材料提交',
|
||||
3: '合同签订',
|
||||
4: '执行回款',
|
||||
5: '完结'
|
||||
}
|
||||
|
||||
const FOLLOW_STATUS_OPTIONS: FollowStatus[] = ['全部', '未联系', '加微前沟通', '跟进中', '已成交', '无意向']
|
||||
|
||||
const FOLLOW_MAP_STORAGE_KEY = 'credit_company_follow_status_map'
|
||||
const STEP_OPTIONS = [0, 1, 2, 3, 4, 5].map(code => ({ code, text: STEP_STATUS_TEXT[code] }))
|
||||
|
||||
const safeParseJSON = <T,>(v: any): T | null => {
|
||||
try {
|
||||
@@ -44,8 +49,6 @@ const safeParseJSON = <T,>(v: any): T | null => {
|
||||
}
|
||||
}
|
||||
|
||||
const getRowIdKey = (c: CreditMpCustomer) => String(c?.id || '')
|
||||
|
||||
const splitPhones = (raw?: string) => {
|
||||
const text = String(raw || '').trim()
|
||||
if (!text) return []
|
||||
@@ -68,6 +71,42 @@ const getRowStatus = (c: CreditMpCustomer) => {
|
||||
return String((c as any)?.statusTxt || (c as any)?.statusText || '').trim()
|
||||
}
|
||||
|
||||
const getStepCode = (row: CreditMpCustomer): number | null => {
|
||||
const anyRow = row as any
|
||||
const raw =
|
||||
anyRow?.step ??
|
||||
anyRow?.stepStatus ??
|
||||
anyRow?.statusStep ??
|
||||
anyRow?.stepNum ??
|
||||
anyRow?.stepCode ??
|
||||
anyRow?.stepId ??
|
||||
undefined
|
||||
|
||||
const n = Number(raw)
|
||||
if (Number.isInteger(n) && n in STEP_STATUS_TEXT) return n
|
||||
|
||||
// 兼容:后端直接返回文字到 statusTxt
|
||||
const txt = getRowStatus(row)
|
||||
if (!txt) return null
|
||||
const found = Object.entries(STEP_STATUS_TEXT).find(([, t]) => t === txt)
|
||||
return found ? Number(found[0]) : null
|
||||
}
|
||||
|
||||
const getStepText = (row: CreditMpCustomer) => {
|
||||
const code = getStepCode(row)
|
||||
if (code != null) return STEP_STATUS_TEXT[code] || '处理中'
|
||||
const txt = getRowStatus(row)
|
||||
return txt || '处理中'
|
||||
}
|
||||
|
||||
const getStepTagType = (code: number | null): any => {
|
||||
if (code === 5) return 'success'
|
||||
if (code === 0) return 'primary'
|
||||
if (code === 4) return 'warning'
|
||||
if (code === 3) return 'danger'
|
||||
return 'primary'
|
||||
}
|
||||
|
||||
export default function CreditCompanyPage() {
|
||||
const serverPageRef = useRef(1)
|
||||
const staffLoadingPromiseRef = useRef<Promise<User[]> | null>(null)
|
||||
@@ -81,8 +120,8 @@ export default function CreditCompanyPage() {
|
||||
const [cityVisible, setCityVisible] = useState(false)
|
||||
const [cityText, setCityText] = useState<string>('全部')
|
||||
|
||||
const [followVisible, setFollowVisible] = useState(false)
|
||||
const [followStatus, setFollowStatus] = useState<FollowStatus>('全部')
|
||||
const [stepVisible, setStepVisible] = useState(false)
|
||||
const [stepCode, setStepCode] = useState<number | null>(null)
|
||||
|
||||
const [statusVisible, setStatusVisible] = useState(false)
|
||||
const [statusText, setStatusText] = useState<string>('全部')
|
||||
@@ -96,11 +135,6 @@ export default function CreditCompanyPage() {
|
||||
const [staffSelectedId, setStaffSelectedId] = useState<number | undefined>(undefined)
|
||||
const [assigning, setAssigning] = useState(false)
|
||||
|
||||
const [followMap, setFollowMap] = useState<Record<string, FollowStatus>>(() => {
|
||||
const raw = Taro.getStorageSync(FOLLOW_MAP_STORAGE_KEY)
|
||||
return safeParseJSON<Record<string, FollowStatus>>(raw) || {}
|
||||
})
|
||||
|
||||
const currentUser = useMemo(() => {
|
||||
return safeParseJSON<User>(Taro.getStorageSync('User')) || ({} as User)
|
||||
}, [])
|
||||
@@ -139,49 +173,11 @@ export default function CreditCompanyPage() {
|
||||
return map
|
||||
}, [staffList])
|
||||
|
||||
const getFollowStatus = useCallback(
|
||||
(c: CreditMpCustomer): FollowStatus => {
|
||||
const k = getRowIdKey(c)
|
||||
const stored = k ? followMap[k] : undefined
|
||||
if (stored) return stored
|
||||
return '未联系'
|
||||
},
|
||||
[followMap]
|
||||
)
|
||||
|
||||
const setFollowStatusFor = async (c: CreditMpCustomer) => {
|
||||
if (!c?.id) return
|
||||
try {
|
||||
const res = await Taro.showActionSheet({
|
||||
itemList: FOLLOW_STATUS_OPTIONS.filter(s => s !== '全部')
|
||||
})
|
||||
const next = FOLLOW_STATUS_OPTIONS.filter(s => s !== '全部')[res.tapIndex] as FollowStatus | undefined
|
||||
if (!next) return
|
||||
|
||||
setFollowMap(prev => {
|
||||
const k = getRowIdKey(c)
|
||||
const merged = { ...prev, [k]: next }
|
||||
Taro.setStorageSync(FOLLOW_MAP_STORAGE_KEY, merged)
|
||||
return merged
|
||||
})
|
||||
|
||||
// 若当前正在按状态筛选,则即时移除不匹配项,避免“改完状态但列表不变”的割裂感。
|
||||
if (followStatus !== '全部' && next !== followStatus && c.id) {
|
||||
const cid = Number(c.id)
|
||||
setList(prev => prev.filter(x => Number(x.id) !== cid))
|
||||
setSelectedIds(prev => prev.filter(id => id !== cid))
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = String((e as any)?.errMsg || (e as any)?.message || e || '')
|
||||
if (msg.includes('cancel')) return
|
||||
}
|
||||
}
|
||||
|
||||
const applyFilters = useCallback(
|
||||
(incoming: CreditMpCustomer[]) => {
|
||||
const city = cityText === '全部' ? '' : cityText
|
||||
const status = statusText === '全部' ? '' : statusText
|
||||
const follow = followStatus === '全部' ? '' : followStatus
|
||||
const step = stepCode == null ? null : stepCode
|
||||
|
||||
return incoming.filter(c => {
|
||||
if (c?.deleted === 1) return false
|
||||
@@ -196,14 +192,15 @@ export default function CreditCompanyPage() {
|
||||
if (!txt.includes(status)) return false
|
||||
}
|
||||
|
||||
if (follow) {
|
||||
if (getFollowStatus(c) !== follow) return false
|
||||
if (step != null) {
|
||||
const code = getStepCode(c)
|
||||
if (code == null || code !== step) return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
},
|
||||
[cityText, followStatus, getFollowStatus, statusText]
|
||||
[cityText, statusText, stepCode]
|
||||
)
|
||||
|
||||
const reload = useCallback(
|
||||
@@ -437,12 +434,12 @@ export default function CreditCompanyPage() {
|
||||
<Button size="small" fill="outline" onClick={() => setCityVisible(true)}>
|
||||
{cityText === '全部' ? '地区' : cityText}
|
||||
</Button>
|
||||
<Button size="small" fill="outline" onClick={() => setFollowVisible(true)}>
|
||||
{followStatus === '全部' ? '跟进状态' : followStatus}
|
||||
</Button>
|
||||
<Button size="small" fill="outline" onClick={() => setStatusVisible(true)}>
|
||||
{statusText === '全部' ? '状态' : statusText}
|
||||
<Button size="small" fill="outline" onClick={() => setStepVisible(true)}>
|
||||
{stepCode == null ? '跟进状态' : STEP_STATUS_TEXT[stepCode]}
|
||||
</Button>
|
||||
{/*<Button size="small" fill="outline" onClick={() => setStatusVisible(true)}>*/}
|
||||
{/* {statusText === '全部' ? '状态' : statusText}*/}
|
||||
{/*</Button>*/}
|
||||
<View className="flex-1" />
|
||||
<Button size="small" fill="outline" icon={<Copy />} onClick={copyPhones}>
|
||||
复制电话
|
||||
@@ -481,7 +478,8 @@ export default function CreditCompanyPage() {
|
||||
{list.map((c, idx) => {
|
||||
const id = Number(c.id)
|
||||
const selected = !!id && selectedIds.includes(id)
|
||||
const follow = getFollowStatus(c)
|
||||
const sCode = getStepCode(c)
|
||||
const sText = getStepText(c)
|
||||
const ownerName =
|
||||
// 兼容后端可能直接下发跟进人字段
|
||||
(c as any)?.realName ||
|
||||
@@ -536,13 +534,10 @@ export default function CreditCompanyPage() {
|
||||
)}
|
||||
</View>
|
||||
<Tag
|
||||
type={follow === '无意向' ? 'danger' : follow === '已成交' ? 'success' : 'primary'}
|
||||
onClick={(e: any) => {
|
||||
e?.stopPropagation?.()
|
||||
setFollowStatusFor(c)
|
||||
}}
|
||||
type={getStepTagType(sCode)}
|
||||
onClick={(e: any) => e?.stopPropagation?.()}
|
||||
>
|
||||
{follow}
|
||||
{sText}
|
||||
</Tag>
|
||||
</View>
|
||||
|
||||
@@ -650,27 +645,31 @@ export default function CreditCompanyPage() {
|
||||
onClose={() => setCityVisible(false)}
|
||||
/>
|
||||
|
||||
<Popup
|
||||
visible={followVisible}
|
||||
position="bottom"
|
||||
style={{ height: '45vh' }}
|
||||
onClose={() => setFollowVisible(false)}
|
||||
>
|
||||
<Popup visible={stepVisible} position="bottom" style={{ height: '45vh' }} onClose={() => setStepVisible(false)}>
|
||||
<View className="p-4">
|
||||
<View className="flex items-center justify-between mb-3">
|
||||
<Text className="text-base font-medium">跟进状态</Text>
|
||||
<Text className="text-sm text-gray-500" onClick={() => setFollowVisible(false)}>
|
||||
<Text className="text-sm text-gray-500" onClick={() => setStepVisible(false)}>
|
||||
关闭
|
||||
</Text>
|
||||
</View>
|
||||
<CellGroup>
|
||||
{FOLLOW_STATUS_OPTIONS.map(s => (
|
||||
<Cell
|
||||
key={s}
|
||||
title={<Text className={s === followStatus ? 'text-blue-600' : ''}>{s}</Text>}
|
||||
key="ALL"
|
||||
title={<Text className={stepCode == null ? 'text-blue-600' : ''}>全部</Text>}
|
||||
onClick={() => {
|
||||
setFollowStatus(s)
|
||||
setFollowVisible(false)
|
||||
setStepCode(null)
|
||||
setStepVisible(false)
|
||||
reload(true).then()
|
||||
}}
|
||||
/>
|
||||
{STEP_OPTIONS.map(s => (
|
||||
<Cell
|
||||
key={s.code}
|
||||
title={<Text className={s.code === stepCode ? 'text-blue-600' : ''}>{s.text}</Text>}
|
||||
onClick={() => {
|
||||
setStepCode(s.code)
|
||||
setStepVisible(false)
|
||||
reload(true).then()
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user