SyuanYu преди 8 месеца
родител
ревизия
58ef907a1c

+ 1 - 0
src/assets/base.css

@@ -17,6 +17,7 @@
 a,
 p,
 li,
+input,
 button {
   font-family: "Noto Sans TC", sans-serif !important;
 }

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


+ 3 - 3
src/components/Chat.vue

@@ -1242,7 +1242,7 @@ let locationList = reactive([
       },
       {
         value: "101F排隊處",
-        text: "101f_queue_area",
+        text: "101f_queue",
       },
     ],
     // navigation: [
@@ -3238,7 +3238,7 @@ let dialog = ref(false);
           <input
             v-model="userMessage"
             type="text"
-            placeholder="Type a message..."
+            :placeholder="t('type_message')"
           />
           <button type="submit" class="submit">
             <img width="20" src="../assets/img/paper-plane-solid.svg" alt="" />
@@ -3405,7 +3405,7 @@ let dialog = ref(false);
             <input
               v-model="userMessage"
               type="text"
-              placeholder="Type a message..."
+              :placeholder="t('type_message')"
             />
             <button type="submit" class="submit">
               <img

+ 0 - 1
src/components/Navbar.vue

@@ -3,7 +3,6 @@ import { useI18n } from "vue-i18n";
 
 const { t, locale } = useI18n();
 const currentLanguage = locale.value;
-console.log("currentLanguage", currentLanguage);
 </script>
 
 <template>

+ 3 - 0
src/language/en.json

@@ -11,6 +11,8 @@
   "food_souvenirs": "Food & Souvenirs",
   "shopping_discounts": "Shopping and Discounts",
   "service_information": "Service Information",
+  "what_around": "What is around?",
+  "type_message": "Type a message ...",
   "ar_tour": "AR navigation",
   "select_location": "Please select you location for AR navigation",
   "question": "Click me to ask a question",
@@ -121,6 +123,7 @@
     "all_brands": "All Brands"
   },
   "external": {
+    "all": "All",
     "gifts": "Gifts",
     "dining": "Dining",
     "accommodation": "Accommodation",

+ 3 - 0
src/language/ja.json

@@ -11,6 +11,8 @@
   "food_souvenirs": "グルメ/お土産",
   "shopping_discounts": "ショッピングと特典",
   "service_information": "サービス情報",
+  "what_around": "附近有什麼?",
+  "type_message": "請輸入訊息",
   "ar_tour": "ARナビを利用",
   "select_location": "あなたの現在地を選んで、ARナビを利用します。",
   "question": "質問するにはここをクリック",
@@ -121,6 +123,7 @@
     "all_brands": "全館品牌"
   },
   "external": {
+    "all": "全部",
     "gifts": "伴手禮",
     "dining": "餐飲",
     "accommodation": "住宿",

+ 3 - 0
src/language/ko.json

@@ -11,6 +11,8 @@
   "food_souvenirs": "맛집/기념품",
   "shopping_discounts": "쇼핑 및 혜택",
   "service_information": "서비스 정보",
+  "what_around": "附近有什麼?",
+  "type_message": "請輸入訊息",
   "ar_tour": "進行 AR 導覽",
   "select_location": "請選擇您的位置進行 AR 導覽",
   "question": "질문하려면 클릭하세요",
@@ -121,6 +123,7 @@
     "all_brands": "全館品牌"
   },
   "external": {
+    "all": "全部",
     "gifts": "伴手禮",
     "dining": "餐飲",
     "accommodation": "住宿",

+ 3 - 0
src/language/zh.json

@@ -11,6 +11,8 @@
   "food_souvenirs": "美食/伴手禮",
   "shopping_discounts": "購物及優惠",
   "service_information": "服務資訊",
+  "what_around": "附近有什麼?",
+  "type_message": "請輸入訊息 ...",
   "ar_tour": "進行 AR 導覽",
   "select_location": "請選擇您的位置進行 AR 導覽",
   "question": "點我問問題",
@@ -121,6 +123,7 @@
     "all_brands": "全館品牌"
   },
   "external": {
+    "all": "全部",
     "gifts": "伴手禮",
     "dining": "餐飲",
     "accommodation": "住宿",

+ 42 - 8
src/views/BrandSearch.vue

@@ -17,13 +17,17 @@ let totalPages = ref(1); // 總頁數
 
 let assignBrand = ref("");
 let searchKeyword = ref("");
+let noResults = ref(false);
 
 // 按鈕篩選
 function filterBrand(val) {
-  console.log("searchKeyword", searchKeyword);
-  pageNum.value = 1;
-  assignBrand.value = val;
+  if (val !== "全部") {
+    assignBrand.value = val;
+  } else {
+    assignBrand.value = "";
+  }
   getBrand();
+  pageNum.value = 1;
 }
 
 watch(pageNum, () => {
@@ -58,6 +62,14 @@ async function getBrand() {
     totalPages.value = getTotalPages(response.data.all_num, pageAmount.value); // 計算頁數
     loading.value = false;
     console.log("館外品牌", response.data.data);
+
+    if (!response.data.data.length) {
+      noResults.value = true;
+    } else {
+      noResults.value = false;
+    }
+
+    console.log("noResults.value", noResults.value);
   } catch (error) {
     console.error(error);
   }
@@ -80,6 +92,10 @@ const truncateText = (text, maxLength) => {
 };
 
 let brandList = reactive([
+  {
+    value: "全部",
+    text: "external.all",
+  },
   {
     value: "伴手禮",
     text: "external.gifts",
@@ -109,6 +125,11 @@ let brandList = reactive([
     text: "external.pet_supplies",
   },
 ]);
+
+function handleSearch() {
+  console.log("Searching for:");
+  searchKeyword.value;
+}
 </script>
 <template>
   <Navbar />
@@ -122,7 +143,8 @@ let brandList = reactive([
       variant="outlined"
       hide-details
       single-line
-      @click:append-inner="onClick"
+      @click:append-inner="getBrand"
+      @keydown.enter="getBrand"
       class="border-sm rounded"
     ></v-text-field>
 
@@ -133,7 +155,11 @@ let brandList = reactive([
         :key="index"
         class="pa-2"
       >
-        <button @click="filterBrand(item.value)" class="rounded brand-btn">
+        <button
+          @click="filterBrand(item.value)"
+          :class="{ active: item.value === assignBrand }"
+          class="rounded brand-btn"
+        >
           {{ t(item.text) }}
         </button>
       </v-col>
@@ -143,7 +169,7 @@ let brandList = reactive([
       <v-progress-circular color="primary" indeterminate></v-progress-circular>
     </div>
 
-    <v-row v-else class="content mt-0">
+    <v-row v-else-if="!loading && !noResults" class="content mt-0">
       <v-col
         cols="12"
         class="card"
@@ -167,8 +193,12 @@ let brandList = reactive([
       </v-col>
     </v-row>
 
+    <p v-else-if="noResults" class="text-center mt-10">
+      找不到符合的資料,請重新搜尋。
+    </p>
+
     <v-pagination
-      v-if="!loading"
+      v-if="!loading && !noResults"
       color="primary"
       v-model="pageNum"
       class="my-10"
@@ -188,9 +218,13 @@ let brandList = reactive([
     color: var(--main-color);
     background-color: #f5f2eb;
     letter-spacing: 1px;
-    transition: all .3s;
+    transition: all 0.3s;
 
     &:hover {
+      opacity: 0.8;
+    }
+
+    &.active {
       color: #fff;
       background-color: var(--main-color);
     }

+ 41 - 85
src/views/HomeView.vue

@@ -50,8 +50,6 @@ let location = ref(""); // 當前位置
 
 // QR Code 掃描
 function onDetect(detectedCodes) {
-  console.log("detectedCodes", detectedCodes);
-
   const url = new URL(detectedCodes[0].rawValue);
   location.value = url.searchParams.get("location");
 
@@ -125,7 +123,6 @@ function onInit(capabilities) {
 onMounted(() => {
   // 取得當前路由參數
   routeParam.value = route.path.replace("/", "");
-  console.log("網址參數", routeParam.value);
   window.addEventListener("resize", handleResize);
 });
 
@@ -136,7 +133,6 @@ let modules = [Navigation, Autoplay]; // Swiper
 let messages = ref([]);
 
 watch(messages.value, (val) => {
-  console.log("messages", val);
   scrollToBottom();
   updateMenuHeight();
 });
@@ -168,7 +164,6 @@ const scrollToBottom = () => {
 
 // 對話選項(按鈕)
 function setBtnValue(val) {
-  console.log("value", val);
   userMessage.value = val;
   sendMessage("text");
 }
@@ -179,7 +174,6 @@ async function sendMessage(type = "") {
     return;
   }
 
-  console.log("sendMessage", userMessage.value);
   qaQuery.push(userMessage.value);
 
   let url = "https://api.itri-101-5g.com/v1/qa/";
@@ -224,8 +218,6 @@ async function sendMessage(type = "") {
           body: response.data.response,
         });
 
-        console.log("response", response);
-
         if (type !== "text") {
           handleTTS(response.data.response); // 取得語音回覆
         }
@@ -239,15 +231,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";
             } else if (
@@ -256,13 +245,9 @@ async function sendMessage(type = "") {
             ) {
               contactFormType.value = item.type;
               labelContent = "contact-form";
-              console.log("contactFormType", contactFormType.value);
             }
           });
 
-          console.log("info", info);
-          console.log("labelContent", labelContent);
-
           setTimeout(() => {
             if (labelContent !== "contact-form") {
               messages.value.push({
@@ -283,8 +268,6 @@ async function sendMessage(type = "") {
       .catch((error) => {
         console.error("Error:", error);
       });
-
-    console.log("messages.value", messages.value);
   } catch (error) {
     console.log("error", error);
   }
@@ -391,15 +374,11 @@ let showAd = ref(false);
 // 取得廣告
 async function setAd() {
   let lang = getLang();
-  console.log("lang", lang);
   let url = `https://cmm.ai:9101/ad?language=${lang}`;
 
   try {
     const response = await axios.get(url);
-    console.log("Ad response", response);
-
     ad.value = response.data.data.body;
-    console.log("Ad", ad.value);
   } catch (error) {
     console.log("error", error);
   }
@@ -442,7 +421,6 @@ const updateMenuHeight = () => {
 };
 
 function chooseLang(lang) {
-  console.log("選擇語言:", lang);
   selectLang.value = lang;
   isLanguagePage.value = false;
   locale.value = lang; // i18n locale
@@ -462,7 +440,6 @@ function chooseLang(lang) {
 
   // 判斷語言修改 title
   const language = localStorage.getItem("lang") || "zh-tw";
-  console.log("language", language);
   if (language === "zh-tw") {
     document.title = "New 台北101 AI智能客服";
   } else if (language === "en-us") {
@@ -471,8 +448,6 @@ function chooseLang(lang) {
 
   setAd();
   handleClick();
-
-  console.log(" messages.value", messages.value);
   setTimeout(() => {
     showAd.value = true;
     // videoPlay();
@@ -1316,11 +1291,29 @@ let mapList = reactive([
   },
 ]);
 
+// 判斷當前時間
+function isTimeRange(startHour, endHour) {
+  const now = new Date();
+  const currentHour = now.getHours();
+  console.log("目前時間:", currentHour, "點");
+  return currentHour >= startHour && currentHour < endHour;
+}
+
+const startHour = 8; // 上午 8 點
+const endHour = 20; // 晚上 8 點
+const isInTimeRange = isTimeRange(startHour, endHour);
+console.log("是否顯示實境景色", isInTimeRange);
+
+// 上午顯示實境景色按鈕(夜間隱藏)
+if (isInTimeRange) {
+  observationList.push({
+    value: "實境景色",
+    text: "observatory_info.view",
+  });
+}
+
 let assignLocation = ref("當前位置"); // 指定定位點
 let assignNavigationList = ref(null); // 指定導覽點列表
-// let assignLocationFloor = ref(null); // 指定定位點樓層
-// let assignNavigation = ref(null); // 指定導覽點
-// let navigationBtn = reactive(["B1", "1F", "2F", "4F", "5F", "89F", "91F"]);
 
 let arVideo = ref(null);
 let arVideoDialog = ref(false);
@@ -1329,10 +1322,8 @@ let arVideoDialog = ref(false);
 async function getArviews(route, text, type = "") {
   let url;
   let lang = getLang();
-  console.log("text >>>", text);
 
   if (type === "garden") {
-    console.log("route", route);
     let start;
     // if (route === "B1 鼎泰豐旁 數位屏幕") {
     //   start = "B1 鼎泰豐";
@@ -1374,8 +1365,6 @@ async function getArviews(route, text, type = "") {
     url = `https://cmm.ai:9101/arviews?language=${lang}&start=${assignLocation.value}&end=${route}`;
   }
 
-  console.log("url", url);
-
   messages.value.push({
     label: "text",
     author: "ai",
@@ -1384,7 +1373,6 @@ async function getArviews(route, text, type = "") {
 
   try {
     const response = await axios.get(url);
-    console.log("AR 導覽影片", response);
 
     messages.value.push({
       label: "ar_views",
@@ -1399,19 +1387,12 @@ async function getArviews(route, text, type = "") {
 // 取得定位點 & 導覽點
 function changeLocation(item) {
   assignLocation.value = item;
-
-  console.log("定位點:", item);
-
   let assign = locationList.filter((e) => e.location === item);
-  console.log("assign", assign);
-
   assignNavigationList.value = assign[0].navigation;
 
   // assignLocation.value = item.location;
   // assignNavigationList.value = item.navigation;
 
-  console.log("assignNavigationList.value", assignNavigationList.value);
-
   messages.value.push({
     label: "text",
     author: "ai",
@@ -1427,7 +1408,6 @@ function changeLocation(item) {
 
 // 顯示平面圖
 function assignMapImg(item) {
-  console.log("顯示平面圖 item", item);
   let name;
   if (item.title === "B1 平面圖") {
     name = "All_b1";
@@ -1486,8 +1466,6 @@ let videoIndex = ref(null); // 影片編號
 async function selectCategory(value, index) {
   assignCategory.value = value;
   assignCategoryIndex.value = index;
-  console.log("selectCategory index", assignCategoryIndex.value);
-  console.log("selectCategory value", assignCategory.value);
 
   if (value === "叫出真人客服") {
     showAnchor.value = true;
@@ -1546,7 +1524,9 @@ async function selectCategory(value, index) {
   //   menuList[0][0].text = "customer_show";
   //   menuList[0][0].value = "叫出真人客服";
   // }
-  else if (value === "秘境花園觀景台") {
+  else if (value === "附近有什麼") {
+    window.open("https://cmm.ai/101-aiv1/#/brand-search", "_blank"); // 另開頁面
+  } else if (value === "秘境花園觀景台") {
     messages.value.push({
       label: "text",
       author: "ai",
@@ -1623,7 +1603,6 @@ async function getAd(type) {
 
   try {
     const response = await axios.get(url);
-    console.log("response", response);
     secondaryAd.value = response.data.data;
     // secondaryAdShow.value = true; // 取消蓋版廣告
 
@@ -1681,7 +1660,6 @@ async function handleClick() {
   let url = "https://cmm.ai:9101/click";
   try {
     const response = await axios.get(url);
-    console.log("Click", response);
   } catch (error) {
     console.log("error", error);
   }
@@ -1757,11 +1735,12 @@ let videoContent = ref(null); // 主播影片區塊
 
 const menuList = reactive([
   [
-    {
-      imgSrc: "素材-05.png",
-      text: "customer_show",
-      value: "叫出真人客服",
-    },
+    // {
+    //   imgSrc: "素材-05.png",
+    //   text: "customer_show",
+    //   value: "叫出真人客服",
+    // },
+    { imgSrc: "素材-11.png", text: "what_around", value: "附近有什麼" },
     { imgSrc: "素材-06.png", text: "service_information", value: "服務資訊" },
     { imgSrc: "素材-07.png", text: "shopping_discounts", value: "購物及優惠" },
   ],
@@ -1778,8 +1757,6 @@ const menuList = reactive([
 
 // 美食伴手禮 or 購物及優惠類別篩選
 async function findBrand(value) {
-  console.log("findBrand", value);
-
   if (value === "館外店家") {
     value = "館外";
   }
@@ -1789,7 +1766,6 @@ async function findBrand(value) {
 
   try {
     const response = await axios.get(url);
-    console.log("response", response);
 
     // response.data.data.map(
     //   (item) => (item.info.tags = JSON.parse(item.info.tags))
@@ -1837,10 +1813,8 @@ async function getStaticTickets(type) {
 
   try {
     const response = await axios.get(url);
-    console.log("線上購票", response.data.result);
 
     response.data.result.map((item) => info.ticketList.push(item));
-    console.log("info", info);
 
     messages.value.push({
       label: "ticket",
@@ -1854,8 +1828,6 @@ async function getStaticTickets(type) {
 
 // 秘境花園觀景台對話
 function handleObservationDialog(value) {
-  console.log("value", value);
-
   if (value === "線上購票") {
     messages.value.push({
       label: "text",
@@ -1972,8 +1944,6 @@ let gardenRouteList = reactive([
 
 // 購物及優惠對話
 function handleShoppingDialog(value) {
-  console.log("value", value);
-
   if (value === "購物品牌查詢") {
     messages.value.push({
       label: "text",
@@ -2052,10 +2022,8 @@ let audioDuration = ref(null); // 音訊秒數
 
 // 文字轉語音 (TTS)
 async function handleTTS(message) {
-  console.log("handleTTS", message);
   let audioLang; // 音訊語言
   let lang = localStorage.getItem("lang");
-  console.log("lang", lang);
 
   switch (lang) {
     case "zh-tw":
@@ -2082,11 +2050,9 @@ async function handleTTS(message) {
 
   try {
     const response = await axios.post(url, formData, { responseType: "blob" });
-    console.log("TTS response", response);
 
     const blob = new Blob([response.data], { type: "audio/mp3" });
     const audioUrl = URL.createObjectURL(blob);
-    console.log("audioUrl", audioUrl);
     cutVideo();
 
     // 取得 mp3 音訊秒數
@@ -2166,12 +2132,10 @@ const onAudioEnded = () => {
 // 判斷音訊是否為播放狀態
 const onAudioPlay = () => {
   isAudioPlaying.value = true;
-  console.log("isAudioPlaying.value", isAudioPlaying.value);
 };
 
 const onAudioPause = () => {
   isAudioPlaying.value = false;
-  console.log("isAudioPlaying.value", isAudioPlaying.value);
 };
 
 const audioURL = ref(null);
@@ -2186,7 +2150,6 @@ async function handleAudioToText() {
   isRecording.value = false;
   let audioLang; // 音訊語言
   let lang = localStorage.getItem("lang");
-  console.log("lang", lang);
 
   switch (lang) {
     case "zh-tw":
@@ -2213,9 +2176,7 @@ async function handleAudioToText() {
   formData.append("file", audioFile.value);
 
   try {
-    console.log("audioFile.value", audioFile.value);
     const response = await axios.post(url, formData);
-    console.log("語音轉文字 response", response);
     // showAnchor.value = false; // 關閉主播視窗
 
     userMessage.value = response.data[0];
@@ -2300,7 +2261,6 @@ function recStop() {
     function (blob, duration) {
       // 利用 URL 產生本地檔案位址,不用時需要 revokeObjectURL
       let localUrl = (window.URL || webkitURL).createObjectURL(blob); // 該 url 只能本地端使用 (例如給 audio.src 進行播放,或是給 a.href download 進行下載)
-      console.log(blob, localUrl, "時長:" + duration + "ms");
       // rec.close(); // 釋放錄音資源 (若不釋放系統或瀏覽器將持續提示在錄音中)
       // rec = null;
 
@@ -2309,8 +2269,6 @@ function recStop() {
         type: "audio/mp3",
       });
 
-      console.log("audioFile", audioFile.value);
-
       // 自動播放(測試用)
       // const audio = document.createElement("audio");
       // audio.src = localUrl;
@@ -2408,13 +2366,13 @@ let contactForm = ref(null);
 let contactDialog = ref(false);
 
 const contactFormData = ref({
-  type: null,
-  title: "",
-  content: "",
-  client_name: "",
-  gender: "",
-  email: "",
-  phone: "",
+  type: null, // 問題類別
+  title: "", // 標題
+  content: "", // 內容
+  client_name: "", // 姓名
+  gender: "", // 性別
+  email: "", // 電子郵件
+  phone: "", // 聯絡電話
 });
 
 watch(contactDialog, (val) => {
@@ -2439,17 +2397,14 @@ const emailRule = (value) =>
 
 // 寄送表單
 const submitContactForm = async () => {
-  console.log("contactForm.value", contactForm.value);
   // 驗證表單
   const validationResult = await contactForm.value.validate();
-  console.log("validationResult", validationResult.valid);
 
   if (validationResult.valid) {
     let url = "https://cmm.ai:9101/insert_table";
 
     try {
       const response = await axios.post(url, contactFormData.value);
-      console.log("表單 response", response);
 
       if (response.data.state === 200) {
         // alert(response.data.message);
@@ -3480,7 +3435,7 @@ const submitContactForm = async () => {
           <input
             v-model="userMessage"
             type="text"
-            placeholder="Type a message..."
+            :placeholder="t('type_message')"
           />
           <button type="submit" class="submit">
             <img width="20" src="../assets/img/paper-plane-solid.svg" alt="" />
@@ -3647,7 +3602,7 @@ const submitContactForm = async () => {
             <input
               v-model="userMessage"
               type="text"
-              placeholder="Type a message..."
+              :placeholder="t('type_message')"
             />
             <button type="submit" class="submit">
               <img
@@ -4894,8 +4849,9 @@ const submitContactForm = async () => {
 
   ::placeholder {
     font-size: 1rem;
-    font-weight: 500;
+    font-weight: 400;
     color: var(--sub-color);
+    letter-spacing: 1px;
     opacity: 1; /* Firefox */
   }
 

+ 2 - 1
src/views/RtspStream.vue

@@ -9,7 +9,8 @@ onMounted(() => {
     techOrder: ["html5"],
     sources: [
       {
-        src: "https://cmm.ai:9101/stream.m3u8", // 替換成 HTTP 服務器地址
+        // src: "https://cmm.ai:9101/stream.m3u8", // 替換成 HTTP 服務器地址
+        src: "http://db.ptt.cx:28888/stream1/index.m3u8", // 替換成 HTTP 服務器地址
         type: "application/x-mpegURL",
       },
     ],