diff --git a/src/dealer/customer/add.tsx b/src/dealer/customer/add.tsx index 7a1c9ab..636e891 100644 --- a/src/dealer/customer/add.tsx +++ b/src/dealer/customer/add.tsx @@ -26,6 +26,8 @@ const AddShopDealerApply = () => { const [existingApply, setExistingApply] = useState(null) const [referee, setReferee] = useState() const PROTECTION_DAYS = 15; + const DUP_CHECK_LIMIT = 200; + const DUP_CHECK_MAX_PAGES = 50; // 房号信息:用 dealerCode 存储唯一键,dealerName 存储展示文案 const buildHouseKey = (community: string, buildingNo: string, unitNo: string | undefined, roomNo: string) => { @@ -159,6 +161,107 @@ const AddShopDealerApply = () => { return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; }; + type DupKey = 'address' | 'buildingNo' | 'unitNo' | 'roomNo' | 'realName' | 'mobile'; + const DUP_LABELS: Record = { + address: '小区名称', + buildingNo: '楼栋号', + unitNo: '单元号', + roomNo: '房号', + realName: '姓名', + mobile: '电话', + }; + + const normalizeText = (v: any) => (v ?? '').toString().trim(); + + const getApplyDupFields = (apply: ShopDealerApply): Record => { + const parsed = parseHouseKey(apply.dealerCode); + return { + address: normalizeText(parsed.community || apply.address), + buildingNo: normalizeText(parsed.buildingNo), + unitNo: normalizeText(parsed.unitNo), + roomNo: normalizeText(parsed.roomNo), + realName: normalizeText(apply.realName), + mobile: normalizeText(apply.mobile), + }; + }; + + const getNewDupFields = (values: any): Record => ({ + address: normalizeText(values.address), + buildingNo: normalizeText(values.buildingNo), + unitNo: normalizeText(values.unitNo), + roomNo: normalizeText(values.roomNo), + realName: normalizeText(values.realName), + mobile: normalizeText(values.mobile), + }); + + const combinationsOf3 = (arr: T[]): [T, T, T][] => { + const res: [T, T, T][] = []; + for (let i = 0; i < arr.length; i++) { + for (let j = i + 1; j < arr.length; j++) { + for (let k = j + 1; k < arr.length; k++) { + res.push([arr[i], arr[j], arr[k]]); + } + } + } + return res; + }; + + const findMatchedTriad = (a: Record, b: Record) => { + const availableKeys = (Object.keys(a) as DupKey[]).filter((k) => a[k] !== ''); + if (availableKeys.length < 3) return null; + const triads = combinationsOf3(availableKeys); + for (const triad of triads) { + if (triad.every((k) => a[k] === b[k] && b[k] !== '')) return triad; + } + return null; + }; + + const checkDuplicateBeforeSubmit = async (values: any, opts?: { skipDealerCode?: string }) => { + const inputFields = getNewDupFields(values); + const nonEmptyCount = Object.values(inputFields).filter((v) => v !== '').length; + if (nonEmptyCount < 3) return null; + + const seen = new Set(); + const scanPages = async (params: any) => { + for (let page = 1; page <= DUP_CHECK_MAX_PAGES; page++) { + const res = await pageShopDealerApply({...params, page, limit: DUP_CHECK_LIMIT}); + const list = res?.list || []; + + for (const item of list) { + if (opts?.skipDealerCode && item.dealerCode === opts.skipDealerCode) continue; + if (item.applyId && seen.has(item.applyId)) continue; + if (item.applyId) seen.add(item.applyId); + + const triad = findMatchedTriad(inputFields, getApplyDupFields(item)); + if (triad) return {item, triad}; + } + + if (list.length < DUP_CHECK_LIMIT) break; + } + return null; + }; + + // 优先按手机号(精确)查询,数据量更小 + if (inputFields.mobile) { + const hit = await scanPages({type: 4, mobile: inputFields.mobile}); + if (hit) return hit; + } + + // 再按小区关键字查询,覆盖房号相关组合 + if (inputFields.address) { + const hit = await scanPages({type: 4, keywords: inputFields.address}); + if (hit) return hit; + } + + // 最后按姓名关键字兜底(用于覆盖不包含“小区/电话”的组合) + if (inputFields.realName) { + const hit = await scanPages({type: 4, keywords: inputFields.realName}); + if (hit) return hit; + } + + return null; + }; + const submitSucceed = async (values: any) => { try { @@ -272,6 +375,20 @@ const AddShopDealerApply = () => { } } + // 新增报备:提交前做“三要素”重复校验(小区/楼栋/单元/房号/姓名/电话 任一三要素重复提示已报备) + if (!isEditMode) { + const dup = await checkDuplicateBeforeSubmit(values, {skipDealerCode: houseKey}); + if (dup) { + const triadLabels = dup.triad.map((k: DupKey) => DUP_LABELS[k]).join('、'); + const existingDisplay = dup.item.dealerName || dup.item.address || '地址未知'; + Taro.showToast({ + title: `疑似重复报备:${triadLabels}一致(${existingDisplay}),已报备`, + icon: 'none', + duration: 3000 + }); + return false; + } + } // 计算过期时间 const expirationTime = isEditMode ? existingApply?.expirationTime : calculateExpirationTime(); diff --git a/src/pages/user/components/UserCell.tsx b/src/pages/user/components/UserCell.tsx index 3ca229c..ee1e473 100644 --- a/src/pages/user/components/UserCell.tsx +++ b/src/pages/user/components/UserCell.tsx @@ -2,11 +2,11 @@ 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, ShieldCheck, LogisticsError, Location, Tips, Ask} from '@nutui/icons-react-taro' +import {ArrowRight, LogisticsError, Tips, Ask} from '@nutui/icons-react-taro' import {useUser} from '@/hooks/useUser' const UserCell = () => { - const {logoutUser, isCertified} = useUser(); + const {logoutUser} = useUser(); const onLogout = () => { Taro.showModal({ @@ -50,37 +50,37 @@ const UserCell = () => { navTo('/user/wallet/index', true) }} /> - - - 收货地址 - - } - align="center" - extra={} - onClick={() => { - navTo('/user/address/index', true) - }} - /> - - - 实名认证 - {isCertified() && ( - 已认证 - )} - - } - align="center" - extra={} - onClick={() => { - navTo('/user/userVerify/index', true) - }} - /> + {/**/} + {/* */} + {/* 收货地址*/} + {/* */} + {/* }*/} + {/* align="center"*/} + {/* extra={}*/} + {/* onClick={() => {*/} + {/* navTo('/user/address/index', true)*/} + {/* }}*/} + {/*/>*/} + {/**/} + {/* */} + {/* 实名认证*/} + {/* {isCertified() && (*/} + {/* 已认证*/} + {/* )}*/} + {/* */} + {/* }*/} + {/* align="center"*/} + {/* extra={}*/} + {/* onClick={() => {*/} + {/* navTo('/user/userVerify/index', true)*/} + {/* }}*/} + {/*/>*/} () @@ -34,7 +33,6 @@ function User() { }}> - @@ -70,7 +68,6 @@ function User() { }}> {/**/} -