SyuanYu 8 months ago
parent
commit
b5dd1cfa88

+ 15 - 0
package-lock.json

@@ -13,6 +13,7 @@
         "ar.js": "^2.2.2",
         "axios": "^1.6.7",
         "pinia": "^2.1.7",
+        "qrcode.vue": "^3.4.1",
         "recorder-core": "^1.3.24040900",
         "swiper": "^11.0.7",
         "vue": "^3.3.11",
@@ -1743,6 +1744,14 @@
         "once": "^1.3.1"
       }
     },
+    "node_modules/qrcode.vue": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.4.1.tgz",
+      "integrity": "sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==",
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/quad-indices": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/quad-indices/-/quad-indices-2.0.1.tgz",
@@ -3272,6 +3281,12 @@
         "once": "^1.3.1"
       }
     },
+    "qrcode.vue": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.4.1.tgz",
+      "integrity": "sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==",
+      "requires": {}
+    },
     "quad-indices": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/quad-indices/-/quad-indices-2.0.1.tgz",

+ 1 - 0
package.json

@@ -14,6 +14,7 @@
     "ar.js": "^2.2.2",
     "axios": "^1.6.7",
     "pinia": "^2.1.7",
+    "qrcode.vue": "^3.4.1",
     "recorder-core": "^1.3.24040900",
     "swiper": "^11.0.7",
     "vue": "^3.3.11",

BIN
src/assets/img/icon/素材-07.png


BIN
src/assets/img/icon/素材-08.png


BIN
src/assets/img/icon/素材-09.png


BIN
src/assets/img/map/All_1.webp


BIN
src/assets/img/map/All_2.webp


BIN
src/assets/img/map/All_b1.webp


BIN
src/assets/img/map/map_B1.jpg


+ 396 - 100
src/components/Chat.vue

@@ -22,6 +22,8 @@ import "recorder-core/src/engine/mp3";
 import "recorder-core/src/engine/mp3-engine";
 // QR Code
 import { QrcodeStream, QrcodeDropZone, QrcodeCapture } from "vue-qrcode-reader";
+// Qrcode.vue
+import QrcodeVue from "qrcode.vue";
 // Components
 // import Navbar from "../components/Navbar.vue";
 // import TicketPurchase from "../components/TicketPurchase.vue";
@@ -104,6 +106,17 @@ function onDetect(detectedCodes) {
   }
 }
 
+// QR Code 彈跳視窗
+let qrcodeImgDialog = ref(false);
+let qrcodeImgUrl = ref("");
+
+// 開啟掃描視窗
+function openQrcodeDialog(item) {
+  console.log("item", item);
+  qrcodeImgUrl.value = item;
+  qrcodeImgDialog.value = true;
+}
+
 // 判斷 vue-qrcode-reader 是否加載完成
 function onInit(capabilities) {
   qrCodeLoading.value = false;
@@ -176,7 +189,7 @@ async function sendMessage(type = "") {
   qaQuery.push(userMessage.value);
   let message = userMessage.value;
   userMessage.value = "";
-  let url = "https://devbox10.itri-nlp.tw:38125/v1/qa/";
+  let url = "https://api.itri-101-5g.com/v1/qa/";
 
   try {
     // 使用者訊息
@@ -356,6 +369,17 @@ const updateMenuHeight = () => {
   });
 };
 
+// 語言下拉選單 (Kiosk)
+function onLanguageChange(lang) {
+  isVideoPause.value = true;
+
+  if (lang === "中文") {
+    chooseLang("zh-tw");
+  } else if (lang === "English") {
+    chooseLang("en-us");
+  }
+}
+
 function chooseLang(lang) {
   console.log("選擇語言:", lang);
   selectLang.value = lang;
@@ -527,10 +551,14 @@ let serviceList = reactive([
   // },
 ]);
 
+// 定位點
 // 定位點
 let locationList = reactive([
   {
-    location: "B1 鼎泰豐", // 編號 1
+    location: {
+      value: "B1 鼎泰豐", // 編號 1
+      text: "location.b1_din_tai_fung",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -597,27 +625,12 @@ let locationList = reactive([
         text: "items_and_luggage_storage",
       },
     ],
-    // navigation: [
-    //   "景觀餐廳櫃台",
-    //   "ATM",
-    //   "觀景台售票處",
-    //   "鼎泰豐",
-    //   "捷運",
-    //   "超市",
-    //   "美食街",
-    //   "退稅/外幣兌換櫃台",
-    //   "計程車乘車處",
-    //   "無障礙廁所",
-    //   "客服(停車折抵)",
-    //   "哺乳室",
-    //   "飲水機",
-    //   "伴手禮區",
-    //   "廁所",
-    //   "置物櫃",
-    // ],
   },
   {
-    location: "B1 中環", // 編號 2
+    location: {
+      value: "B1 中環", // 編號 2
+      text: "location.b1_centre",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -674,7 +687,10 @@ let locationList = reactive([
     // ],
   },
   {
-    location: "B1 2號電梯", // 編號 3
+    location: {
+      value: "B1 2號電梯", // 編號 3
+      text: "location.b1_elevator_no_2",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -766,7 +782,10 @@ let locationList = reactive([
     // ],
   },
   {
-    location: "B1 4號電梯", // 編號 4
+    location: {
+      value: "B1 4號電梯", // 編號 4
+      text: "location.b1_elevator_no_4",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -853,7 +872,10 @@ let locationList = reactive([
     // ],
   },
   {
-    location: "B1 3號電梯", // 編號 5
+    location: {
+      value: "B1 3號電梯", // 編號 5
+      text: "location.b1_elevator_no_3",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -920,9 +942,30 @@ let locationList = reactive([
         text: "supermarket",
       },
     ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "無障礙廁所",
+    //   "計程車乘車處",
+    //   "客服(退稅櫃台/外幣兌換)",
+    //   "飲水機",
+    //   "哺乳室",
+    //   "置物櫃",
+    //   "客服(停車折抵)",
+    //   "美食街",
+    //   "伴手禮區",
+    //   "鼎泰豐",
+    //   "捷運",
+    //   "超市",
+    // ],
   },
   {
-    location: "1F 信義環", // 編號 6
+    location: {
+      value: "1F 信義環", // 編號 6
+      text: "location.1f_south_xinyi",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -989,9 +1032,30 @@ let locationList = reactive([
         text: "supermarket",
       },
     ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "ATM",
+    //   "置物櫃",
+    //   "飲水機",
+    //   "無障礙廁所",
+    //   "哺乳室",
+    //   "客服(停車折抵)",
+    //   "退稅/外幣兌換櫃台",
+    //   "伴手禮區",
+    //   "美食街",
+    //   "捷運",
+    //   "鼎泰豐",
+    //   "超市",
+    // ],
   },
   {
-    location: "1F 3號電梯", // 編號 7
+    location: {
+      value: "1F 3號電梯", // 編號 7
+      text: "location.1f_elevator_no_3",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -1054,9 +1118,29 @@ let locationList = reactive([
         text: "snackable_souvenirs",
       },
     ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "置物櫃",
+    //   "飲水機",
+    //   "無障礙廁所",
+    //   "哺乳室",
+    //   "客服(停車折抵)",
+    //   "退稅/外幣兌換櫃台",
+    //   "計程車乘車處",
+    //   "美食街",
+    //   "超市",
+    //   "捷運",
+    //   "伴手禮區",
+    // ],
   },
   {
-    location: "2F 3號電梯", // 編號 8
+    location: {
+      value: "2F 3號電梯", // 編號 8
+      text: "location.2f_elevator_no_3",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -1103,9 +1187,25 @@ let locationList = reactive([
         text: "service_counter_parking_discount",
       },
     ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "置物櫃",
+    //   "飲水機",
+    //   "無障礙廁所",
+    //   "哺乳室",
+    //   "計程車乘車處",
+    //   "退稅/外幣兌換櫃台",
+    //   "客服(停車折抵)",
+    // ],
   },
   {
-    location: "89F 觀景台", // 編號 9
+    location: {
+      value: "89F 觀景台", // 編號 9
+      text: "location.89f_observatory",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -1132,9 +1232,20 @@ let locationList = reactive([
         text: "101f_queue_area",
       },
     ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "客服",
+    //   "91F戶外區",
+    //   "廁所",
+    //   "101F排隊處",
+    // ],
   },
   {
-    location: "88F 觀景台", // 編號 10
+    location: {
+      value: "88F 觀景台", // 編號 10
+      text: "location.88f_observatory",
+    },
     navigation: [
       {
         value: "景觀餐廳櫃台",
@@ -1157,23 +1268,28 @@ let locationList = reactive([
         text: "nursing_room",
       },
     ],
+    // navigation: ["景觀餐廳櫃台", "ATM", "廁所", "飲水機", "哺乳室"],
   },
 ]);
 
 // 平面圖
 let mapList = reactive([
   {
-    title: "B1 平面圖",
+    value: "B1 平面圖",
+    text: "b1_floor_plan",
   },
   {
-    title: "1F 平面圖",
+    value: "1F 平面圖",
+    text: "1f_floor_plan",
   },
   {
-    title: "2F 平面圖",
+    value: "2F 平面圖",
+    text: "2f_floor_plan",
   },
 ]);
 
 let assignLocation = ref("當前位置"); // 指定定位點
+let assignLocationText = ref(""); // 指定定位點 (i18n)
 let assignNavigationList = ref(null); // 指定導覽點列表
 
 let arVideo = ref(null);
@@ -1247,18 +1363,21 @@ async function getArviews(route, text, type = "") {
 
 // 取得定位點 & 導覽點
 function changeLocation(item) {
-  assignLocation.value = item;
-
+  console.log("item", item);
   console.log("定位點:", item);
 
-  let assign = locationList.filter((e) => e.location === item);
-  console.log("assign", assign);
+  // 掃描 QR Code
+  // assignLocation.value = item;
+  // let assign = locationList.filter((e) => e.location === item);
+  // console.log("assign", assign);
+  // assignNavigationList.value = assign[0].navigation;
 
-  assignNavigationList.value = assign[0].navigation;
-
-  // assignLocation.value = item.location;
-  // assignNavigationList.value = item.navigation;
+  // 下拉選單
+  assignLocation.value = item.location.value;
+  assignNavigationList.value = item.navigation;
+  assignLocationText.value = item.location.text;
 
+  console.log("assignLocation.value", assignLocation.value);
   console.log("assignNavigationList.value", assignNavigationList.value);
 
   messages.value.push({
@@ -1278,11 +1397,11 @@ function changeLocation(item) {
 function assignMapImg(item) {
   console.log("顯示平面圖 item", item);
   let name;
-  if (item.title === "B1 平面圖") {
+  if (item.value === "B1 平面圖") {
     name = "All_b1";
-  } else if (item.title === "1F 平面圖") {
+  } else if (item.value === "1F 平面圖") {
     name = "All_1";
-  } else if (item.title === "2F 平面圖") {
+  } else if (item.value === "2F 平面圖") {
     name = "All_2";
   }
 
@@ -1401,12 +1520,21 @@ async function selectCategory(value, index) {
       body: observationList,
     });
   } else if (value === "位置導引") {
+    assignLocationText.value = "";
+
     messages.value.push({
       label: "text",
       author: "ai",
-      body: t("qr_code_scan_prompt"),
+      body: t("select_location"),
     });
 
+    // 掃描 QR Code
+    // messages.value.push({
+    //   label: "text",
+    //   author: "ai",
+    //   body: t("qr_code_scan_prompt"),
+    // });
+
     // messages.value.push({
     //   label: "map_img",
     //   author: "ai",
@@ -1772,7 +1900,7 @@ function handleShoppingDialog(value) {
     messages.value.push({
       label: "text",
       author: "ai",
-      body: "請選擇品牌類別",
+      body: t("shopping_discounts_info.brands.choose"),
     });
 
     messages.value.push({
@@ -1784,7 +1912,7 @@ function handleShoppingDialog(value) {
     messages.value.push({
       label: "text",
       author: "user",
-      body: "國際貴賓卡專屬禮遇",
+      body: t("shopping_discounts_info.tourist_card.title"),
     });
 
     messages.value.push({
@@ -2006,8 +2134,9 @@ async function handleAudioToText() {
         messages.value.push({
           label: "text",
           author: "user",
-          body: "語音辨識有誤,請重新錄製。",
+          body: t("speech_error"),
         });
+        videoLoading.value = false;
       }
     }
   } catch (error) {
@@ -2222,21 +2351,30 @@ async function messageNotInCache(question, answer) {
 
 <template>
   <div v-if="isLanguagePage" class="lang-content">
-    <button
-      v-for="(item, index) in langList"
-      :key="index"
-      @click="chooseLang(item.value)"
-      class="main-btn"
-    >
-      {{ item.lang }}
-    </button>
-
-    <!-- <div class="ar-test">
-      <router-link to="/ar-tour">AR 導覽測試</router-link>
-    </div> -->
+    <div class="btn-list">
+      <button
+        v-for="(item, index) in langList"
+        :key="index"
+        @click="chooseLang(item.value)"
+        class="main-btn"
+      >
+        {{ item.lang }}
+      </button>
+    </div>
   </div>
+
   <div v-else class="main-containar">
     <div class="video-content">
+      <!-- 語言選單 -->
+      <div class="lang-select">
+        <v-select
+          label="切換語言"
+          :items="['中文', 'English']"
+          variant="solo"
+          @update:modelValue="onLanguageChange"
+        ></v-select>
+      </div>
+
       <video ref="video" preload playsinline>
         <source :src="videoSrc" type="video/mp4" />
         <!-- <source src="../assets/video/start_1.mp4" type="video/mp4" /> -->
@@ -2304,13 +2442,18 @@ async function messageNotInCache(question, answer) {
           <p v-if="!isRecording" class="mb-3">
             {{ t("tap_to_record") }}
           </p>
-          <p v-else class="mb-3">錄音中:{{ recordTime }} 秒</p>
+          <span v-else>
+            <p class="text-center">
+              {{ t("recording") }}:{{ recordTime }} {{ t("second") }}
+            </p>
+            <p class="text-center mb-3" v-html="t('stop_recording')"></p>
+          </span>
           <!-- 錄音按鈕 -->
           <v-btn
             v-if="!isRecording"
             @click="recStart"
             icon="mdi-circle"
-            size="large"
+            size="x-large"
           >
             <v-icon icon="mdi-circle" color="red" size="large"></v-icon>
           </v-btn>
@@ -2397,7 +2540,7 @@ async function messageNotInCache(question, answer) {
         ref="chatArea"
         class="chat-area"
         :class="{ 'area-open': isRotate, 'hide-menu': hideMenu }"
-        :style="{ paddingBottom: !hideMenu ? menuHeight + 20 + 'px' : '70px' }"
+        :style="{ paddingBottom: !hideMenu ? menuHeight + 20 + 'px' : '90px' }"
       >
         <div v-for="message in messages" class="message-content">
           <p
@@ -2484,6 +2627,79 @@ async function messageNotInCache(question, answer) {
 
           <!-- 位置導引 (定位點) -->
           <div v-else-if="message.label === 'location'">
+            <!-- 定位點 & 平面圖下拉選單 -->
+            <div class="d-flex">
+              <v-menu location="top">
+                <template v-slot:activator="{ props }">
+                  <v-btn class="mt-5 py-2" color="primary" dark v-bind="props">
+                    <p class="me-2">
+                      {{
+                        assignLocationText !== ""
+                          ? t(assignLocationText)
+                          : t("current_location")
+                      }}
+                    </p>
+                    <svg
+                      xmlns="http://www.w3.org/2000/svg"
+                      width="18"
+                      height="18"
+                      fill="currentColor"
+                      class="bi bi-caret-down-fill pt-1"
+                      viewBox="0 0 16 16"
+                    >
+                      <path
+                        d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"
+                      />
+                    </svg>
+                  </v-btn>
+                </template>
+
+                <v-list style="max-height: 155px; overflow-y: auto">
+                  <v-list-item
+                    v-for="(item, index) in locationList"
+                    :key="index"
+                  >
+                    <v-list-item-title @click="changeLocation(item)">
+                      {{ index + 1 }}. {{ t(item.location.text) }}
+                    </v-list-item-title>
+                  </v-list-item>
+                </v-list>
+              </v-menu>
+
+              <v-menu location="top">
+                <template v-slot:activator="{ props }">
+                  <v-btn
+                    class="ms-5 mt-5 py-2"
+                    color="primary"
+                    dark
+                    v-bind="props"
+                  >
+                    <p class="me-2">{{ t("view_floor_plan") }}</p>
+                    <svg
+                      xmlns="http://www.w3.org/2000/svg"
+                      width="18"
+                      height="18"
+                      fill="currentColor"
+                      class="bi bi-caret-down-fill pt-1"
+                      viewBox="0 0 16 16"
+                    >
+                      <path
+                        d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"
+                      />
+                    </svg>
+                  </v-btn>
+                </template>
+
+                <v-list style="max-height: 155px; overflow-y: auto">
+                  <v-list-item v-for="(item, index) in mapList" :key="index">
+                    <v-list-item-title @click="assignMapImg(item)">
+                      {{ t(item.text) }}
+                    </v-list-item-title>
+                  </v-list-item>
+                </v-list>
+              </v-menu>
+            </div>
+
             <!-- <v-select
               @update:modelValue="changeLocation"
               v-model="assignLocation"
@@ -2500,7 +2716,7 @@ async function messageNotInCache(question, answer) {
             ></v-select> -->
 
             <!-- 掃描 QR Code -->
-            <v-btn @click="qrCodeDialog = true" color="primary" class="mt-5">
+            <!-- <v-btn @click="qrCodeDialog = true" color="primary" class="mt-5">
               {{ t("tap_to_start_scan") }}
             </v-btn>
 
@@ -2527,7 +2743,7 @@ async function messageNotInCache(question, answer) {
                   </v-btn>
                 </template>
               </v-card>
-            </v-dialog>
+            </v-dialog> -->
 
             <!-- <div class="d-flex">
               <v-menu location="top">
@@ -2597,14 +2813,14 @@ async function messageNotInCache(question, answer) {
           </div>
 
           <!-- 定位點平面圖 -->
-          <!-- <div v-else-if="message.label === 'map_img'" class="mt-5">
+          <div v-else-if="message.label === 'map_img'" class="mt-5">
             <img
               class="map-img"
               :class="{ 'show-anchor': showAnchor }"
               :src="`../src/assets/img/map/${message.body}.webp`"
               alt=""
             />
-          </div> -->
+          </div>
 
           <!-- 位置導引 (導覽點) -->
           <div v-else-if="message.label === 'navigation'">
@@ -2843,7 +3059,7 @@ async function messageNotInCache(question, answer) {
               :modules="modules"
               class="ticket-slide"
             >
-              <swiper-slide v-for="item in message.body.ticketList">
+              <swiper-slide v-for="(item, index) in message.body.ticketList">
                 <div class="slide-item">
                   <img
                     class="cover-img"
@@ -2857,12 +3073,24 @@ async function messageNotInCache(question, answer) {
                     <div class="price-info">
                       <span>{{ item.info.floor || item.info.price }}</span>
                       <div class="link-btn">
-                        <a
+                        <!-- <a
                           :href="item.info.website_url || item.info.url"
                           target="_blank"
                         >
                           {{ t("ctaGoUrl") }}
-                        </a>
+                        </a> -->
+
+                        <!-- 掃描 QR Code -->
+                        <v-btn
+                          @click="
+                            openQrcodeDialog(
+                              item.info.website_url || item.info.url
+                            )
+                          "
+                          color="primary"
+                        >
+                          <p class="text-white m-0">{{ t("ctaGoUrl") }}</p>
+                        </v-btn>
                       </div>
                     </div>
                     <p class="description">
@@ -2904,7 +3132,7 @@ async function messageNotInCache(question, answer) {
               :modules="modules"
               class="ticket-slide"
             >
-              <swiper-slide v-for="item in message.body.ticketList">
+              <swiper-slide v-for="(item, index) in message.body.ticketList">
                 <div class="slide-item">
                   <img
                     class="cover-img aa"
@@ -2918,9 +3146,17 @@ async function messageNotInCache(question, answer) {
                     <div class="price-info">
                       <span>{{ item.info.floor || item.info.price }}</span>
                       <div class="link-btn">
-                        <a :href="item.info.url" target="_blank">
+                        <!-- <a :href="item.info.url" target="_blank">
                           {{ t("ctaGoUrl") }}
-                        </a>
+                        </a> -->
+
+                        <!-- 掃描 QR Code -->
+                        <v-btn
+                          @click="openQrcodeDialog(item.info.url)"
+                          color="primary"
+                        >
+                          <p class="text-white m-0">{{ t("ctaGoUrl") }}</p>
+                        </v-btn>
                       </div>
                     </div>
                     <p class="description">
@@ -2939,7 +3175,7 @@ async function messageNotInCache(question, answer) {
               :modules="modules"
               class="ticket-slide"
             >
-              <swiper-slide v-for="item in message.body">
+              <swiper-slide v-for="(item, index) in message.body">
                 <div class="slide-item">
                   <img
                     class="cover-img"
@@ -2968,9 +3204,17 @@ async function messageNotInCache(question, answer) {
                     <div class="price-info">
                       <span>{{ item.info.floor || item.info.price }}</span>
                       <div class="link-btn">
-                        <a :href="item.info.url" target="_blank">
+                        <!-- <a :href="item.info.url" target="_blank">
                           {{ t("ctaGoUrl") }}
-                        </a>
+                        </a> -->
+
+                        <!-- 掃描 QR Code -->
+                        <v-btn
+                          @click="openQrcodeDialog(item.info.url)"
+                          color="primary"
+                        >
+                          <p class="text-white m-0">{{ t("ctaGoUrl") }}</p>
+                        </v-btn>
                       </div>
                     </div>
 
@@ -3129,7 +3373,7 @@ async function messageNotInCache(question, answer) {
         </div>
       </div> -->
 
-        <div class="menu-table" :class="{ 'hide-table': hideMenu }">
+        <div class="menu-table mb-3" :class="{ 'hide-table': hideMenu }">
           <table class="mt-3">
             <tbody>
               <tr v-for="(row, rowIndex) in menuList" :key="rowIndex">
@@ -3157,7 +3401,7 @@ async function messageNotInCache(question, answer) {
         </tbody>
       </table> -->
 
-        <div class="d-flex align-center position-relative">
+        <div class="d-flex align-center position-relative" style="bottom: 20px">
           <div class="position-absolute">
             <button
               v-if="!showInput"
@@ -3314,6 +3558,20 @@ async function messageNotInCache(question, answer) {
       </section>
     </div>
   </v-dialog> -->
+
+  <!-- 掃描視窗 -->
+  <v-dialog v-model="qrcodeImgDialog" width="auto">
+    <v-card class="pa-5">
+      <v-card-title class="text-center"> 請掃描 QR Code 前往 </v-card-title>
+      <v-card-text>
+        <qrcode-vue
+          :value="qrcodeImgUrl"
+          class="w-100 h-100"
+          style="max-width: 300px"
+        />
+      </v-card-text>
+    </v-card>
+  </v-dialog>
 </template>
 
 <style lang="scss">
@@ -3334,14 +3592,30 @@ async function messageNotInCache(question, answer) {
 }
 
 .lang-content {
+  width: 100%;
+  height: 100vh;
+  padding: 0 20vw;
   display: flex;
-  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background-color: rgba(0, 0, 0, 0.6);
+  background-blend-mode: multiply;
+  background-image: url("@/assets/img/banner.jpg");
+  background-size: cover;
+  background-position: center center;
+
   .main-btn {
     margin-bottom: 40px;
     &:last-child {
       margin-bottom: 0;
     }
   }
+
+  .btn-list {
+    max-width: 400px;
+    display: flex;
+    flex-direction: column;
+  }
 }
 
 .main-containar {
@@ -3356,8 +3630,8 @@ async function messageNotInCache(question, answer) {
     display: flex;
     justify-content: center;
     transform: scale(1);
-    // background-color: #cab78e;
-    background-color: var(--sub-color);
+    background-color: #cab78e;
+    // background-color: var(--sub-color);
 
     video {
       width: 100%;
@@ -3412,17 +3686,25 @@ async function messageNotInCache(question, answer) {
 
       p {
         color: #fff;
-        font-size: 0.875rem;
+        font-size: 1rem;
         letter-spacing: 1px;
         text-shadow: 1px 1px 2px #333;
       }
     }
   }
 
+  .map-img {
+    max-width: 100%;
+
+    &.show-anchor {
+      max-width: 70%;
+    }
+  }
+
   .chat-content {
     width: 100%;
-    height: 60vh;
-    margin-top: -20px;
+    height: 65vh;
+    margin-top: -1rem;
     position: relative;
     z-index: 100;
     letter-spacing: 1px;
@@ -3443,7 +3725,7 @@ async function messageNotInCache(question, answer) {
         color: white;
 
         @media (max-width: 375px) {
-          font-size: 0.875rem;
+          font-size: 1rem;
         }
       }
 
@@ -3472,7 +3754,7 @@ async function messageNotInCache(question, answer) {
       display: flex;
       flex-direction: column;
       background: var(--sub-color);
-      height: 40vh;
+      height: 45vh;
       padding: 0 1em 2em;
       overflow-x: hidden;
       overflow-y: auto;
@@ -3532,7 +3814,7 @@ async function messageNotInCache(question, answer) {
 
         @media (max-width: 600px) {
           max-width: 80%;
-          font-size: 0.875rem;
+          font-size: 1rem;
         }
 
         &.message-out {
@@ -3647,12 +3929,12 @@ async function messageNotInCache(question, answer) {
         .description {
           line-height: 1.8;
           // text-align: justify;
-          font-size: 0.875rem;
+          font-size: 1rem;
         }
 
         .date {
           color: #646464;
-          font-size: 0.875rem;
+          font-size: 1rem;
           text-align: center;
         }
       }
@@ -3816,18 +4098,24 @@ async function messageNotInCache(question, answer) {
           justify-content: space-between;
 
           @media (max-width: 575px) {
-            font-size: 0.875rem;
+            font-size: 1rem;
             // flex-direction: column;
             // align-items: start;
           }
 
+          button {
+            border-radius: 100px;
+          }
+
           span {
-            display: block;
-            margin: 10px 0;
-            padding-right: 5px;
-            font-size: 1rem;
-            font-weight: 600;
-            color: var(--main-color);
+            &:first-child {
+              display: block;
+              margin: 10px 0;
+              padding-right: 5px;
+              font-size: 1rem;
+              font-weight: 600;
+              color: var(--main-color);
+            }
           }
         }
       }
@@ -3853,7 +4141,7 @@ async function messageNotInCache(question, answer) {
         border: none;
         background-color: var(--main-color);
         letter-spacing: 2px;
-        font-size: 0.875rem;
+        font-size: 1rem;
         transition: all 0.3s;
 
         &:hover {
@@ -3918,7 +4206,7 @@ async function messageNotInCache(question, answer) {
       p {
         padding: 10px 0;
         font-weight: 500;
-        font-size: 0.875em;
+        font-size: 1rem;
         text-align: center;
         border-bottom: 2px solid #fff;
       }
@@ -3931,7 +4219,7 @@ async function messageNotInCache(question, answer) {
           padding-bottom: 5px;
           letter-spacing: 1px;
           font-weight: 500;
-          font-size: 0.875em;
+          font-size: 1rem;
         }
       }
     }
@@ -4026,7 +4314,7 @@ async function messageNotInCache(question, answer) {
 }
 
 .v-expansion-panel-text {
-  font-size: 0.875rem;
+  font-size: 1rem;
   line-height: 1.7;
 }
 
@@ -4120,7 +4408,7 @@ async function messageNotInCache(question, answer) {
 
             @media (max-width: 430px) {
               // width: 115px;
-              font-size: 0.875rem;
+              font-size: 1rem;
             }
           }
         }
@@ -4151,4 +4439,12 @@ async function messageNotInCache(question, answer) {
     }
   }
 }
+
+.lang-select {
+  position: absolute;
+  z-index: 100;
+  bottom: 15px;
+  right: 20px;
+  width: 130px;
+}
 </style>

+ 8 - 0
src/language/en.json

@@ -17,10 +17,18 @@
   "qr_code_scan_prompt": "Please scan QR Code to obtain current location",
   "tap_to_start_scan": "Tap to start scanning",
   "current_location": "Current location",
+  "view_floor_plan": "View floor plan",
   "select_navigation_location": "Please select navigation location",
+  "b1_floor_plan": "B1 floor plan",
+  "1f_floor_plan": "1F floor plan",
+  "2f_floor_plan": "2F floor plan",
   "system_construction": "The system is under construction. <br> The voice input function is disabled.",
   "tap_to_record": "Tap to record",
   "close": "Close",
+  "recording": "Recording",
+  "second": "second",
+  "stop_recording": "After the conversation is complete,<br>please press the stop button to end the recording.",
+  "speech_error": "Speech recognition error. Please re-record.",
   "service_info": {
     "title": "Service information",
     "inquiry_prompt": "What are you searching for?<br>Or please enter your question",

+ 8 - 0
src/language/ja.json

@@ -17,10 +17,18 @@
   "qr_code_scan_prompt": "請掃描 QR Code 取得當前位置",
   "tap_to_start_scan": "點我開啟掃描",
   "current_location": "當前位置",
+  "view_floor_plan": "查看平面圖",
   "select_navigation_location": "請選擇導覽位置",
+  "b1_floor_plan": "B1 平面圖",
+  "1f_floor_plan": "1F 平面圖",
+  "2f_floor_plan": "2F 平面圖",
   "system_construction": "系統建置中,<br />關閉語音輸入功能。",
   "tap_to_record": "點選以進行錄音",
   "close": "關閉",
+  "recording": "錄音中",
+  "second": "秒",
+  "stop_recording": "對話完畢後,請按下停止按鈕結束錄音",
+  "speech_error": "語音辨識有誤,請重新錄製。",
   "service_info": {
     "title": "サービス情報",
     "inquiry_prompt": "サービス情報を選択<br>下のメッセージボックスにご質問を入力してください。",

+ 8 - 0
src/language/ko.json

@@ -17,10 +17,18 @@
   "qr_code_scan_prompt": "請掃描 QR Code 取得當前位置",
   "tap_to_start_scan": "點我開啟掃描",
   "current_location": "當前位置",
+  "view_floor_plan": "查看平面圖",
   "select_navigation_location": "請選擇導覽位置",
+  "b1_floor_plan": "B1 平面圖",
+  "1f_floor_plan": "1F 平面圖",
+  "2f_floor_plan": "2F 平面圖",
   "system_construction": "系統建置中,<br />關閉語音輸入功能。",
   "tap_to_record": "點選以進行錄音",
   "close": "關閉",
+  "recording": "錄音中",
+  "second": "秒",
+  "stop_recording": "對話完畢後,請按下停止按鈕結束錄音",
+  "speech_error": "語音辨識有誤,請重新錄製。",
   "service_info": {
     "title": "服務資訊",
     "inquiry_prompt": "請問您想查詢?",

+ 8 - 0
src/language/zh.json

@@ -17,10 +17,18 @@
   "qr_code_scan_prompt": "請掃描 QR Code 取得當前位置",
   "tap_to_start_scan": "點我開啟掃描",
   "current_location": "當前位置",
+  "view_floor_plan": "查看平面圖",
   "select_navigation_location": "請選擇導覽位置",
+  "b1_floor_plan": "B1 平面圖",
+  "1f_floor_plan": "1F 平面圖",
+  "2f_floor_plan": "2F 平面圖",
   "system_construction": "系統建置中,<br>關閉語音輸入功能。",
   "tap_to_record": "點選以進行錄音",
   "close": "關閉",
+  "recording": "錄音中",
+  "second": "秒",
+  "stop_recording": "對話完畢後,請按下停止按鈕結束錄音",
+  "speech_error": "語音辨識有誤,請重新錄製。",
   "service_info": {
     "title": "服務資訊",
     "inquiry_prompt": "請問您想查詢?<br>或於下方文字框輸入您的問題",