SyuanYu 7 달 전
부모
커밋
9b155d5e42
5개의 변경된 파일280개의 추가작업 그리고 72개의 파일을 삭제
  1. 273 65
      src/components/Chat.vue
  2. 2 2
      src/language/en.json
  3. 2 2
      src/language/ja.json
  4. 2 2
      src/language/ko.json
  5. 1 1
      src/language/zh.json

+ 273 - 65
src/components/Chat.vue

@@ -1,5 +1,12 @@
 <script setup>
-import { ref, reactive, onMounted, watch, nextTick } from "vue";
+import {
+  ref,
+  reactive,
+  onMounted,
+  onBeforeUnmount,
+  watch,
+  nextTick,
+} from "vue";
 import { useRoute } from "vue-router";
 // VR
 import "aframe";
@@ -138,12 +145,34 @@ let messages = ref([]);
 watch(messages.value, (val) => {
   console.log("messages", val);
   // scrollToBottom();
-  // 判斷最後一個值是否為文字訊息
-  if (
-    messages.value.length > 0 &&
-    messages.value[messages.value.length - 1].label !== "brand"
-  ) {
-    scrollToBottom();
+  //  // 判斷最後一個值是否為文字訊息
+  // if (
+  //   messages.value.length > 0 &&
+  //   messages.value[messages.value.length - 1].label !== "brand"
+  // )
+
+  if (messages.value.length > 0) {
+    let lastMessage = messages.value[messages.value.length - 1]; // 取得最後一個訊息
+
+    setTimeout(() => {
+      // 若訊息為按鈕選單則滑至底部
+      if (
+        lastMessage.label === "observation_deck" ||
+        lastMessage.label === "check" ||
+        lastMessage.label === "visit" ||
+        lastMessage.label === "navigation" ||
+        lastMessage.label === "garden_route" ||
+        lastMessage.label === "dining" ||
+        lastMessage.label === "shopping" ||
+        lastMessage.label === "shopping_brand" ||
+        lastMessage.label === "service" ||
+        lastMessage.label === "ticket"
+      ) {
+        scrollToBottom();
+      } else {
+        scrollToMessage();
+      }
+    }, 100);
   }
   updateMenuHeight();
 });
@@ -153,30 +182,51 @@ let qaQuery = reactive([]);
 
 const chatArea = ref(null); // 對話框
 
-// 滾動到對話框底部
-const scrollToBottom = () => {
-  setTimeout(() => {
-    // chatArea.value.scrollTop = chatArea.value.scrollHeight;
-    console.log("chatArea.value", chatArea.value.scrollHeight);
-
-    chatArea.value.scrollTo({
-      top: chatArea.value.scrollHeight,
-      behavior: "smooth",
-    });
+// 捲軸滾動到使用者訊息底下第一則回覆
+const scrollToMessage = () => {
+  // 使用 nextTick 確保 DOM 已更新
+  nextTick(() => {
+    // 取得所有的 .message.message-out 元素
+    let messageOutElements = document.querySelectorAll(".message.message-out");
+
+    if (messageOutElements.length > 0) {
+      // 取得倒數第一個 .message.message-out
+      const lastMessageOut = messageOutElements[messageOutElements.length - 1];
+
+      // 取得該元素後的第一個 .message-content
+      const nextMessageContent =
+        lastMessageOut.parentElement.nextElementSibling;
+
+      if (
+        nextMessageContent &&
+        nextMessageContent.classList.contains("message-content")
+      ) {
+        // 取得該元素的滾動高度
+        const scrollTop = nextMessageContent.offsetTop;
+
+        setTimeout(() => {
+          // chatArea.value.scrollTop = scrollTop;
+
+          chatArea.value.scrollTo({
+            top: scrollTop - 45,
+            behavior: "smooth",
+          });
+        }, 100);
+      }
+    }
+  });
+};
 
-    // window.scrollTo({
-    //   top: document.body.scrollHeight,
-    //   behavior: "smooth", // 平滑滾動
-    // });
-  }, 100);
-
-  // let list = messages.value;
-  // const item = list[list.length - 1];
-  // if (item.label === "text") {
-  //   setTimeout(() => {
-  //     chatArea.value.scrollTop = chatArea.value.scrollHeight;
-  //   }, 100);
-  // }
+// 捲軸滾動到對話框底部
+const scrollToBottom = () => {
+  nextTick(() => {
+    setTimeout(() => {
+      chatArea.value.scrollTo({
+        top: chatArea.value.scrollHeight,
+        behavior: "smooth",
+      });
+    }, 100);
+  });
 };
 
 // 對話選項(按鈕)
@@ -282,15 +332,12 @@ async function sendMessage(type = "") {
 
           response.data.data.map((item) => {
             if (item.type === "button") {
-              console.log("按鈕");
               info.buttonList.push(item);
               labelContent = "ticket";
             } else if (item.type === "ticket") {
-              console.log("票");
               info.ticketList.push(item);
               labelContent = "ticket";
             } else if (item.type === "brand") {
-              console.log("品牌");
               info.ticketList.push(item);
               labelContent = "brand";
             }
@@ -491,6 +538,8 @@ function chooseLang(lang) {
     sources = videoSourcesEn.value;
   } else if (language === "ja-jp") {
     sources = videoSourcesJp.value;
+  } else if (language === "ko-kr") {
+    sources = videoSourcesKo.value;
   } else {
     sources = videoSources.value;
   }
@@ -1488,6 +1537,7 @@ function assignMapImg(item) {
 const videoSources = ref([]); // 開場白影片(中)
 const videoSourcesEn = ref([]); // 開場白影片(英)
 const videoSourcesJp = ref([]); // 開場白影片(日)
+const videoSourcesKo = ref([]); // 開場白影片(韓)
 const videoMuteSources = ref([]); // 點頭影片(靜音)
 const videoSpeakSources = ref([]); // 動嘴型影片
 
@@ -1510,6 +1560,7 @@ const loadVideoSources = async () => {
     "https://cmm.ai/101-video/start_en_2.mp4",
   ];
   videoSourcesJp.value = ["https://cmm.ai/101-video/start_jp_2.mp4"];
+  videoSourcesKo.value = ["https://cmm.ai/101-video/start_ko_2.mp4"];
   videoMuteSources.value = [
     // "https://cmm.ai/101-ai-chatbot-new/video/mute_1.mp4",
     "https://cmm.ai/101-video/mute_2.mp4",
@@ -1552,6 +1603,8 @@ async function selectCategory(value, index) {
         sources = videoSourcesEn.value;
       } else if (lang === "jp") {
         sources = videoSourcesJp.value;
+      } else if (lang === "ko") {
+        sources = videoSourcesKo.value;
       }
 
       const randomIndex = Math.floor(Math.random() * sources.length);
@@ -1601,6 +1654,12 @@ async function selectCategory(value, index) {
     // });
     funFilterDialog.value = true;
   } else if (value === "秘境花園觀景台") {
+    messages.value.push({
+      label: "text",
+      author: "user",
+      body: t("observation_deck"),
+    });
+
     messages.value.push({
       label: "text",
       author: "ai",
@@ -1615,6 +1674,12 @@ async function selectCategory(value, index) {
   } else if (value === "位置導引") {
     assignLocationText.value = "";
 
+    messages.value.push({
+      label: "text",
+      author: "user",
+      body: t("location_guide"),
+    });
+
     messages.value.push({
       label: "text",
       author: "ai",
@@ -1642,6 +1707,12 @@ async function selectCategory(value, index) {
   } else if (value === "美食/伴手禮") {
     // getAd("美食伴手禮");
 
+    messages.value.push({
+      label: "text",
+      author: "user",
+      body: t("food_souvenirs"),
+    });
+
     messages.value.push({
       label: "text",
       author: "ai",
@@ -1655,6 +1726,11 @@ async function selectCategory(value, index) {
     });
   } else if (value === "購物及優惠") {
     // getAd("購物及優惠");
+    messages.value.push({
+      label: "text",
+      author: "user",
+      body: t("shopping_discounts"),
+    });
 
     messages.value.push({
       label: "shopping",
@@ -1662,6 +1738,12 @@ async function selectCategory(value, index) {
       body: shoppingList,
     });
   } else if (value === "服務資訊") {
+    messages.value.push({
+      label: "text",
+      author: "user",
+      body: t("service_information"),
+    });
+
     messages.value.push({
       label: "text",
       author: "ai",
@@ -1829,6 +1911,12 @@ const menuList = reactive([
 async function findBrand(value) {
   console.log("findBrand", value);
 
+  messages.value.push({
+    label: "text",
+    author: "user",
+    body: value,
+  });
+
   if (value === "館外店家") {
     value = "館外";
   }
@@ -1849,7 +1937,7 @@ async function findBrand(value) {
     assignCategoryIndex.value = null;
     assignCategory.value = "";
 
-    scrollToLastMessage();
+    // scrollToLastMessage();
   } catch (error) {
     console.log("error", error);
   }
@@ -1885,6 +1973,12 @@ async function getStaticTickets(type) {
 function handleObservationDialog(value) {
   console.log("value", value);
 
+  messages.value.push({
+    label: "text",
+    author: "user",
+    body: value,
+  });
+
   if (value === "線上購票") {
     messages.value.push({
       label: "text",
@@ -2013,10 +2107,16 @@ function handleShoppingDialog(value) {
     });
 
     messages.value.push({
-      label: "text",
+      label: "tourist_card",
       author: "ai",
       body: t("shopping_discounts_info.tourist_card.content"),
     });
+
+    // messages.value.push({
+    //   label: "text",
+    //   author: "ai",
+    //   body: t("shopping_discounts_info.tourist_card.content"),
+    // });
   } else if (value === "參觀資訊") {
     messages.value.push({
       label: "text",
@@ -2216,9 +2316,17 @@ async function handleAudioToText() {
       break;
   }
 
-  // let url = `http://172.104.93.163:9880/whisper/${audioLang}/`;
-  // let url = `https://cmm.ai:9001/whisper/${audioLang}/`;
-  let url = `https://cmm.ai:9001/gcp/speech-to-text?language_code=${audioLang}`;
+  let url;
+
+  if (lang === "ko-kr") {
+    // 韓文使用 azure
+    url = `https://cmm.ai:9001/azure/speech-to-text?language_code=${audioLang}`;
+  } else {
+    // 其他語言使用 gcp
+    url = `https://cmm.ai:9001/gcp/speech-to-text?language_code=${audioLang}`;
+  }
+
+  // let url = `https://cmm.ai:9001/gcp/speech-to-text?language_code=${audioLang}`;
 
   const formData = new FormData();
   formData.append("file", audioFile.value);
@@ -2229,11 +2337,19 @@ async function handleAudioToText() {
     console.log("語音轉文字 response", response);
     // showAnchor.value = false; // 關閉主播視窗
 
-    userMessage.value = response.data[0];
+    let message;
+
+    if (lang === "ko-kr") {
+      message = response.data;
+    } else {
+      message = response.data[0];
+    }
+
+    userMessage.value = message;
 
     // handleTTS(userMessage.value); // 取得語音回覆
 
-    if (response.data[0] && response.data[0] !== "") {
+    if (message && message !== "") {
       sendMessage(); // 傳送使用者訊息
     } else {
       if (showAnchor.value) {
@@ -2317,6 +2433,18 @@ function recStop() {
       // rec.close(); // 釋放錄音資源 (若不釋放系統或瀏覽器將持續提示在錄音中)
       // rec = null;
 
+      // // 韓文轉 wav 格式,其他語言轉 mp3 格式
+      // let lang = getLang();
+      // if (lang === "ko") {
+      //   audioFile.value = new File([blob], "recording.wav", {
+      //     type: "audio/wav",
+      //   });
+      // } else {
+      //   audioFile.value = new File([blob], "recording.mp3", {
+      //     type: "audio/mp3",
+      //   });
+      // }
+
       // 將 Blob 轉換為 File 對象
       audioFile.value = new File([blob], "recording.mp3", {
         type: "audio/mp3",
@@ -2395,15 +2523,12 @@ function setInfo(data) {
 
   data.map((item) => {
     if (item.type === "button") {
-      console.log("按鈕");
       info.buttonList.push(item);
       labelContent = "ticket";
     } else if (item.type === "ticket") {
-      console.log("票");
       info.ticketList.push(item);
       labelContent = "ticket";
     } else if (item.type === "brand") {
-      console.log("品牌");
       info.ticketList.push(item);
       labelContent = "brand";
     }
@@ -2541,26 +2666,22 @@ async function messageNotInCache(question, answer) {
   }
 }
 
-const scrollToLastMessage = () => {
-  nextTick(() => {
-    console.log(">>>");
-
-    const lastMessage = chatArea.value?.querySelector(
-      ".message-content:last-child"
-    );
-
-    console.log("lastMessage", lastMessage);
-
-    if (lastMessage) {
-      const scrollTop = lastMessage.offsetTop;
-      console.log("捲軸高度:", scrollTop);
-      setTimeout(() => {
-        chatArea.value.scrollTop = lastMessage.offsetTop - 45;
-      }, 500);
-      console.log("chatArea.value.scrollTop", chatArea.value.scrollTop);
-    }
-  });
-};
+// // 捲軸捲動到最後一個訊息
+// const scrollToLastMessage = () => {
+//   nextTick(() => {
+//     const lastMessage = chatArea.value?.querySelector(
+//       ".message-content:last-child"
+//     );
+
+//     if (lastMessage) {
+//       const scrollTop = lastMessage.offsetTop;
+//       console.log("捲軸高度:", scrollTop);
+//       setTimeout(() => {
+//         chatArea.value.scrollTop = lastMessage.offsetTop - 45;
+//       }, 500);
+//     }
+//   });
+// };
 
 // 判斷輸入框狀態
 let inputFocus = ref(false);
@@ -2572,6 +2693,55 @@ const handleFocus = () => {
 const handleBlur = () => {
   inputFocus.value = false;
 };
+
+// 判斷影片區塊背景色
+function videoSrcBg() {
+  // 四種語言開場白 & 純點頭 & 對嘴影片
+  if (
+    videoSrc.value === videoSources.value[0] ||
+    videoSrc.value === videoSourcesEn.value[0] ||
+    videoSrc.value === videoSourcesJp.value[0] ||
+    videoSrc.value === videoSourcesKo.value[0] ||
+    videoSrc.value[0] === videoMuteSources.value[0] ||
+    videoSrc.value === videoSpeakSources.value[0]
+  ) {
+    return "#cfba93";
+  } else {
+    return "#cab78e";
+  }
+}
+
+// 時間內無動作需 reload
+let timeout;
+
+const resetTimeout = () => {
+  clearTimeout(timeout);
+  timeout = setTimeout(() => {
+    window.location.reload(); // 重新整理網頁
+  }, 5 * 60 * 1000); // 5 分鐘 = 5 * 60 * 1000 毫秒
+};
+
+const startInactivityTimer = () => {
+  console.log("startInactivityTimer", timeout);
+
+  window.addEventListener("mousemove", resetTimeout);
+  window.addEventListener("keydown", resetTimeout);
+  resetTimeout(); // 初始化計時器
+};
+
+const stopInactivityTimer = () => {
+  clearTimeout(timeout);
+  window.removeEventListener("mousemove", resetTimeout);
+  window.removeEventListener("keydown", resetTimeout);
+};
+
+onMounted(() => {
+  startInactivityTimer();
+});
+
+onBeforeUnmount(() => {
+  stopInactivityTimer();
+});
 </script>
 
 <template>
@@ -2589,7 +2759,12 @@ const handleBlur = () => {
   </div>
 
   <div v-else class="main-containar">
-    <div class="video-content">
+    <div
+      class="video-content"
+      :style="{
+        backgroundColor: videoSrcBg(),
+      }"
+    >
       <!-- 語言選單 -->
       <div class="lang-select">
         <v-select
@@ -3553,6 +3728,37 @@ const handleBlur = () => {
               <span class="price-item">{{ message.body.price }}</span>
             </section>
           </div>
+
+          <!-- 國際貴賓卡專屬禮遇 -->
+
+          <div
+            v-if="message.label === 'tourist_card'"
+            class="message animate__animated"
+            :class="{
+              'message-out': message.author === 'user',
+              'message-in': message.author !== 'user',
+              animate__fadeInRight: message.author === 'user',
+              animate__fadeInLeft: message.author !== 'user',
+            }"
+          >
+            <p v-html="t('shopping_discounts_info.tourist_card.content')"></p>
+            <button
+              @click="
+                openQrcodeDialog(
+                  'https://stage.taipei101mall.com.tw/join-member/AIsystem'
+                )
+              "
+              class="ar-link mt-5 mb-3"
+            >
+              {{ t("shopping_discounts_info.tourist_card.apply") }}
+            </button>
+            <!-- <a
+              href="https://stage.taipei101mall.com.tw/join-member/AIsystem"
+              class="ar-link mb-3"
+              target="_blank"
+              >立即線上申辦</a
+            > -->
+          </div>
         </div>
       </section>
 
@@ -3897,7 +4103,8 @@ const handleBlur = () => {
     display: flex;
     justify-content: center;
     transform: scale(1);
-    background-color: #cab78e;
+    // background-color: #cfba93;
+    // background-color: #cab78e;
     // background-color: #d1bf99;
     // background-color: var(--sub-color);
 
@@ -4572,6 +4779,7 @@ const handleBlur = () => {
   background-color: var(--main-color);
   border-radius: 5px;
   text-decoration: none;
+  letter-spacing: 1px;
 }
 
 .ar-card {

+ 2 - 2
src/language/en.json

@@ -18,7 +18,7 @@
   "fun_filter": "Fun Filter",
   "type_message": "Type a message",
   "ar_tour": "AR navigation",
-  "select_location": "Please select you location for AR navigation",
+  "select_location": "Please select your location",
   "question": "Click me to ask a question",
   "scan_qr_code": "Please scan the QR code to proceed",
   "qr_code_scan_prompt": "Please scan QR Code to obtain current location",
@@ -96,7 +96,7 @@
     },
     "tourist_card": {
       "title": "Privileges for Taipei 101 Tourist Card",
-      "content": "Visit Taipei 101 to enjoy 2024 exclusive benefits - Once you apply for the Taipei 101 Tourist Card, you can enjoy exclusive benefits only for international travelers.<br>‧Shopping - Special discounts starting from 10% off on selected brands.<br>‧Gifts - Welcome Pack + NTD300 Cash Voucher<br>‧Tax Refund - Spend over NTD2000 can proceed fast 5% tax refund on B1.<br><br>Welcome to the Customer Service Center on B1 to fill out the application form and enjoy exclusive benefits.<br><br><a href='https://stage.taipei101mall.com.tw/?lang=zh&utm_source=101TTE_PH&utm_medium=Travel_Fair&utm_campaign=Stage' class='ar-link mb-3' target='_blank'>Apply online now</a>",
+      "content": "Visit Taipei 101 to enjoy 2024 exclusive benefits - Once you apply for the Taipei 101 Tourist Card, you can enjoy exclusive benefits only for international travelers.<br>‧Shopping - Special discounts starting from 10% off on selected brands.<br>‧Gifts - Welcome Pack + NTD300 Cash Voucher<br>‧Tax Refund - Spend over NTD2000 can proceed fast 5% tax refund on B1.<br><br>Welcome to the Customer Service Center on B1 to fill out the application form and enjoy exclusive benefits.",
       "apply": "Apply online now"
     }
   },

+ 2 - 2
src/language/ja.json

@@ -18,7 +18,7 @@
   "fun_filter": "面白いフィルター",
   "type_message": "メッセージを入力してください",
   "ar_tour": "ARナビを利用",
-  "select_location": "あなたの現在地を選んで、ARナビを利用します。",
+  "select_location": "位置を選択してください",
   "question": "質問するにはここをクリック",
   "scan_qr_code": "QRコードをスキャンして進んでください",
   "qr_code_scan_prompt": "QRコードをスキャンして現在位置を取得してください",
@@ -96,7 +96,7 @@
     },
     "tourist_card": {
       "title": "台北101提携カード割引",
-      "content": "2024 年には、台北 101 の提携ブランド カードである Dingji Unlimited Card をスワイプすると、三つの特典を獲得できます。<br>‧ショッピング:ブランドをで 10% 割引<br>‧特典:Welcome Pack+ NTD300現金割引券をプレゼント<br>‧外国人旅行者への税還付:同日に台北101ショッピングモールで累計2,000元(税込)のお買い物をされた方は地下1階の「税還付サービスセンター」ですぐ還付手続きを行えます。<br><br>台北101地下1階カスタマーサービスセンターにて、提携カードを申請し、今すぐ特典をゲットしましょう!<br><br><a href='https://stage.taipei101mall.com.tw/?lang=zh&utm_source=101TTE_PH&utm_medium=Travel_Fair&utm_campaign=Stage' class='ar-link mb-3' target='_blank'>インターネットお申し込み</a>",
+      "content": "2024 年には、台北 101 の提携ブランド カードである Dingji Unlimited Card をスワイプすると、三つの特典を獲得できます。<br>‧ショッピング:ブランドをで 10% 割引<br>‧特典:Welcome Pack+ NTD300現金割引券をプレゼント<br>‧外国人旅行者への税還付:同日に台北101ショッピングモールで累計2,000元(税込)のお買い物をされた方は地下1階の「税還付サービスセンター」ですぐ還付手続きを行えます。<br><br>台北101地下1階カスタマーサービスセンターにて、提携カードを申請し、今すぐ特典をゲットしましょう!",
       "apply": "インターネットお申し込み"
     }
   },

+ 2 - 2
src/language/ko.json

@@ -18,7 +18,7 @@
   "fun_filter": "재미있는 필터",
   "type_message": "메시지를 입력하세요",
   "ar_tour": "AR 투어 시작",
-  "select_location": "AR 투어를 위해 위치를 선택하세요",
+  "select_location": "위치를 선택해 주세요",
   "question": "질문하려면 클릭하세요",
   "scan_qr_code": "QR 코드를 스캔하여 진행하세요",
   "qr_code_scan_prompt": "현재 위치를 얻기 위해 QR 코드를 스캔하세요",
@@ -96,7 +96,7 @@
     },
     "tourist_card": {
       "title": "국제 VIP 카드 전용 혜택",
-      "content": "지금부터 타이베이 101에 오시면 2024년 특별 혜택을 제공해 드립니다-타이베이 101 국제 VIP 카드를 신청하시면, 국제 여행객 전용 3중 혜택을 누리실 수 있습니다:<br>‧쇼핑-브랜드 10% 할인 혜택<br>‧혜택-Welcome Pack+ NTD300 현금 할인 쿠폰<br>‧세금 환급-2,000 NTD 이상 구매 시 B1에서 세금 환급 가능하며, 5% 신속 처리 서비스를 제공합니다.<br><br>쇼핑몰 B1 고객 서비스 센터에서 신청서를 작성하시면 전용 혜택을 누리실 수 있습니다.<br><br><a href='https://stage.taipei101mall.com.tw/?lang=zh&utm_source=101TTE_PH&utm_medium=Travel_Fair&utm_campaign=Stage' class='ar-link mb-3' target='_blank'>지금 온라인 신청</a>",
+      "content": "지금부터 타이베이 101에 오시면 2024년 특별 혜택을 제공해 드립니다-타이베이 101 국제 VIP 카드를 신청하시면, 국제 여행객 전용 3중 혜택을 누리실 수 있습니다:<br>‧쇼핑-브랜드 10% 할인 혜택<br>‧혜택-Welcome Pack+ NTD300 현금 할인 쿠폰<br>‧세금 환급-2,000 NTD 이상 구매 시 B1에서 세금 환급 가능하며, 5% 신속 처리 서비스를 제공합니다.<br><br>쇼핑몰 B1 고객 서비스 센터에서 신청서를 작성하시면 전용 혜택을 누리실 수 있습니다",
       "apply": "지금 온라인 신청"
     }
   },

+ 1 - 1
src/language/zh.json

@@ -96,7 +96,7 @@
     },
     "tourist_card": {
       "title": "國際貴賓卡專屬禮遇",
-      "content": "即日起來台北101,提供2024年特別禮遇-申辦台北101國際貴賓卡,可享用國際旅客限定專屬三重好禮:<br>‧購物-品牌9折起特別優惠<br>‧禮遇-Welcome Pack+ NTD300現金折抵券<br>‧退稅-消費2000元以上可至B1退稅,並提供5%快速辦理服務<br><br>歡迎至購物中心B1客服中心填寫申請書,即可享有專屬優惠<br><br><a href='https://stage.taipei101mall.com.tw/join-member/AIsystem' class='ar-link mb-3' target='_blank'>立即線上申辦</a>",
+      "content": "即日起來台北101,提供2024年特別禮遇-申辦台北101國際貴賓卡,可享用國際旅客限定專屬三重好禮:<br>‧購物-品牌9折起特別優惠<br>‧禮遇-Welcome Pack+ NTD300現金折抵券<br>‧退稅-消費2000元以上可至B1退稅,並提供5%快速辦理服務<br><br>歡迎至購物中心B1客服中心填寫申請書,即可享有專屬優惠",
       "apply": "立即線上申辦"
     }
   },