Quellcode durchsuchen

fix(顾客页): 标签页标题不被后台系统标题覆盖,离开时还原

顾客自助收银台(免登录 /#/checkout/<token>)面对的是正要付钱的人,
标签页上挂着管理端的名字(sys_web_title,「公司管理」)不合适。
两处一起改才成立:

- checkout/index.vue:onMounted 写自己的标题(Payment Platform),
  onUnmounted 把**进来之前那一份**写回去 —— 这是 SPA,浏览器后退键会把人
  送回后台那一屏,不还原的话后台标签页会一直挂着顾客页的名字。
  存的是自己读到的原值,不是写死一个常量回填。
- main.ts:setTitleAndFavicon() 是异步的(先 await 一次配置请求),
  不拦的话配置晚到一步就会把顾客页标题改回 sys_web_title,
  症状是「标题偶尔又变回公司管理」。判据用 hash 前缀而不是路由名 ——
  本函数在 router 就绪之前就会跑,那时读不到 currentRoute。

验证(frontend/_verify-checkout/title.mjs,4 条断言全绿):
本仓没有 jsdom,改用 createRenderer + 自写内存 nodeOps,真 vue 运行时、
onMounted/onUnmounted 真的会触发。T1 挂载后标题变、T2 卸载后还原;
T3 顾客页 hash 下 sys_web_title 不生效;T4 是正对照 —— 后台 hash 下它照常生效
(没有这条,「把整个功能删掉」也能让 T3 通过)。
变异:去掉 main.ts 那道守卫 ⇒ 只有 T3 红;去掉 onMounted 里的标题写入 ⇒ 只有 T1 红。
alphaH vor 2 Wochen
Ursprung
Commit
ca27929e32
2 geänderte Dateien mit 41 neuen und 3 gelöschten Zeilen
  1. 7 1
      frontend/src/main.ts
  2. 34 2
      frontend/src/views/checkout/index.vue

+ 7 - 1
frontend/src/main.ts

@@ -32,7 +32,13 @@ const setTitleAndFavicon = async () => {
     const webFavicon = configStore.configData.sys_web_favicon?.config_value;
     const webLogo = configStore.configData.sys_web_logo?.config_value;
 
-    if (webTitle) {
+    // ★ 顾客页(免登录收款链接 /#/checkout/<token>)自带标题,别覆盖它。
+    //   本函数是**异步**的(上面 await 了一次配置请求),而顾客页在 onMounted 里就写好了
+    //   自己的标题 ⇒ 不加这道判断时,配置晚到一步就会把顾客页的标签页改回 `sys_web_title`
+    //   (管理端的名字),症状是「标题偶尔又变回公司管理」。
+    //   判据用 hash 前缀而不是路由名:本函数在 router 就绪之前就会跑,那时还读不到 currentRoute。
+    const isPublicCheckout = window.location.hash.startsWith("#/checkout/");
+    if (webTitle && !isPublicCheckout) {
       document.title = webTitle;
     }
 

+ 34 - 2
frontend/src/views/checkout/index.vue

@@ -90,7 +90,7 @@ defineOptions({
   inheritAttrs: false,
 });
 
-import { onMounted, ref } from "vue";
+import { onMounted, onUnmounted, ref } from "vue";
 import { useRoute } from "vue-router";
 import axios from "axios";
 
@@ -104,6 +104,17 @@ import axios from "axios";
  */
 const MIN_FEN = 100;
 
+/**
+ * 顾客页的浏览器标题。**有意与后台不同**。
+ *
+ * 【为什么不能靠全局那支】全站标题由 `main.ts` 的 `setTitleAndFavicon()` 从
+ * `sys_web_title`(库里的系统配置)取,值是**管理端**的名字(「公司管理」)——
+ * 那是给操作员看的,不该出现在一位正要付钱的顾客的标签页上。
+ * 而且那个函数是**异步**的(要先等一次配置请求),谁先跑完不确定 ⇒
+ * 光在这里写还不够,`main.ts` 那侧也加了一道同名判据(见那里的注释)。
+ */
+const PAGE_TITLE = "Payment Platform";
+
 type Phase = "loading" | "invalid" | "ready";
 
 /** 只看 `code` 的最小信封形状。`Result.ok` 的成功码是 **0**(不是 200)。 */
@@ -251,7 +262,28 @@ async function submit() {
   }
 }
 
-onMounted(loadInfo);
+/**
+ * 进页面时换成顾客页自己的标题,**离开时还原**。
+ *
+ * ★ 还原不是洁癖:这是一个 SPA,浏览器后退键会把人送回后台那一屏,
+ *   而 `main.ts` 只在应用启动时设一次标题 —— 不还原的话,后台的标签页会一直挂着
+ *   「Payment Platform」,而那个名字对操作员没有任何意义。
+ * ★ 自己之前的标题由**自己**存一份再写回(不是写死一个常量回填):将来若全站标题
+ *   改成从别处来,这里不需要跟着改。
+ */
+let titleBefore: string | null = null;
+
+onMounted(() => {
+  titleBefore = document.title;
+  document.title = PAGE_TITLE;
+  loadInfo();
+});
+
+onUnmounted(() => {
+  if (titleBefore !== null) {
+    document.title = titleBefore;
+  }
+});
 </script>
 
 <style lang="scss" scoped>