feat(credit): 重构客户管理页面为订单管理功能
- 在app.config.ts中添加creditMpCustomer相关页面配置 - 将src/credit/customer/index.config.ts中的标题从'客户联系人管理'改为'企业经办人订单管理' - 重写src/credit/customer/index.tsx文件,将原有客户列表功能替换为企业经办人订单列表 - 实现订单搜索、筛选和详情查看功能 - 添加mock数据用于展示企业经办人、企业名称和跟进人信息 - 创建新的API模型和接口文件用于小程序端客户管理功能
This commit is contained in:
101
src/api/credit/creditMpCustomer/index.ts
Normal file
101
src/api/credit/creditMpCustomer/index.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult, PageResult } from '@/api/index';
|
||||||
|
import type { CreditMpCustomer, CreditMpCustomerParam } from './model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询小程序端客户
|
||||||
|
*/
|
||||||
|
export async function pageCreditMpCustomer(params: CreditMpCustomerParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<CreditMpCustomer>>>(
|
||||||
|
'/credit/credit-mp-customer/page',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询小程序端客户列表
|
||||||
|
*/
|
||||||
|
export async function listCreditMpCustomer(params?: CreditMpCustomerParam) {
|
||||||
|
const res = await request.get<ApiResult<CreditMpCustomer[]>>(
|
||||||
|
'/credit/credit-mp-customer',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加小程序端客户
|
||||||
|
*/
|
||||||
|
export async function addCreditMpCustomer(data: CreditMpCustomer) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/credit/credit-mp-customer',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改小程序端客户
|
||||||
|
*/
|
||||||
|
export async function updateCreditMpCustomer(data: CreditMpCustomer) {
|
||||||
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
|
'/credit/credit-mp-customer',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除小程序端客户
|
||||||
|
*/
|
||||||
|
export async function removeCreditMpCustomer(id?: number) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/credit/credit-mp-customer/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除小程序端客户
|
||||||
|
*/
|
||||||
|
export async function removeBatchCreditMpCustomer(data: (number | undefined)[]) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/credit/credit-mp-customer/batch',
|
||||||
|
{
|
||||||
|
data
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询小程序端客户
|
||||||
|
*/
|
||||||
|
export async function getCreditMpCustomer(id: number) {
|
||||||
|
const res = await request.get<ApiResult<CreditMpCustomer>>(
|
||||||
|
'/credit/credit-mp-customer/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
57
src/api/credit/creditMpCustomer/model/index.ts
Normal file
57
src/api/credit/creditMpCustomer/model/index.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序端客户
|
||||||
|
*/
|
||||||
|
export interface CreditMpCustomer {
|
||||||
|
// ID
|
||||||
|
id?: number;
|
||||||
|
// 拖欠方
|
||||||
|
toUser?: string;
|
||||||
|
// 拖欠金额
|
||||||
|
price?: string;
|
||||||
|
// 拖欠年数
|
||||||
|
years?: string;
|
||||||
|
// 链接
|
||||||
|
url?: string;
|
||||||
|
// 状态
|
||||||
|
statusTxt?: string;
|
||||||
|
// 企业ID
|
||||||
|
companyId?: number;
|
||||||
|
// 所在省份
|
||||||
|
province?: string;
|
||||||
|
// 所在城市
|
||||||
|
city?: string;
|
||||||
|
// 所在辖区
|
||||||
|
region?: string;
|
||||||
|
// 文件路径
|
||||||
|
files?: string;
|
||||||
|
// 是否有数据
|
||||||
|
hasData?: string;
|
||||||
|
// 备注
|
||||||
|
comments?: string;
|
||||||
|
// 是否推荐
|
||||||
|
recommend?: number;
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber?: number;
|
||||||
|
// 状态, 0正常, 1冻结
|
||||||
|
status?: number;
|
||||||
|
// 是否删除, 0否, 1是
|
||||||
|
deleted?: number;
|
||||||
|
// 用户ID
|
||||||
|
userId?: number;
|
||||||
|
// 租户id
|
||||||
|
tenantId?: number;
|
||||||
|
// 创建时间
|
||||||
|
createTime?: string;
|
||||||
|
// 修改时间
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序端客户搜索条件
|
||||||
|
*/
|
||||||
|
export interface CreditMpCustomerParam extends PageParam {
|
||||||
|
id?: number;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
@@ -119,8 +119,7 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "credit",
|
"root": "credit",
|
||||||
"pages": [
|
"pages": ["data/index",
|
||||||
"data/index",
|
|
||||||
"customer/index",
|
"customer/index",
|
||||||
"order/index",
|
"order/index",
|
||||||
"order/add",
|
"order/add",
|
||||||
@@ -128,8 +127,9 @@ export default {
|
|||||||
"company/add",
|
"company/add",
|
||||||
"company/detail",
|
"company/detail",
|
||||||
"company/follow-step1",
|
"company/follow-step1",
|
||||||
"company/edit"
|
"company/edit",
|
||||||
]
|
'creditMpCustomer/index',
|
||||||
|
'creditMpCustomer/add',]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
window: {
|
window: {
|
||||||
|
|||||||
180
src/app.config.ts.backup.1773665957890
Normal file
180
src/app.config.ts.backup.1773665957890
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
export default {
|
||||||
|
pages: [
|
||||||
|
'pages/index/index',
|
||||||
|
'pages/cart/cart',
|
||||||
|
'pages/find/find',
|
||||||
|
'pages/user/user'
|
||||||
|
],
|
||||||
|
"subpackages": [
|
||||||
|
{
|
||||||
|
"root": "passport",
|
||||||
|
"pages": [
|
||||||
|
"login",
|
||||||
|
"register",
|
||||||
|
"forget",
|
||||||
|
"setting",
|
||||||
|
"agreement",
|
||||||
|
"sms-login",
|
||||||
|
'qr-login/index',
|
||||||
|
'qr-confirm/index',
|
||||||
|
'unified-qr/index'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "cms",
|
||||||
|
"pages": [
|
||||||
|
'category/index',
|
||||||
|
"detail/index"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "coupon",
|
||||||
|
"pages": [
|
||||||
|
"index"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "user",
|
||||||
|
"pages": [
|
||||||
|
"order/order",
|
||||||
|
"order/logistics/index",
|
||||||
|
"order/evaluate/index",
|
||||||
|
"order/refund/index",
|
||||||
|
"order/progress/index",
|
||||||
|
"company/company",
|
||||||
|
"profile/profile",
|
||||||
|
"setting/setting",
|
||||||
|
"userVerify/index",
|
||||||
|
"address/index",
|
||||||
|
"address/add",
|
||||||
|
"address/wxAddress",
|
||||||
|
"help/index",
|
||||||
|
"about/index",
|
||||||
|
"wallet/wallet",
|
||||||
|
"coupon/index",
|
||||||
|
"points/points",
|
||||||
|
"ticket/index",
|
||||||
|
"ticket/use",
|
||||||
|
"ticket/orders/index",
|
||||||
|
// "gift/index",
|
||||||
|
// "gift/redeem",
|
||||||
|
// "gift/detail",
|
||||||
|
// "gift/add",
|
||||||
|
"store/verification",
|
||||||
|
"store/orders/index",
|
||||||
|
"theme/index",
|
||||||
|
"poster/poster",
|
||||||
|
"chat/conversation/index",
|
||||||
|
"chat/message/index",
|
||||||
|
"chat/message/add",
|
||||||
|
"chat/message/detail"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "dealer",
|
||||||
|
"pages": [
|
||||||
|
"index",
|
||||||
|
"apply/add",
|
||||||
|
"withdraw/index",
|
||||||
|
"orders/index",
|
||||||
|
"capital/index",
|
||||||
|
"team/index",
|
||||||
|
"qrcode/index",
|
||||||
|
"invite-stats/index",
|
||||||
|
"info"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "shop",
|
||||||
|
"pages": [
|
||||||
|
'category/index',
|
||||||
|
'orderDetail/index',
|
||||||
|
'goodsDetail/index',
|
||||||
|
'orderConfirm/index',
|
||||||
|
'orderConfirmCart/index',
|
||||||
|
'comments/index',
|
||||||
|
'search/index']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "store",
|
||||||
|
"pages": [
|
||||||
|
"index",
|
||||||
|
"orders/index"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "rider",
|
||||||
|
"pages": [
|
||||||
|
"index",
|
||||||
|
"orders/index",
|
||||||
|
"ticket/verification/index"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "admin",
|
||||||
|
"pages": [
|
||||||
|
"index",
|
||||||
|
"article/index",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "credit",
|
||||||
|
"pages": [
|
||||||
|
"data/index",
|
||||||
|
"customer/index",
|
||||||
|
"order/index",
|
||||||
|
"order/add",
|
||||||
|
"company/index",
|
||||||
|
"company/add",
|
||||||
|
"company/detail",
|
||||||
|
"company/follow-step1",
|
||||||
|
"company/edit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
window: {
|
||||||
|
backgroundTextStyle: 'dark',
|
||||||
|
navigationBarBackgroundColor: '#fff',
|
||||||
|
navigationBarTitleText: 'WeChat',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
},
|
||||||
|
tabBar: {
|
||||||
|
custom: false,
|
||||||
|
color: "#8a8a8a",
|
||||||
|
selectedColor: "#e60012",
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
pagePath: "pages/index/index",
|
||||||
|
iconPath: "assets/tabbar/home.png",
|
||||||
|
selectedIconPath: "assets/tabbar/home-active.png",
|
||||||
|
text: "首页",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: "pages/find/find",
|
||||||
|
iconPath: "assets/tabbar/shop.png",
|
||||||
|
selectedIconPath: "assets/tabbar/shop-active.png",
|
||||||
|
text: "网点",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: "pages/user/user",
|
||||||
|
iconPath: "assets/tabbar/user.png",
|
||||||
|
selectedIconPath: "assets/tabbar/user-active.png",
|
||||||
|
text: "我的",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
requiredPrivateInfos: [
|
||||||
|
"getLocation",
|
||||||
|
"chooseLocation",
|
||||||
|
"chooseAddress"
|
||||||
|
],
|
||||||
|
permission: {
|
||||||
|
"scope.userLocation": {
|
||||||
|
"desc": "你的位置信息将用于小程序位置接口的效果展示"
|
||||||
|
},
|
||||||
|
"scope.writePhotosAlbum": {
|
||||||
|
"desc": "用于保存小程序码到相册,方便分享给好友"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: '客户联系人管理',
|
navigationBarTitleText: '企业经办人订单管理',
|
||||||
navigationBarTextStyle: 'black',
|
navigationBarTextStyle: 'black',
|
||||||
navigationBarBackgroundColor: '#ffffff'
|
navigationBarBackgroundColor: '#ffffff'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,654 +1,115 @@
|
|||||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import Taro, { useDidShow } from '@tarojs/taro'
|
|
||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
import {
|
import { ConfigProvider, Empty, Popup, SearchBar } from '@nutui/nutui-react-taro'
|
||||||
Address,
|
|
||||||
Button,
|
|
||||||
Cell,
|
|
||||||
CellGroup,
|
|
||||||
Checkbox,
|
|
||||||
ConfigProvider,
|
|
||||||
Empty,
|
|
||||||
InfiniteLoading,
|
|
||||||
Loading,
|
|
||||||
Popup,
|
|
||||||
PullToRefresh,
|
|
||||||
SearchBar,
|
|
||||||
Tag
|
|
||||||
} from '@nutui/nutui-react-taro'
|
|
||||||
import { Copy, Phone } from '@nutui/icons-react-taro'
|
|
||||||
|
|
||||||
import RegionData from '@/api/json/regions-data.json'
|
type AgentOrderRow = {
|
||||||
import { pageCreditCompany, updateCreditCompany } from '@/api/credit/creditCompany'
|
id: string
|
||||||
import type { CreditCompany } from '@/api/credit/creditCompany/model'
|
agentName: string
|
||||||
import { listUsers } from '@/api/system/user'
|
companyName: string
|
||||||
import type { User } from '@/api/system/user/model'
|
follower: string
|
||||||
import { hasRole } from '@/utils/permission'
|
// 预留:若经办人为老板,可用于后续扩展“老板名下多企业订单”
|
||||||
|
isBoss?: boolean
|
||||||
const PAGE_SIZE = 10
|
|
||||||
|
|
||||||
type FollowStatus = '全部' | '未联系' | '加微前沟通' | '跟进中' | '已成交' | '无意向'
|
|
||||||
|
|
||||||
const FOLLOW_STATUS_OPTIONS: FollowStatus[] = ['全部', '未联系', '加微前沟通', '跟进中', '已成交', '无意向']
|
|
||||||
|
|
||||||
const FOLLOW_MAP_STORAGE_KEY = 'credit_company_follow_status_map'
|
|
||||||
|
|
||||||
const safeParseJSON = <T,>(v: any): T | null => {
|
|
||||||
try {
|
|
||||||
if (!v) return null
|
|
||||||
if (typeof v === 'object') return v as T
|
|
||||||
if (typeof v === 'string') return JSON.parse(v) as T
|
|
||||||
return null
|
|
||||||
} catch (_e) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCompanyIdKey = (c: CreditCompany) => String(c?.id || '')
|
const MOCK_LIST: AgentOrderRow[] = [
|
||||||
|
{ id: '1', agentName: '邓莉莉', companyName: '网宿公司', follower: '章鱼' },
|
||||||
const splitPhones = (raw?: string) => {
|
{ id: '2', agentName: '邓莉莉', companyName: '飞数公司', follower: '章鱼' }
|
||||||
const text = String(raw || '').trim()
|
]
|
||||||
if (!text) return []
|
|
||||||
// 常见分隔符:逗号/顿号/分号/换行/空格
|
|
||||||
return text
|
|
||||||
.split(/[\s,,;;、\n\r]+/g)
|
|
||||||
.map(s => s.trim())
|
|
||||||
.filter(Boolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCompanyPhones = (c: CreditCompany) => {
|
|
||||||
const arr = [...splitPhones(c.tel), ...splitPhones(c.moreTel)]
|
|
||||||
return Array.from(new Set(arr))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCompanyIndustry = (c: CreditCompany) => {
|
|
||||||
// 兼容:不同数据源字段可能不一致,优先取更具体的大类
|
|
||||||
return String(
|
|
||||||
c.nationalStandardIndustryCategories6 ||
|
|
||||||
c.nationalStandardIndustryCategories2 ||
|
|
||||||
c.nationalStandardIndustryCategories ||
|
|
||||||
c.institutionType ||
|
|
||||||
''
|
|
||||||
).trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function CreditCompanyPage() {
|
|
||||||
const serverPageRef = useRef(1)
|
|
||||||
const [list, setList] = useState<CreditCompany[]>([])
|
|
||||||
const [hasMore, setHasMore] = useState(true)
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [error, setError] = useState<string | null>(null)
|
|
||||||
|
|
||||||
|
export default function CreditCustomerPage() {
|
||||||
const [searchValue, setSearchValue] = useState('')
|
const [searchValue, setSearchValue] = useState('')
|
||||||
|
const [detailVisible, setDetailVisible] = useState(false)
|
||||||
|
const [activeRow, setActiveRow] = useState<AgentOrderRow | null>(null)
|
||||||
|
|
||||||
const [cityVisible, setCityVisible] = useState(false)
|
const filteredList = useMemo(() => {
|
||||||
const [cityText, setCityText] = useState<string>('全部')
|
const q = searchValue.trim()
|
||||||
|
if (!q) return MOCK_LIST
|
||||||
|
return MOCK_LIST.filter(r => String(r.agentName || '').includes(q))
|
||||||
|
}, [searchValue])
|
||||||
|
|
||||||
const [followVisible, setFollowVisible] = useState(false)
|
const totalText = useMemo(() => `${filteredList.length}个订单`, [filteredList.length])
|
||||||
const [followStatus, setFollowStatus] = useState<FollowStatus>('全部')
|
|
||||||
|
|
||||||
const [industryVisible, setIndustryVisible] = useState(false)
|
|
||||||
const [industryText, setIndustryText] = useState<string>('全部')
|
|
||||||
|
|
||||||
const [selectMode, setSelectMode] = useState(false)
|
|
||||||
const [selectedIds, setSelectedIds] = useState<number[]>([])
|
|
||||||
|
|
||||||
const [staffPopupVisible, setStaffPopupVisible] = useState(false)
|
|
||||||
const [staffLoading, setStaffLoading] = useState(false)
|
|
||||||
const [staffList, setStaffList] = useState<User[]>([])
|
|
||||||
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)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const canAssign = useMemo(() => {
|
|
||||||
// 超级管理员:允许分配并更改客户归属(userId)
|
|
||||||
if (currentUser?.isSuperAdmin) return true
|
|
||||||
if (hasRole('superAdmin')) return true
|
|
||||||
return false
|
|
||||||
}, [currentUser?.isSuperAdmin])
|
|
||||||
|
|
||||||
const cityOptions = useMemo(() => {
|
|
||||||
// NutUI Address options: [{ text, value, children }]
|
|
||||||
// @ts-ignore
|
|
||||||
return (RegionData || []).map(a => ({
|
|
||||||
value: a.label,
|
|
||||||
text: a.label,
|
|
||||||
children: (a.children || []).map(b => ({
|
|
||||||
value: b.label,
|
|
||||||
text: b.label,
|
|
||||||
children: (b.children || []).map(c => ({
|
|
||||||
value: c.label,
|
|
||||||
text: c.label
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const staffNameMap = useMemo(() => {
|
|
||||||
const map = new Map<number, string>()
|
|
||||||
for (const u of staffList) {
|
|
||||||
if (!u?.userId) continue
|
|
||||||
const name = String(u.realName || u.nickname || u.username || `员工${u.userId}`)
|
|
||||||
map.set(u.userId, name)
|
|
||||||
}
|
|
||||||
return map
|
|
||||||
}, [staffList])
|
|
||||||
|
|
||||||
const getFollowStatus = useCallback(
|
|
||||||
(c: CreditCompany): FollowStatus => {
|
|
||||||
const k = getCompanyIdKey(c)
|
|
||||||
const stored = k ? followMap[k] : undefined
|
|
||||||
if (stored) return stored
|
|
||||||
return '未联系'
|
|
||||||
},
|
|
||||||
[followMap]
|
|
||||||
)
|
|
||||||
|
|
||||||
const setFollowStatusFor = async (c: CreditCompany) => {
|
|
||||||
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 = getCompanyIdKey(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: CreditCompany[]) => {
|
|
||||||
const city = cityText === '全部' ? '' : cityText
|
|
||||||
const industry = industryText === '全部' ? '' : industryText
|
|
||||||
const follow = followStatus === '全部' ? '' : followStatus
|
|
||||||
|
|
||||||
return incoming.filter(c => {
|
|
||||||
if (c?.deleted === 1) return false
|
|
||||||
|
|
||||||
if (city) {
|
|
||||||
const full = [c.province, c.city, c.region].filter(Boolean).join(' ')
|
|
||||||
if (!full.includes(city) && String(c.city || '') !== city) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (industry) {
|
|
||||||
const ind = getCompanyIndustry(c)
|
|
||||||
if (!ind.includes(industry)) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (follow) {
|
|
||||||
if (getFollowStatus(c) !== follow) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[cityText, followStatus, getFollowStatus, industryText]
|
|
||||||
)
|
|
||||||
|
|
||||||
const reload = useCallback(
|
|
||||||
async (resetPage = false) => {
|
|
||||||
if (loading) return
|
|
||||||
setLoading(true)
|
|
||||||
setError(null)
|
|
||||||
|
|
||||||
if (resetPage) {
|
|
||||||
serverPageRef.current = 1
|
|
||||||
setHasMore(true)
|
|
||||||
setSelectedIds([])
|
|
||||||
setSelectMode(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextList = resetPage ? [] : list
|
|
||||||
let page = serverPageRef.current
|
|
||||||
let serverHasMore = true
|
|
||||||
|
|
||||||
// 当筛选条件较严格时,可能需要多拉几页才能拿到“至少一条匹配数据”
|
|
||||||
const MAX_PAGE_TRIES = 8
|
|
||||||
let tries = 0
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (tries < MAX_PAGE_TRIES) {
|
|
||||||
tries += 1
|
|
||||||
const params: any = {
|
|
||||||
page,
|
|
||||||
limit: PAGE_SIZE,
|
|
||||||
keywords: searchValue?.trim() || undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await pageCreditCompany(params)
|
|
||||||
const incoming = (res?.list || []) as CreditCompany[]
|
|
||||||
const filtered = applyFilters(incoming)
|
|
||||||
|
|
||||||
if (resetPage) {
|
|
||||||
nextList = filtered
|
|
||||||
resetPage = false
|
|
||||||
} else {
|
|
||||||
nextList = nextList.concat(filtered)
|
|
||||||
}
|
|
||||||
|
|
||||||
page += 1
|
|
||||||
|
|
||||||
// 服务端无更多页:终止
|
|
||||||
if (incoming.length < PAGE_SIZE) {
|
|
||||||
serverHasMore = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已拿到可展示数据:先返回,让用户继续滚动触发下一次加载
|
|
||||||
if (filtered.length > 0) break
|
|
||||||
}
|
|
||||||
|
|
||||||
serverPageRef.current = page
|
|
||||||
setList(nextList)
|
|
||||||
setHasMore(serverHasMore)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('加载客户列表失败:', e)
|
|
||||||
setError('加载失败,请重试')
|
|
||||||
setHasMore(false)
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[applyFilters, list, loading, searchValue]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleRefresh = useCallback(async () => {
|
|
||||||
await reload(true)
|
|
||||||
}, [reload])
|
|
||||||
|
|
||||||
const loadMore = useCallback(async () => {
|
|
||||||
if (loading || !hasMore) return
|
|
||||||
await reload(false)
|
|
||||||
}, [hasMore, loading, reload])
|
|
||||||
|
|
||||||
useDidShow(() => {
|
|
||||||
reload(true).then()
|
|
||||||
// 预加载员工列表,保证“跟进人(realName)”可展示
|
|
||||||
ensureStaffLoaded().then()
|
|
||||||
})
|
|
||||||
|
|
||||||
const visibleIndustryOptions = useMemo(() => {
|
|
||||||
const uniq = new Set<string>()
|
|
||||||
for (const m of list) {
|
|
||||||
const ind = getCompanyIndustry(m)
|
|
||||||
if (ind) uniq.add(ind)
|
|
||||||
}
|
|
||||||
const arr = Array.from(uniq)
|
|
||||||
arr.sort()
|
|
||||||
return ['全部'].concat(arr)
|
|
||||||
}, [list])
|
|
||||||
|
|
||||||
const toggleSelectId = (companyId?: number, checked?: boolean) => {
|
|
||||||
if (!companyId) return
|
|
||||||
setSelectedIds(prev => {
|
|
||||||
if (checked) return prev.includes(companyId) ? prev : prev.concat(companyId)
|
|
||||||
return prev.filter(id => id !== companyId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyPhones = async () => {
|
|
||||||
const pool = selectMode && selectedIds.length ? list.filter(c => selectedIds.includes(Number(c.id))) : list
|
|
||||||
const phones = pool.flatMap(c => getCompanyPhones(c))
|
|
||||||
const unique = Array.from(new Set(phones)).filter(Boolean)
|
|
||||||
if (!unique.length) {
|
|
||||||
Taro.showToast({ title: '暂无可复制的电话', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await Taro.setClipboardData({ data: unique.join('\n') })
|
|
||||||
Taro.showToast({ title: `已复制${unique.length}个电话`, icon: 'success' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const callPhone = async (phone?: string) => {
|
|
||||||
const num = String(phone || '').trim()
|
|
||||||
if (!num) {
|
|
||||||
Taro.showToast({ title: '暂无电话', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await Taro.makePhoneCall({ phoneNumber: num })
|
|
||||||
} catch (e) {
|
|
||||||
console.error('拨号失败:', e)
|
|
||||||
Taro.showToast({ title: '拨号失败', icon: 'none' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const openAddCustomer = () => {
|
|
||||||
Taro.navigateTo({ url: '/credit/company/add' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const ensureStaffLoaded = async () => {
|
|
||||||
if (staffLoading) return
|
|
||||||
if (staffList.length) return
|
|
||||||
setStaffLoading(true)
|
|
||||||
try {
|
|
||||||
const res = await listUsers({ isAdmin: true } as any)
|
|
||||||
setStaffList((res || []) as User[])
|
|
||||||
} catch (e) {
|
|
||||||
console.error('加载员工列表失败:', e)
|
|
||||||
Taro.showToast({ title: '加载员工失败', icon: 'none' })
|
|
||||||
} finally {
|
|
||||||
setStaffLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const openAssign = async () => {
|
|
||||||
if (!canAssign) {
|
|
||||||
Taro.showToast({ title: '当前角色无分配权限', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!selectMode) {
|
|
||||||
setSelectMode(true)
|
|
||||||
setSelectedIds([])
|
|
||||||
Taro.showToast({ title: '请选择要分配的客户', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!selectedIds.length) {
|
|
||||||
Taro.showToast({ title: '请先勾选客户', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await ensureStaffLoaded()
|
|
||||||
setStaffPopupVisible(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitAssign = async () => {
|
|
||||||
if (!staffSelectedId) {
|
|
||||||
Taro.showToast({ title: '请选择分配对象', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!selectedIds.length) {
|
|
||||||
Taro.showToast({ title: '请先勾选客户', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setAssigning(true)
|
|
||||||
try {
|
|
||||||
for (const id of selectedIds) {
|
|
||||||
await updateCreditCompany({ id, userId: staffSelectedId } as any)
|
|
||||||
}
|
|
||||||
Taro.showToast({ title: '分配成功', icon: 'success' })
|
|
||||||
setStaffPopupVisible(false)
|
|
||||||
setSelectMode(false)
|
|
||||||
setSelectedIds([])
|
|
||||||
await reload(true)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('分配失败:', e)
|
|
||||||
Taro.showToast({ title: '分配失败,请重试', icon: 'none' })
|
|
||||||
} finally {
|
|
||||||
setAssigning(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="bg-gray-50 min-h-screen">
|
<View className="bg-gray-50 min-h-screen">
|
||||||
<ConfigProvider>
|
<ConfigProvider>
|
||||||
<View className="py-2">
|
<View className="py-2 px-3">
|
||||||
<SearchBar
|
<SearchBar placeholder="请输入经办人姓名" value={searchValue} onChange={setSearchValue} />
|
||||||
placeholder="公司名称 / 统一代码"
|
|
||||||
value={searchValue}
|
|
||||||
onChange={setSearchValue}
|
|
||||||
onSearch={() => reload(true)}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className="px-3 pb-2 flex items-center gap-2">
|
<View className="px-3">
|
||||||
<Button size="small" fill="outline" onClick={() => setCityVisible(true)}>
|
<View className="bg-yellow-50 border border-yellow-200 rounded-lg p-3 text-sm text-yellow-900">
|
||||||
{cityText === '全部' ? '地区' : cityText}
|
<View className="font-medium mb-1">功能说明</View>
|
||||||
</Button>
|
<View>可根据经办人姓名进行搜索;</View>
|
||||||
<Button size="small" fill="outline" onClick={() => setFollowVisible(true)}>
|
<View>搜索结果以列表形式展示:企业经办人、企业名称、跟进人;</View>
|
||||||
{followStatus === '全部' ? '跟进状态' : followStatus}
|
<View>点击任意一行可进入查看该订单的跟进详情内容;</View>
|
||||||
</Button>
|
<View>若某企业经办人是老板,则可通过此功能查看该老板名下关联的所有企业订单数量。</View>
|
||||||
<Button size="small" fill="outline" onClick={() => setIndustryVisible(true)}>
|
<View className="mt-2 text-xs text-yellow-700">zzl</View>
|
||||||
{industryText === '全部' ? '行业' : industryText}
|
</View>
|
||||||
</Button>
|
|
||||||
<View className="flex-1" />
|
|
||||||
<Button size="small" fill="outline" icon={<Copy />} onClick={copyPhones}>
|
|
||||||
复制电话
|
|
||||||
</Button>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{!!error && (
|
<View className="px-3 mt-3 flex items-center justify-between">
|
||||||
<View className="px-4 pb-2 text-sm text-red-500">
|
<Text className="text-sm text-gray-700">搜索结果</Text>
|
||||||
{error}
|
<Text className="text-sm text-gray-600">{totalText}</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
|
|
||||||
<PullToRefresh onRefresh={handleRefresh} headHeight={60}>
|
<View className="px-3 mt-2">
|
||||||
<View className="px-3" style={{ height: 'calc(100vh - 190px)', overflowY: 'auto' }} id="credit-company-scroll">
|
<View className="bg-white rounded-lg overflow-hidden border border-gray-100">
|
||||||
{list.length === 0 && !loading ? (
|
<View className="flex bg-gray-50 text-xs text-gray-600 px-3 py-2">
|
||||||
<View className="flex flex-col justify-center items-center" style={{ height: 'calc(100vh - 240px)' }}>
|
<View className="flex-1">企业经办人</View>
|
||||||
<Empty description="暂无客户数据" style={{ backgroundColor: 'transparent' }} />
|
<View className="flex-1">企业名称</View>
|
||||||
|
<View className="flex-1">跟进人</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{filteredList.length === 0 ? (
|
||||||
|
<View className="py-10">
|
||||||
|
<Empty description="暂无数据" />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<InfiniteLoading
|
filteredList.map(r => (
|
||||||
target="credit-company-scroll"
|
|
||||||
hasMore={hasMore}
|
|
||||||
onLoadMore={loadMore}
|
|
||||||
loadingText={
|
|
||||||
<View className="flex justify-center items-center py-4">
|
|
||||||
<Loading />
|
|
||||||
<View className="ml-2">加载中...</View>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
loadMoreText={
|
|
||||||
<View className="text-center py-4 text-gray-500">
|
|
||||||
{list.length === 0 ? '暂无数据' : '没有更多了'}
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{list.map((c, idx) => {
|
|
||||||
const id = Number(c.id)
|
|
||||||
const selected = !!id && selectedIds.includes(id)
|
|
||||||
const follow = getFollowStatus(c)
|
|
||||||
const ownerName =
|
|
||||||
// 兼容后端可能直接下发跟进人字段
|
|
||||||
(c as any)?.realName ||
|
|
||||||
(c as any)?.userRealName ||
|
|
||||||
(c as any)?.followRealName ||
|
|
||||||
(c.userId ? staffNameMap.get(Number(c.userId)) : undefined)
|
|
||||||
const name = c.matchName || c.name || `企业${c.id || ''}`
|
|
||||||
const industry = getCompanyIndustry(c)
|
|
||||||
const phones = getCompanyPhones(c)
|
|
||||||
const primaryPhone = phones[0]
|
|
||||||
return (
|
|
||||||
<CellGroup key={c.id || idx} className="mb-3">
|
|
||||||
<Cell
|
|
||||||
onClick={() => {
|
|
||||||
if (selectMode) return
|
|
||||||
if (!c?.id) return
|
|
||||||
Taro.navigateTo({ url: `/credit/company/detail?id=${c.id}` })
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View className="flex gap-3 items-start w-full">
|
|
||||||
{selectMode && (
|
|
||||||
<View
|
<View
|
||||||
className="pt-1"
|
key={r.id}
|
||||||
onClick={(e) => {
|
className="flex px-3 py-3 text-sm text-gray-900 border-t border-gray-100"
|
||||||
e.stopPropagation()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={selected}
|
|
||||||
onChange={(checked) => toggleSelectId(id, checked)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<View className="flex-1 min-w-0">
|
|
||||||
<View className="flex items-start justify-between gap-2">
|
|
||||||
<View className="min-w-0 flex-1">
|
|
||||||
<View className="text-base font-bold text-gray-900 truncate">
|
|
||||||
{name}
|
|
||||||
</View>
|
|
||||||
{!!industry && (
|
|
||||||
<View className="text-xs text-gray-500 truncate mt-1">
|
|
||||||
{industry}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<Tag
|
|
||||||
type={follow === '无意向' ? 'danger' : follow === '已成交' ? 'success' : 'primary'}
|
|
||||||
onClick={(e: any) => {
|
|
||||||
e?.stopPropagation?.()
|
|
||||||
setFollowStatusFor(c)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{follow}
|
|
||||||
</Tag>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="mt-2 flex items-center justify-between gap-2">
|
|
||||||
<View className="text-xs text-gray-600 truncate">
|
|
||||||
<Text className="mr-2">
|
|
||||||
{primaryPhone ? primaryPhone : '暂无电话'}
|
|
||||||
</Text>
|
|
||||||
<Text className="text-gray-500">
|
|
||||||
跟进人:{ownerName || '未分配'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="flex items-center gap-2">
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
fill="outline"
|
|
||||||
icon={<Phone />}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
callPhone(primaryPhone)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
拨打
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{!!(c.province || c.city || c.region) && (
|
|
||||||
<View className="mt-2 text-xs text-gray-500 truncate">
|
|
||||||
{[c.province, c.city, c.region].filter(Boolean).join(' ')}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</Cell>
|
|
||||||
</CellGroup>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</InfiniteLoading>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</PullToRefresh>
|
|
||||||
|
|
||||||
<View className="h-20 w-full" />
|
|
||||||
<View className="fixed z-50 bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-3 py-3 safe-area-bottom">
|
|
||||||
<View className="flex items-center gap-3">
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
style={{ background: '#ef4444' }}
|
|
||||||
className="flex-1"
|
|
||||||
onClick={openAddCustomer}
|
|
||||||
>
|
|
||||||
添加客户
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
style={{ background: canAssign ? '#3b82f6' : '#94a3b8' }}
|
|
||||||
disabled={!canAssign || assigning}
|
|
||||||
className="flex-1"
|
|
||||||
onClick={openAssign}
|
|
||||||
>
|
|
||||||
{selectMode ? '分配所选' : '分配客户'}
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
{selectMode && (
|
|
||||||
<View className="mt-2 flex items-center justify-between text-xs text-gray-500">
|
|
||||||
<Text>已选 {selectedIds.length} 个</Text>
|
|
||||||
<View className="flex items-center justify-end gap-3">
|
|
||||||
<Text
|
|
||||||
className="text-blue-600"
|
|
||||||
onClick={() => copyPhones()}
|
|
||||||
>
|
|
||||||
复制所选电话
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
className="text-gray-500"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectMode(false)
|
setActiveRow(r)
|
||||||
setSelectedIds([])
|
setDetailVisible(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
取消
|
<View className="flex-1 truncate">{r.agentName}</View>
|
||||||
</Text>
|
<View className="flex-1 truncate">{r.companyName}</View>
|
||||||
</View>
|
<View className="flex-1 truncate">{r.follower}</View>
|
||||||
</View>
|
</View>
|
||||||
|
))
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
<Address
|
|
||||||
visible={cityVisible}
|
|
||||||
options={cityOptions as any}
|
|
||||||
title="选择地区(到城市)"
|
|
||||||
onChange={(value: any[]) => {
|
|
||||||
const txt = value.filter(Boolean).slice(0, 2).join(' ')
|
|
||||||
setCityText(txt || '全部')
|
|
||||||
setCityVisible(false)
|
|
||||||
reload(true).then()
|
|
||||||
}}
|
|
||||||
onClose={() => setCityVisible(false)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Popup
|
<Popup
|
||||||
visible={followVisible}
|
visible={detailVisible}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
style={{ height: '45vh' }}
|
style={{ height: '45vh' }}
|
||||||
onClose={() => setFollowVisible(false)}
|
onClose={() => setDetailVisible(false)}
|
||||||
>
|
>
|
||||||
<View className="p-4">
|
<View className="p-4">
|
||||||
<View className="flex items-center justify-between mb-3">
|
<View className="flex items-center justify-between mb-3">
|
||||||
<Text className="text-base font-medium">跟进状态</Text>
|
<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={() => setDetailVisible(false)}>
|
||||||
关闭
|
关闭
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<CellGroup>
|
{activeRow ? (
|
||||||
{FOLLOW_STATUS_OPTIONS.map(s => (
|
<View className="text-sm text-gray-700 leading-6">
|
||||||
<Cell
|
<View>企业经办人:{activeRow.agentName}</View>
|
||||||
key={s}
|
<View>企业名称:{activeRow.companyName}</View>
|
||||||
title={<Text className={s === followStatus ? 'text-blue-600' : ''}>{s}</Text>}
|
<View>跟进人:{activeRow.follower}</View>
|
||||||
onClick={() => {
|
<View className="mt-2 text-xs text-gray-500">此处可接入真实“订单跟进详情”页面或接口返回内容。</View>
|
||||||
setFollowStatus(s)
|
</View>
|
||||||
setFollowVisible(false)
|
) : (
|
||||||
reload(true).then()
|
<View className="text-sm text-gray-500">暂无详情</View>
|
||||||
}}
|
)}
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</CellGroup>
|
|
||||||
</View>
|
</View>
|
||||||
</Popup>
|
</Popup>
|
||||||
|
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user