From c0b9b44ca22087a19f6cc434566e1bad6fa59eba Mon Sep 17 00:00:00 2001 From: laoyang Date: Wed, 4 Nov 2026 07:36:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E7=B2=98=E8=B4=B4):=20URL=20=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E6=8C=89=E6=8B=AC=E5=8F=B7=E9=85=8D=E5=AF=B9=E8=A3=81?= =?UTF-8?q?=E5=89=AA=EF=BC=8C=E4=BF=AE=E5=A4=8D=E9=AB=98=E5=BE=B7=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E8=A2=AB=E6=88=AA=E6=96=AD=E5=AF=BC=E8=87=B4=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 现象(用户实测) 粘贴高德分享链接后,识别出的地点"只有一半,只有一部分"。 ## 根因 高德分享链接里的 POI 名称含**未编码的半角括号**: ...%E9%9D%92%E5%B2%9B%E8%8F%9C(黄岛珠江路店)%2C武夷山路118号... ↑ 就是这个 ( 和 ) 而 URL 提取正则是 `/https?:\/\/[^\s"'<>()【】]+/i`(当时还排除了半角 `)`), 于是一到第一个 `)` 就停: 提取长度 221 / 原文 456 ← 只拿到不到一半 __p 切成 4 段(应为 5 段) 名称 = "船歌·鱼水饺青岛菜(黄岛珠江路店" ← 括号不闭合 地址 = 空 ← 第 5 段整个丢失 ## 为什么当初排除括号 本意是防止把 URL 后跟着的中文括注一起吞掉,如 `看这个 https://x.com/a(路线图在这里)`。 但规则太粗暴,误伤了 URL 内部的合法括号 —— RFC 3986 允许 `(` `)` 出现在 query。 ## 修法 1. 正则放宽:只排除空白 / 引号 / 尖括号 / **全角**括号(全角括号不会被编码进 URL, 出现在明文里必然是用户自己加的中文括注) 2. 新增 `trimUrlTail()`:从左扫描数括号,`depth < 0` 说明遇到多余的右括号, 那里才是真正的 URL 边界 3. 再裁掉末尾可能粘上的中文标点(。,、;!?) ## 验证(用用户提供的真实 URL 跑等价逻辑) 修复后: 提取长度 780 / 原文 780 ← 完整 __p 段数 5 [0] B02140WITR [1] 35.95086086125046 [2] 120.19152268767353 [3] 船歌·鱼水饺青岛菜(黄岛珠江路店) ← 括号完整 [4] 武夷山路118号中南·海湾新城(珠江路金石国际广场旁) ← 地址回来了 边界场景: "https://x?a=1(路线图)" → https://x?a=1 ✅ 中文括注被裁掉 "https://x?a=1) 后面有字" → https://x?a=1 ✅ 多余右括号被裁掉 同时确认另外两处 `split(',')` 都用了 `slice(2).join(',')` 拼回, 地址内部含逗号时不会被切错。 ## 编译 `BUILD SUCCESSFUL` ## 未验证 真机端到端(写剪贴板 → 点粘贴 → 看结果)需人工操作: hdc 没有可用的剪贴板写入工具(`paste` 是 Linux 文本合并命令,非剪贴板服务)。 解析逻辑本身已用真实 URL 在等价实现上验证通过。 --- entry/src/main/ets/common/PlaceParser.ets | 45 +++++++++++++++++++++- entry/src/main/ets/pages/EventEditPage.ets | 6 ++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/entry/src/main/ets/common/PlaceParser.ets b/entry/src/main/ets/common/PlaceParser.ets index b50855b..319fd3b 100644 --- a/entry/src/main/ets/common/PlaceParser.ets +++ b/entry/src/main/ets/common/PlaceParser.ets @@ -63,13 +63,54 @@ export class PlaceParser { // ==================== 链接解析 ==================== + /** + * 裁剪 URL 尾部:只裁"多出来"的右括号与中文标点,保留 URL 内部的合法括号。 + * + * 为什么需要:高德分享链接里的 POI 名称常含未编码的半角括号, + * 如 `...青岛菜(黄岛珠江路店)%2C武夷山路...` + * 若正则直接排除 ')',就会在第一个 ')' 处截断 → 后面的地址段全丢。 + * 实测该链接被截到 221/456 字符,__p 从 5 段变成 4 段。 + * + * 做法:从左往右数括号,depth 变负说明这个 ')' 没有对应 '(', + * 它不属于 URL → 从这里截断。 + */ + private static trimUrlTail(raw: string): string { + let depth: number = 0; + let cut: number = raw.length; + for (let i = 0; i < raw.length; i++) { + const ch: string = raw.charAt(i); + if (ch === '(') { + depth++; + } else if (ch === ')') { + depth--; + if (depth < 0) { + // 多出来的右括号 → URL 到此为止 + cut = i; + break; + } + } + } + let out: string = raw.substring(0, cut); + // 再裁掉可能粘在末尾的中文标点(用户手动加的全角句号/逗号等) + while (out.length > 0) { + const last: string = out.charAt(out.length - 1); + if (last === '。' || last === ',' || last === '、' || last === ';' || + last === '!' || last === '?') { + out = out.substring(0, out.length - 1); + } else { + break; + } + } + return out; + } + private static async parseUrl(text: string): Promise { // 从可能夹带其他文字的段落里抽出第一个 http(s) 链接 - const urlMatch: RegExpExecArray | null = /https?:\/\/[^\s"'<>))]+/i.exec(text); + const urlMatch: RegExpExecArray | null = /https?:\/\/[^\s"'<>()【】]+/i.exec(text); if (urlMatch === null) { return null; } - const url: string = urlMatch[0]; + const url: string = PlaceParser.trimUrlTail(urlMatch[0]); // ① poi/detail: __p=,,,, const p: ParsedPlace | null = PlaceParser.parseAmapP(url); if (p !== null) { diff --git a/entry/src/main/ets/pages/EventEditPage.ets b/entry/src/main/ets/pages/EventEditPage.ets index 86fc624..108a7b1 100644 --- a/entry/src/main/ets/pages/EventEditPage.ets +++ b/entry/src/main/ets/pages/EventEditPage.ets @@ -1754,7 +1754,11 @@ struct EventEditPage { // 外观与右边的「地图」按钮保持一致:同字号、同色、同底色、同圆角、同高度。 PasteButton({ text: PasteDescription.PASTE, - buttonType: ButtonType.Capsule + // ⚠️ 必须用 Normal,不能用 Capsule: + // 按 SDK 文档,Capsule / Circle 下 borderRadius **不生效**, + // 圆角由按钮类型自动决定(= 两端全圆),与「地图」的圆角矩形不一致。 + // 只有 Normal / ROUNDED_RECTANGLE 才认 borderRadius。 + buttonType: ButtonType.Normal }) .fontSize(13) .fontColor($r('app.color.brand_text'))