feat(user): 添加地址编辑时的地区锁定功能

- 新增 regionLocked 状态管理地区锁定状态
- 编辑模式下有经纬度时自动锁定地区,防止被识别覆盖
- 地图选点后锁定地区并验证省市区完整性
- 锁定状态下点击地区选择器显示提示信息
- 表单提交前验证必填的省市区字段
- 使用 View 组件替换 div 优化 Taro 兼容性
- 识别成功时根据锁定状态显示不同提示文案
This commit is contained in:
2026-03-06 11:39:47 +08:00
parent 23af704c68
commit b929b8d35e

View File

@@ -47,6 +47,7 @@ const AddUserAddress = () => {
const [FormData, setFormData] = useState<ShopUserAddress>({})
const [inputText, setInputText] = useState<string>('')
const [selectedLocation, setSelectedLocation] = useState<SelectedLocation | null>(null)
const [regionLocked, setRegionLocked] = useState(false)
const formRef = useRef<any>(null)
const wxDraftRef = useRef<Partial<ShopUserAddress> | null>(null)
const wxDraftPatchedRef = useRef(false)
@@ -120,7 +121,12 @@ const AddUserAddress = () => {
// 设置所在地区
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) {
console.error('加载地址失败:', error)
Taro.showToast({
@@ -172,30 +178,39 @@ const AddUserAddress = () => {
const result = parseAddressText(inputText);
// 更新表单数据
const newFormData = {
const newFormData: any = {
...FormData,
name: result.name || FormData.name,
phone: result.phone || FormData.phone,
address: result.address || FormData.address,
province: result.province || FormData.province,
city: result.city || FormData.city,
region: result.region || FormData.region
address: result.address || FormData.address
};
if (!regionLocked) {
newFormData.province = result.province || FormData.province
newFormData.city = result.city || FormData.city
newFormData.region = result.region || FormData.region
}
setFormData(newFormData);
// 更新地区显示文本
if (result.province && result.city && result.region) {
if (!regionLocked && result.province && result.city && result.region) {
setText(`${result.province} ${result.city} ${result.region}`);
}
// 更新表单字段值
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({
title: '识别成功',
title: regionLocked ? '识别成功(所在地区以定位为准)' : '识别成功',
icon: 'success'
});
@@ -311,7 +326,6 @@ const AddUserAddress = () => {
name: res.name,
address: res.address
}
setSelectedLocation(next)
// 尝试从地图返回的 address 文本解析省市区best-effort
const regionResult = res?.provinceName || res?.cityName || res?.adName
@@ -322,15 +336,22 @@ const AddUserAddress = () => {
}
: 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 rawAddr = String(res.address || '').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 往往包含省市区,这里尽量剥离掉,避免和表单的省市区字段重复
let detail = rawAddr
for (const part of [province, city, region]) {
@@ -350,20 +371,18 @@ const AddUserAddress = () => {
lng: next.lng,
lat: next.lat,
address: nextDetailAddress || prev.address,
province: regionResult?.province || prev.province,
city: regionResult?.city || prev.city,
region: regionResult?.region || prev.region
province,
city,
region
}))
if (regionResult?.province && regionResult?.city && regionResult?.region) {
setText(`${regionResult.province} ${regionResult.city} ${regionResult.region}`)
}
setText(`${province} ${city} ${region}`)
// 更新表单展示值Form initialValues 不会跟随 FormData 变化)
if (formRef.current) {
const patch: any = {}
if (nextDetailAddress) patch.address = nextDetailAddress
if (regionResult?.region) patch.region = regionResult.region
patch.region = region
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 loc =
@@ -416,6 +443,10 @@ const AddUserAddress = () => {
Taro.showToast({ title: '请选择定位', icon: 'none' })
return
}
if (!FormData.province || !FormData.city || !FormData.region) {
Taro.showToast({ title: '请先选择定位以自动填写所在地区', icon: 'none' })
return
}
try {
// 准备提交的数据
@@ -487,6 +518,12 @@ const AddUserAddress = () => {
})
}, [fromWx, isEditMode]);
useEffect(() => {
if (!regionLocked) return
if (!visible) return
setVisible(false)
}, [regionLocked, visible])
// NutUI Form 的 initialValues 在首次渲染后不再响应更新;微信导入时做一次 setFieldsValue 兜底回填。
useEffect(() => {
if (loading) return
@@ -523,7 +560,7 @@ const AddUserAddress = () => {
onFinishFailed={(errors) => submitFailed(errors)}
>
<CellGroup className={'px-3'}>
<div
<View
style={{
border: '1px dashed #22c55e',
display: 'flex',
@@ -549,7 +586,7 @@ const AddUserAddress = () => {
>
</Button>
</div>
</View>
</CellGroup>
<View className={'bg-gray-100 h-3'}></View>
<CellGroup style={{padding: '4px 0'}}>
@@ -581,10 +618,10 @@ const AddUserAddress = () => {
rules={[{message: '请输入您的所在地区'}]}
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/>
<ArrowRight className={'text-gray-400'}/>
</div>
</View>
</Form.Item>
<Form.Item name="address" label="收货地址" initialValue={FormData.address} required>
<TextArea maxLength={50} placeholder="请输入详细收货地址"/>
@@ -598,15 +635,15 @@ const AddUserAddress = () => {
(selectedLocation ? `经纬度:${selectedLocation.lng}, ${selectedLocation.lat}` : '用于计算是否超出配送范围')
}
extra={(
<div className={'flex items-center gap-2'}>
<div
<View className={'flex items-center gap-2'}>
<View
className={'text-gray-900 text-sm'}
style={{maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}
>
{selectedLocation?.name || (selectedLocation ? '已选择' : '请选择')}
</div>
</View>
<ArrowRight className={'text-gray-400'}/>
</div>
</View>
)}
onClick={chooseGeoLocation}
/>
@@ -618,6 +655,10 @@ const AddUserAddress = () => {
options={optionsDemo1}
title="选择地址"
onChange={(value, _) => {
if (regionLocked) {
Taro.showToast({ title: '所在地区已由定位确定,修改请重新选择定位', icon: 'none' })
return
}
setFormData({
...FormData,
province: `${value[0]}`,