feat(user): 添加地址编辑时的地区锁定功能
- 新增 regionLocked 状态管理地区锁定状态 - 编辑模式下有经纬度时自动锁定地区,防止被识别覆盖 - 地图选点后锁定地区并验证省市区完整性 - 锁定状态下点击地区选择器显示提示信息 - 表单提交前验证必填的省市区字段 - 使用 View 组件替换 div 优化 Taro 兼容性 - 识别成功时根据锁定状态显示不同提示文案
This commit is contained in:
@@ -47,6 +47,7 @@ const AddUserAddress = () => {
|
|||||||
const [FormData, setFormData] = useState<ShopUserAddress>({})
|
const [FormData, setFormData] = useState<ShopUserAddress>({})
|
||||||
const [inputText, setInputText] = useState<string>('')
|
const [inputText, setInputText] = useState<string>('')
|
||||||
const [selectedLocation, setSelectedLocation] = useState<SelectedLocation | null>(null)
|
const [selectedLocation, setSelectedLocation] = useState<SelectedLocation | null>(null)
|
||||||
|
const [regionLocked, setRegionLocked] = useState(false)
|
||||||
const formRef = useRef<any>(null)
|
const formRef = useRef<any>(null)
|
||||||
const wxDraftRef = useRef<Partial<ShopUserAddress> | null>(null)
|
const wxDraftRef = useRef<Partial<ShopUserAddress> | null>(null)
|
||||||
const wxDraftPatchedRef = useRef(false)
|
const wxDraftPatchedRef = useRef(false)
|
||||||
@@ -120,7 +121,12 @@ const AddUserAddress = () => {
|
|||||||
// 设置所在地区
|
// 设置所在地区
|
||||||
setText(`${address.province} ${address.city} ${address.region}`)
|
setText(`${address.province} ${address.city} ${address.region}`)
|
||||||
// 回显已保存的经纬度(编辑模式)
|
// 回显已保存的经纬度(编辑模式)
|
||||||
if (hasValidLngLat(address)) setSelectedLocation({ lng: String(address.lng), lat: String(address.lat) })
|
if (hasValidLngLat(address)) {
|
||||||
|
setSelectedLocation({ lng: String(address.lng), lat: String(address.lat) })
|
||||||
|
setRegionLocked(true)
|
||||||
|
} else {
|
||||||
|
setRegionLocked(false)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载地址失败:', error)
|
console.error('加载地址失败:', error)
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -172,30 +178,39 @@ const AddUserAddress = () => {
|
|||||||
const result = parseAddressText(inputText);
|
const result = parseAddressText(inputText);
|
||||||
|
|
||||||
// 更新表单数据
|
// 更新表单数据
|
||||||
const newFormData = {
|
const newFormData: any = {
|
||||||
...FormData,
|
...FormData,
|
||||||
name: result.name || FormData.name,
|
name: result.name || FormData.name,
|
||||||
phone: result.phone || FormData.phone,
|
phone: result.phone || FormData.phone,
|
||||||
address: result.address || FormData.address,
|
address: result.address || FormData.address
|
||||||
province: result.province || FormData.province,
|
|
||||||
city: result.city || FormData.city,
|
|
||||||
region: result.region || FormData.region
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!regionLocked) {
|
||||||
|
newFormData.province = result.province || FormData.province
|
||||||
|
newFormData.city = result.city || FormData.city
|
||||||
|
newFormData.region = result.region || FormData.region
|
||||||
|
}
|
||||||
|
|
||||||
setFormData(newFormData);
|
setFormData(newFormData);
|
||||||
|
|
||||||
// 更新地区显示文本
|
// 更新地区显示文本
|
||||||
if (result.province && result.city && result.region) {
|
if (!regionLocked && result.province && result.city && result.region) {
|
||||||
setText(`${result.province} ${result.city} ${result.region}`);
|
setText(`${result.province} ${result.city} ${result.region}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新表单字段值
|
// 更新表单字段值
|
||||||
if (formRef.current) {
|
if (formRef.current) {
|
||||||
formRef.current.setFieldsValue(newFormData);
|
const patch: any = {
|
||||||
|
name: newFormData.name,
|
||||||
|
phone: newFormData.phone,
|
||||||
|
address: newFormData.address
|
||||||
|
}
|
||||||
|
if (!regionLocked && newFormData.region) patch.region = newFormData.region
|
||||||
|
formRef.current.setFieldsValue(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '识别成功',
|
title: regionLocked ? '识别成功(所在地区以定位为准)' : '识别成功',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -311,7 +326,6 @@ const AddUserAddress = () => {
|
|||||||
name: res.name,
|
name: res.name,
|
||||||
address: res.address
|
address: res.address
|
||||||
}
|
}
|
||||||
setSelectedLocation(next)
|
|
||||||
|
|
||||||
// 尝试从地图返回的 address 文本解析省市区(best-effort)
|
// 尝试从地图返回的 address 文本解析省市区(best-effort)
|
||||||
const regionResult = res?.provinceName || res?.cityName || res?.adName
|
const regionResult = res?.provinceName || res?.cityName || res?.adName
|
||||||
@@ -322,15 +336,22 @@ const AddUserAddress = () => {
|
|||||||
}
|
}
|
||||||
: parseRegion(String(res.address || ''))
|
: parseRegion(String(res.address || ''))
|
||||||
|
|
||||||
|
const province = String(regionResult?.province || '').trim()
|
||||||
|
const city = String(regionResult?.city || '').trim()
|
||||||
|
const region = String(regionResult?.region || '').trim()
|
||||||
|
if (!province || !city || !region) {
|
||||||
|
Taro.showToast({ title: '定位未识别到所在地区,请重新选择定位', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedLocation(next)
|
||||||
|
setRegionLocked(true)
|
||||||
|
|
||||||
// 将地图选点的地址同步到“收货地址”(不额外拼接省市区字段,省市区由独立字段保存)
|
// 将地图选点的地址同步到“收货地址”(不额外拼接省市区字段,省市区由独立字段保存)
|
||||||
const nextDetailAddress = (() => {
|
const nextDetailAddress = (() => {
|
||||||
const rawAddr = String(res.address || '').trim()
|
const rawAddr = String(res.address || '').trim()
|
||||||
const name = String(res.name || '').trim()
|
const name = String(res.name || '').trim()
|
||||||
|
|
||||||
const province = String(regionResult?.province || '').trim()
|
|
||||||
const city = String(regionResult?.city || '').trim()
|
|
||||||
const region = String(regionResult?.region || '').trim()
|
|
||||||
|
|
||||||
// 选择定位返回的 address 往往包含省市区,这里尽量剥离掉,避免和表单的省市区字段重复
|
// 选择定位返回的 address 往往包含省市区,这里尽量剥离掉,避免和表单的省市区字段重复
|
||||||
let detail = rawAddr
|
let detail = rawAddr
|
||||||
for (const part of [province, city, region]) {
|
for (const part of [province, city, region]) {
|
||||||
@@ -350,20 +371,18 @@ const AddUserAddress = () => {
|
|||||||
lng: next.lng,
|
lng: next.lng,
|
||||||
lat: next.lat,
|
lat: next.lat,
|
||||||
address: nextDetailAddress || prev.address,
|
address: nextDetailAddress || prev.address,
|
||||||
province: regionResult?.province || prev.province,
|
province,
|
||||||
city: regionResult?.city || prev.city,
|
city,
|
||||||
region: regionResult?.region || prev.region
|
region
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if (regionResult?.province && regionResult?.city && regionResult?.region) {
|
setText(`${province} ${city} ${region}`)
|
||||||
setText(`${regionResult.province} ${regionResult.city} ${regionResult.region}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新表单展示值(Form initialValues 不会跟随 FormData 变化)
|
// 更新表单展示值(Form initialValues 不会跟随 FormData 变化)
|
||||||
if (formRef.current) {
|
if (formRef.current) {
|
||||||
const patch: any = {}
|
const patch: any = {}
|
||||||
if (nextDetailAddress) patch.address = nextDetailAddress
|
if (nextDetailAddress) patch.address = nextDetailAddress
|
||||||
if (regionResult?.region) patch.region = regionResult.region
|
patch.region = region
|
||||||
formRef.current.setFieldsValue(patch)
|
formRef.current.setFieldsValue(patch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -407,6 +426,14 @@ const AddUserAddress = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openRegionPicker = () => {
|
||||||
|
if (regionLocked) {
|
||||||
|
Taro.showToast({ title: '所在地区已由定位确定,修改请重新选择定位', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setVisible(true)
|
||||||
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const submitSucceed = async (values: any) => {
|
const submitSucceed = async (values: any) => {
|
||||||
const loc =
|
const loc =
|
||||||
@@ -416,6 +443,10 @@ const AddUserAddress = () => {
|
|||||||
Taro.showToast({ title: '请选择定位', icon: 'none' })
|
Taro.showToast({ title: '请选择定位', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!FormData.province || !FormData.city || !FormData.region) {
|
||||||
|
Taro.showToast({ title: '请先选择定位以自动填写所在地区', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 准备提交的数据
|
// 准备提交的数据
|
||||||
@@ -487,6 +518,12 @@ const AddUserAddress = () => {
|
|||||||
})
|
})
|
||||||
}, [fromWx, isEditMode]);
|
}, [fromWx, isEditMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!regionLocked) return
|
||||||
|
if (!visible) return
|
||||||
|
setVisible(false)
|
||||||
|
}, [regionLocked, visible])
|
||||||
|
|
||||||
// NutUI Form 的 initialValues 在首次渲染后不再响应更新;微信导入时做一次 setFieldsValue 兜底回填。
|
// NutUI Form 的 initialValues 在首次渲染后不再响应更新;微信导入时做一次 setFieldsValue 兜底回填。
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) return
|
if (loading) return
|
||||||
@@ -523,7 +560,7 @@ const AddUserAddress = () => {
|
|||||||
onFinishFailed={(errors) => submitFailed(errors)}
|
onFinishFailed={(errors) => submitFailed(errors)}
|
||||||
>
|
>
|
||||||
<CellGroup className={'px-3'}>
|
<CellGroup className={'px-3'}>
|
||||||
<div
|
<View
|
||||||
style={{
|
style={{
|
||||||
border: '1px dashed #22c55e',
|
border: '1px dashed #22c55e',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -549,7 +586,7 @@ const AddUserAddress = () => {
|
|||||||
>
|
>
|
||||||
识别
|
识别
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</View>
|
||||||
</CellGroup>
|
</CellGroup>
|
||||||
<View className={'bg-gray-100 h-3'}></View>
|
<View className={'bg-gray-100 h-3'}></View>
|
||||||
<CellGroup style={{padding: '4px 0'}}>
|
<CellGroup style={{padding: '4px 0'}}>
|
||||||
@@ -581,10 +618,10 @@ const AddUserAddress = () => {
|
|||||||
rules={[{message: '请输入您的所在地区'}]}
|
rules={[{message: '请输入您的所在地区'}]}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<div className={'flex justify-between items-center'} onClick={() => setVisible(true)}>
|
<View className={'flex justify-between items-center'} onClick={openRegionPicker}>
|
||||||
<Input placeholder="选择所在地区" value={text} disabled/>
|
<Input placeholder="选择所在地区" value={text} disabled/>
|
||||||
<ArrowRight className={'text-gray-400'}/>
|
<ArrowRight className={'text-gray-400'}/>
|
||||||
</div>
|
</View>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="address" label="收货地址" initialValue={FormData.address} required>
|
<Form.Item name="address" label="收货地址" initialValue={FormData.address} required>
|
||||||
<TextArea maxLength={50} placeholder="请输入详细收货地址"/>
|
<TextArea maxLength={50} placeholder="请输入详细收货地址"/>
|
||||||
@@ -598,15 +635,15 @@ const AddUserAddress = () => {
|
|||||||
(selectedLocation ? `经纬度:${selectedLocation.lng}, ${selectedLocation.lat}` : '用于计算是否超出配送范围')
|
(selectedLocation ? `经纬度:${selectedLocation.lng}, ${selectedLocation.lat}` : '用于计算是否超出配送范围')
|
||||||
}
|
}
|
||||||
extra={(
|
extra={(
|
||||||
<div className={'flex items-center gap-2'}>
|
<View className={'flex items-center gap-2'}>
|
||||||
<div
|
<View
|
||||||
className={'text-gray-900 text-sm'}
|
className={'text-gray-900 text-sm'}
|
||||||
style={{maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}
|
style={{maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}
|
||||||
>
|
>
|
||||||
{selectedLocation?.name || (selectedLocation ? '已选择' : '请选择')}
|
{selectedLocation?.name || (selectedLocation ? '已选择' : '请选择')}
|
||||||
</div>
|
</View>
|
||||||
<ArrowRight className={'text-gray-400'}/>
|
<ArrowRight className={'text-gray-400'}/>
|
||||||
</div>
|
</View>
|
||||||
)}
|
)}
|
||||||
onClick={chooseGeoLocation}
|
onClick={chooseGeoLocation}
|
||||||
/>
|
/>
|
||||||
@@ -618,6 +655,10 @@ const AddUserAddress = () => {
|
|||||||
options={optionsDemo1}
|
options={optionsDemo1}
|
||||||
title="选择地址"
|
title="选择地址"
|
||||||
onChange={(value, _) => {
|
onChange={(value, _) => {
|
||||||
|
if (regionLocked) {
|
||||||
|
Taro.showToast({ title: '所在地区已由定位确定,修改请重新选择定位', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
setFormData({
|
setFormData({
|
||||||
...FormData,
|
...FormData,
|
||||||
province: `${value[0]}`,
|
province: `${value[0]}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user