最近有一个需求,是要在前端做一个地址识别功能。。。确认不交给后台调接口去用ai识别吗?
看需求图:(识别出来的地址要给勾选上,这是个四级联动)

说一下解决方案:先将输入的地址格式化,去掉空格,统一把中文标点转换成英文,去掉换行符等。。。然后对字符串进行切割,进行强匹配,输出匹配上的数组。
地址识别组件:
import React, { FC, useState } from "react";
import { FormShape, createForm } from "@components/Form";
import { Textarea } from "@components/Input";
import Tooltip from "@components/Tooltip";
import { errorTip, successTip } from "@components/Tips";
type DistinguishProps = {
form: FormShape;
addressCheck?: (...args) => void;
addressData: any;
};
const Distinguish: FC<DistinguishProps> = ({ form, addressCheck, addressData }) => {
const [successAdd, setSuccessAdd] = useState([]);
const [isSuccess, setIsSuccess] = useState("");
const re1 = /[;]/;
const re2 = /[:]/;
// 处理识别数据
const dealText = value => {
if (!value) return;
// 格式化,转换成英文字符,方便切割
let str = value
.replace(/:/g, ":")
.replace(/;/g, ";")
.replace(/[\r\n]/g, "")
.replace(/\t/g, " ");
let newData1 = [];
let newData2 = [];
let newData3 = [];
try {
if (re1.test(str)) {
newData1 = str.split(";");
newData1.forEach(item1 => {
item1 ? newData2.push(item1) : " ";
});
newData2.forEach(item2 => {
if (re2.test(item2)) {
newData3.push({
[item2.split(":")[0]]: item2.split(":")[1].split(" "),
});
} else {
newData3.push({ [item2]: [] });
}
});
} else {
if (re2.test(str)) {
newData1 = str.split(":");
newData1.forEach(item1 => {
item1 ? newData2.push(item1) : " ";
});
newData3.push({
[newData2[0]]: newData2[1].split(" "),
});
} else {
newData3.push({ [str]: [] });
}
}
return newData3;
} catch (e) {
errorTip("地址识别失败!请按照提示的格式输入地址信息。");
setSuccessAdd([]);
}
};
// 数组对象去重,合并
const mergeData = value => {
let initArr = [];
let initData = {};
let data = xt.cloneDeep(value); //深拷贝
if (data.length > 0) {
data.forEach(item => {
if (typeof item == "string") {
initArr.push(item);
} else {
let map = item;
for (let k in map) {
if (k != null && k != "null") {
if (initData.hasOwnProperty(k)) {
initData[k] += "," + map[k];
} else {
initData[k] = map[k];
}
}
}
}
});
}
Object.keys(initData).forEach(key => {
if (initData[key]) {
let a = Array.from(new Set(initData[key].split(","))).join(",");
initArr.push({ [key]: a });
} else {
initArr.push(key);
}
});
return initArr;
};
// 地址匹配
const addressPick = (val: any) => {
let errAddress = []; // 识别失败地址
let sucessAddress = []; // 识别成功地址
let inputAddress = val; // 输入的地址
let propsData = xt.cloneDeep(addressData); // 识别选中的地址
try {
inputAddress.forEach(item => {
// @ts-ignore
if (Object.values(item)?.flat().length) {
// 选中区下面的街道
Object.values(item)
// @ts-ignore
.flat()
.forEach(address => {
propsData.forEach(data => {
// @ts-ignore
if (data.nameCN == xt.nesGame(Object.keys(item)?.flat()[0])) {
data?.childrenStreet.forEach(a => {
if (a.nameCN == xt.nesGame(address)) { // xt.nesGame去掉空格
sucessAddress.push({ [data.nameCN]: address });
a.choose = true;
}
});
}
});
});
} else {
// 直接选中区以及下面所有的街道
propsData.forEach(data => {
// @ts-ignore
if (data.nameCN == xt.nesGame(Object.keys(item)?.flat()[0])) {
data.choose = true;
data?.childrenStreet.forEach(a => {
a.choose = true;
});
// @ts-ignore
sucessAddress.push(xt.nesGame(Object.keys(item)?.flat()[0]));
}
});
}
});
if (sucessAddress.length > 0) {
setIsSuccess("识别成功地址");
successTip("识别成功");
addressCheck(Array.from(new Set(sucessAddress)));
setSuccessAdd(mergeData(sucessAddress)); // 识别成功地址
} else {
setIsSuccess("地址识别失败!请检查后再试");
setSuccessAdd([]);
}
} catch (e) {
errorTip("地址匹配失败!请检查后再试");
setSuccessAdd([]);
}
};
// 提交给父组件
// 宝安区:福永街道 石岩街道 航城街道 福海街道 西乡街道 西乡街道;罗湖区
const submit = () => {
setIsSuccess("");
form.validateFields((error, value: any) => {
if (error) return;
addressPick(dealText(value.note));
});
};
const cancel = () => {
form.resetFields();
setSuccessAdd([]);
setIsSuccess("");
};
return (
<div
className="pb-20"
style={{ border: "1px solid #eaedf3", borderRadius: "4px", backgroundColor: "#fff" }}
>
<Textarea
form={form}
fieldName="note"
textareaStyle={{ border: "none" }}
placeholder={
"请粘贴文本至此处,自动识别区、县、街道名称并选中,若有多个街道请以区名或县名加冒号开头,多个街道以空格隔开,以分号结束,若需选中整个区或县所有街道则直接输入区名或县名以分号结束。\n例:坪山区:坪山街道 坑梓街道;南山区;"
}
style={{ width: "100%", border: "none" }}
maxLength={300}
showError="down"
rules={false}
normalize={(val, prev) => {
if (val.length > 300) return prev.trim();
if (!val.trim().length || val.length === 300) return val.trim();
return val;
}}
/>
<div className="mt-20 pr-20 flex-align-center" style={{ paddingLeft: "15px" }}>
<button className="button-primary mr-20" onClick={submit}>
识别文本
</button>
<button className="button-default" onClick={cancel}>
清空
</button>
<div className="ml-30">
<span
className="mr-5 font12 c-606266"
style={{ color: isSuccess == "识别成功地址" ? "" : "#FF6666" }}
>
{isSuccess}
</span>
{!!successAdd.length && (
<Tooltip
overlay={
<>
{successAdd.map((val, index) => {
if (typeof val === "string") {
return <p>{val};</p>;
} else {
return (
<p>
{Object.keys(val)[0]}:
{Array.from(new Set(Object.values(val))).join(
","
)}
</p>
);
}
})}
</>
}
>
<i className={"wsf-icon-question"} />
</Tooltip>
)}
</div>
</div>
</div>
);
};
export default createForm()(Distinguish);

浙公网安备 33010602011771号