v68:修好日程「地点」的高德联想(v54 起静默失效)+ 账本四条动作路由
用户 2026-10-20:「网页端添加日程的时候,输入地点,应该调用高德接口,
获得准确的详细地址,现在这个功能不好用了,先修一下。」
## 根因不是 key、不是域名白名单 —— 是插件从来没被拉起来
`layout.php` 里 `fb-amap` meta 给的 SDK 地址**不带 `plugin`**,而加载器优先用它
⇒ `window.AMap.AutoComplete` **恒为 undefined** ⇒ 联想实例建不出来。
更糟的是 `addr_suggest.js` **一进函数就打 `data-addr-bound='1'`**,
于是"绑定失败"被记成"已经绑定过",此后**再也不重试** —— 全链路静默,
界面上就是"输了字什么也不弹"。
(用真浏览器逐环量过才定位:key 有效、`inputtips` 真能返回 10 条候选,
问题全在我们自己这一侧。)
修法(双保险):
- `layout.php` 新增 `needAutoComplete()`:SDK 到位后 `AMap.plugin('AMap.AutoComplete', cb)`,
超时也放行;启动顺序改成 **SDK → 插件 → 我们的脚本**。
- `addr_suggest.js` 就绪判据收紧成 `!!(AMap && AMap.AutoComplete)`;
`data-addr-bound` 改为 **new 成功之后**才打;新增 `ensurePlugin()`
并监听 `fb-amap-ready` 自愈(插件晚到也能接上)。
## 「准确的详细地址」
日程的地点**只有一栏**(`_event_fields.php` 只有 location),只把名字写回去,
回头要导航/找人还是查不到门牌。⇒ 地点框加 `data-addr-detail="1"`,
选中候选后写「名字 · 详细地址」(名字在前,人认的是名字),
并在这一行下面给一条「高德:省市区 · 门牌」提示。
⚠️ 提示行必须插在**整行之后**,塞进 `.mv-row` 会把 62px 标签列挤掉(v24 踩过)。
⚠️ 详细地址必须**延后一拍**写:AutoComplete 在 select 回调之后还会把 `poi.name`
塞回输入框,同步写会被它覆盖(第一版就因此白写)。
## 账本:服务端补四条路由
`Ledger::rename / setShare / unshare / shares / updateEntry / removeEntry`
**模型一直都有**,`ApiHandler::ledger()` 却只开了「建账本 / 记一笔 / 删账本」
⇒ 端上只能把「共享给谁」「账本名字」画成灰条,账目也改不了删不掉。
新增:`PATCH /ledger/<id>`(改名)、`/ledger/<id>/shares`(GET/POST/DELETE)、
`PATCH|DELETE /ledger/<id>/entries`(id 走 `Api::int('id')` ——
`Api::dispatch()` 只解析 3 段,`/ledger/3/entries/17` 的第 4 段会被丢掉)。
`Ledger::mine/find` 补 `is_owner`(`my_perm` 只有 read/write,分不出主人);
改共享只允许主人(422)。
## 验证
- `familybook_push.sh` 全量流水线跑到 v47 停在 1 条失败:
「③ 选中候选后地点被换成规范地名」—— 因为这条旧契约是 `loc == '万象城 CGV'`,
而我们现在**有意**写成「名字 · 详细地址」。
⇒ 断言**升级**(不是放宽):既要求名字在前、又要求门牌进来,比原来更严。
- `_fb_v47_addr_sim.py` 复验 **16 / 0**;
并做注入验证:把 `data-addr-detail="1"` 删掉 → ③ 立刻变红(证明断言真守着)。
- `_fb_v68_addr.py` 三模式 5/0(`--old` 拆掉两处时候选 0 条、`--one` 只拆 layout 仍 5/0)。
- 线上核对:`/api/v1/ledger/1/shares` 返回 **401**(不是 404,路由已生效)。
This commit is contained in:
@@ -64,10 +64,18 @@ $evLab = static function (string $text, string $for = '', bool $star = false) us
|
||||
换成高德认得的规范地名",后续要查经纬度/导航才查得到。
|
||||
⚠️ 这段表单既在整页(各视图的日程弹框)也在 AJAX 片段里出现,
|
||||
所以打好 `data-addr` 标记即可 —— 由 layout 的 loader 与
|
||||
弹框打开时的 `fbLoadAddr(scope)` 负责绑定(与 person_picker 同一套)。 */ ?>
|
||||
弹框打开时的 `fbLoadAddr(scope)` 负责绑定(与 person_picker 同一套)。
|
||||
🔴 v68(用户 2026-10-20):「输入地点,应该调用高德接口,获得**准确的详细地址**,
|
||||
现在这个功能不好用了。」
|
||||
① 不好用的真根因不在这一行(是 `layout.php` 的加载器取了不带
|
||||
`plugin=AMap.AutoComplete` 的 SDK 地址,见那里的注释);
|
||||
② 「详细地址」这一条要额外做点事:这一栏**只有它一个字段**,
|
||||
光把名字写回来,门牌号仍然没有 ⇒ 加 `data-addr-detail="1"`:
|
||||
选中候选时把地址一并写进 value(`名字 · 门牌`),
|
||||
并在这一行下面用一行灰字显示「省市区 · 门牌」。 */ ?>
|
||||
<input class="input" id="ev-location" name="location"
|
||||
value="<?= e((string)($evF['location'] ?? '')) ?>" placeholder="如:学校三楼会议室"
|
||||
data-addr>
|
||||
data-addr data-addr-detail="1">
|
||||
</div>
|
||||
|
||||
<?php echo $evRowOpen(); ?>
|
||||
|
||||
+45
-1
@@ -376,6 +376,9 @@ window.FB_NOTIFY = {
|
||||
s.setAttribute('data-fb-amap', '1');
|
||||
s.onload = function () {
|
||||
window.__AMAP_LOADED = true;
|
||||
/* v68:通知"SDK 到了"。`addr_suggest.js` 可能在 SDK 之前就执行过
|
||||
(比如页面自己先引了那份脚本),它监听这个事件补一次按需加载插件 + 重扫。 */
|
||||
try { window.dispatchEvent(new Event('fb-amap-ready')); } catch (e) {}
|
||||
res(typeof window.AMap !== 'undefined');
|
||||
};
|
||||
s.onerror = function () { sdkReady = null; res(false); }; // 失败不锁死,下次可重试
|
||||
@@ -384,12 +387,53 @@ window.FB_NOTIFY = {
|
||||
return sdkReady;
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔴🔴 v68(用户 2026-10-20):「日程里输入地点应该调高德获得详细地址,
|
||||
* 现在这个功能不好用了。」
|
||||
*
|
||||
* 真根因(**不是 key 失效,也不是域名白名单**,真浏览器逐环量过):
|
||||
* v54 给旅程地图加了 `fb-amap` 这条 meta(SDK 地址**不带** `plugin=AMap.AutoComplete`),
|
||||
* 同时把加载器的配置源改成 `metaAmap || metaAddr` ⇒ 拉的是**不带插件**的那个地址。
|
||||
* SDK 的 `script.onload` 时 `AMap` 有了、`AMap.AutoComplete` 是 undefined
|
||||
* (实测 `{"ac":"undefined","amap":"object"}`)⇒ `addr_suggest.js` 的 `sdkReady()`
|
||||
* 恒假 ⇒ 联想一次都没绑上。而"地图视图"本身只用 `AMap.Map`,所以这条路一直都正常,
|
||||
* 坏的只有联想 —— 表现就是"输入地点没反应",且**一声不响**。
|
||||
*
|
||||
* 两个保险都加上:
|
||||
* ① 这里在绑联想之前**显式**把插件按需拉起来(`AMap.plugin`,高德 2.0 的官方姿势;
|
||||
* 带 `&plugin=` 参数也行,但那条只能由 SDK 地址决定,容易再被别的入口绕开);
|
||||
* ② `addr_suggest.js` 那边不再"没就绪就打已绑定标记"(见该文件的 v68 注释)。
|
||||
* @return Promise<boolean> 插件是否可用
|
||||
*/
|
||||
function needAutoComplete() {
|
||||
if (typeof window.AMap === 'undefined') { return Promise.resolve(false); }
|
||||
if (window.AMap.AutoComplete) { return Promise.resolve(true); }
|
||||
return new Promise(function (res) {
|
||||
var done = false;
|
||||
function fin(ok) { if (!done) { done = true; res(!!ok); } }
|
||||
try {
|
||||
window.AMap.plugin('AMap.AutoComplete', function () {
|
||||
fin(window.AMap && window.AMap.AutoComplete);
|
||||
});
|
||||
} catch (e) { fin(false); }
|
||||
/* 插件是异步 chunk:网络慢时别把整页流程挂住 —— 超时后按"还没好"返回,
|
||||
`addr_suggest.js` 会自己再兜一次(它也认 `AMap.plugin`)。 */
|
||||
setTimeout(function () { fin(window.AMap && window.AMap.AutoComplete); }, 8000);
|
||||
});
|
||||
}
|
||||
|
||||
/* 安全密钥必须在 SDK **之前**设好(高德要求),否则 AutoComplete 会被拒。 */
|
||||
function boot() {
|
||||
/* ⚠️ 密钥的设置已经收进 `fbAmapSdk()`(地图与联想两个入口共用一处),
|
||||
这里只需要"先 SDK、再我们的脚本"这个顺序。 */
|
||||
这里只需要"先 SDK、再插件、最后我们的脚本"这个顺序。
|
||||
🔴 插件那一步**不管成功与否都继续**去拉我们的脚本:
|
||||
插件 8 秒没到(弱网)也要让 `addr_suggest.js` 上线 ——
|
||||
它自己还会再 `AMap.plugin` 一次并监听 `fb-amap-ready`。
|
||||
在这里 `return false` 会连脚本都不加载 ⇒ 连"自愈"的机会都没有。 */
|
||||
return window.fbAmapSdk().then(function (ok) {
|
||||
if (!ok) { return false; }
|
||||
return needAutoComplete();
|
||||
}).then(function () {
|
||||
return loadOurs();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user