From f0e9d7fda65590ec82b2ae57bea99188138f9527 Mon Sep 17 00:00:00 2001 From: ko1234 Date: Thu, 10 Jul 2025 10:34:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A3=9F=E5=88=A5=E8=A8=AD=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 2 +- .env.staging | 6 +- package.json | 3 +- src/components/navbar/NavbarBuilding.vue | 21 +++--- src/stores/useBuildingStore.js | 40 ++++++----- src/util/request.js | 84 +++++++++++++----------- src/views/login/Login.vue | 3 + 7 files changed, 91 insertions(+), 68 deletions(-) diff --git a/.env.development b/.env.development index 6da8916..8988210 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ -VITE_API_BASEURL = "https://ibms-cvilux-api.production.mjmtech.com.tw" +VITE_API_BASEURL = "https://ibms-cvilux-demo-api.production.mjmtech.com.tw" VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088" VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net" VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist" \ No newline at end of file diff --git a/.env.staging b/.env.staging index 3a51cfe..3e57378 100644 --- a/.env.staging +++ b/.env.staging @@ -1,3 +1,3 @@ -VITE_API_BASEURL = "http://220.132.206.5:8008" -VITE_FILE_API_BASEURL = "http://220.132.206.5:8085/file" -VITE_FORGE_BASEURL = "http://localhost:5173" \ No newline at end of file +VITE_API_BASEURL = "https://ibms-cvilux-demo-api.production.mjmtech.com.tw" +VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088" +VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net" \ No newline at end of file diff --git a/package.json b/package.json index abef3dc..7f18668 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "build:staging": "vite build --mode staging" }, "dependencies": { "@ant-design/icons-vue": "^7.0.1", diff --git a/src/components/navbar/NavbarBuilding.vue b/src/components/navbar/NavbarBuilding.vue index 7270901..e6198b1 100644 --- a/src/components/navbar/NavbarBuilding.vue +++ b/src/components/navbar/NavbarBuilding.vue @@ -6,7 +6,6 @@ const store = useBuildingStore(); const selectBuilding = (bui) => { store.selectedBuilding = bui; // 改變 selectedBuilding,watch 會自動更新資料 - localStorage.setItem("CviBuilding", JSON.stringify(bui)); }; onMounted(() => { @@ -28,16 +27,18 @@ onMounted(() => { tabindex="0" class="dropdown-content w-48 left-8 translate-y-2 z-[1] menu py-3 shadow rounded bg-[#4c625e] border text-center" > -
  • - {{ bui.full_name }} -
  • + - \ No newline at end of file + diff --git a/src/stores/useBuildingStore.js b/src/stores/useBuildingStore.js index 94d2787..a396bda 100644 --- a/src/stores/useBuildingStore.js +++ b/src/stores/useBuildingStore.js @@ -45,38 +45,46 @@ const useBuildingStore = defineStore("buildingInfo", () => { // 獲取所有建築物 const fetchBuildings = async () => { - const res = await getBuildings(); - buildings.value = res.data; - if (res.data.length > 0 && !selectedBuilding.value) { - const storedBuilding = JSON.parse(localStorage.getItem("CviBuilding")); - selectedBuilding.value = storedBuilding || res.data[0]; // 預設選第一個建築 + // const res = await getBuildings(); + buildings.value = JSON.parse(localStorage.getItem("CviBuildingList")) || []; + const storedBuilding = JSON.parse(localStorage.getItem("CviBuilding")); + if (buildings.value.length > 0) { + selectedBuilding.value = storedBuilding || buildings.value[0]; // 預設選第一個建築 + } else { + selectedBuilding.value = null; // 如果沒有建築物,清空選擇 } }; // 獲取樓層資料 const fetchFloorList = async (building_guid) => { const res = await getAssetFloorList(building_guid); - floorList.value = res.data[0]?.floors.map((d) => ({ - ...d, - title: d.full_name, - key: d.floor_guid, - })) || []; + floorList.value = + res.data[0]?.floors.map((d) => ({ + ...d, + title: d.full_name, + key: d.floor_guid, + })) || []; }; // 獲取部門資料 const fetchDepartmentList = async () => { const res = await getDepartmentList(); - deptList.value = res.data.map((d) => ({ - ...d, - title: d.name, - key: d.id, - })) || []; + deptList.value = + res.data.map((d) => ({ + ...d, + title: d.name, + key: d.id, + })) || []; }; // 當 selectedBuilding 改變時,更新 floorList 和 deptList watch(selectedBuilding, async (newBuilding) => { if (newBuilding) { - await Promise.all([fetchFloorList(newBuilding.building_guid), fetchDepartmentList()]); + localStorage.setItem("CviBuilding", JSON.stringify(newBuilding)) + await Promise.all([ + fetchFloorList(newBuilding.building_guid), + fetchDepartmentList(), + ]); } }); diff --git a/src/util/request.js b/src/util/request.js index 9c3a0e6..b7cd089 100644 --- a/src/util/request.js +++ b/src/util/request.js @@ -2,38 +2,59 @@ import useGetCookie from "@/hooks/useGetCookie"; import axios from "axios"; const BASEURL = import.meta.env.VITE_API_BASEURL; +// --- 請求攔截器的共用邏輯 --- +const requestInterceptor = (config) => { + // 確保 headers 物件存在 + if (!config.headers) { + config.headers = {}; + } + + // 1. 取得並附加最新的 Token + const token = useGetCookie("JWT-Authorization"); + if (token) { + // 正確做法:修改屬性,而不是覆蓋整個物件 + config.headers.Authorization = `Bearer ${token}`; + } + + // 2. 取得並附加選定的建築物 GUID + const storedBuilding = localStorage.getItem("CviBuilding"); + if (storedBuilding) { + try { + const buildingObject = JSON.parse(storedBuilding); + if (buildingObject && buildingObject.building_guid) { + // 與後端約定好要用哪個標頭,這裡使用 'X-Building-GUID' 作為範例 + config.headers["X-Building-GUID"] = buildingObject.building_guid; + } + } catch (e) { + console.error("解析 localStorage 中的 CviBuilding 失敗:", e); + } + } + return config; +}; + +const requestErrorInterceptor = (error) => { + return Promise.reject(error); +}; + + +// --- 一般 API 實例 --- const instance = axios.create({ baseURL: BASEURL, - timeout: -1, - headers: { Authorization: `Bearer ${useGetCookie("JWT-Authorization")}` }, + timeout: 10000, // 建議設定超時 + // 移除靜態 headers }); -// Add a request interceptor -instance.interceptors.request.use( - function (config) { - // Do something before request is sent - const token = useGetCookie("JWT-Authorization"); - config.headers = { - Authorization: `Bearer ${token}`, - }; - return config; - }, - function (error) { - // Do something with request error - return Promise.reject(error); - } -); +// 使用共用的攔截器 +instance.interceptors.request.use(requestInterceptor, requestErrorInterceptor); // Add a response interceptor instance.interceptors.response.use( function (response) { // Any status code that lie within the range of 2xx cause this function to trigger // Do something with response data - const { status, data, headers } = response; + const { data } = response; - return { - ...data, - }; + return { ...data }; }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger @@ -45,27 +66,16 @@ instance.interceptors.response.use( } ); + +// --- 檔案處理 API 實例 --- export const fileInstance = axios.create({ baseURL: BASEURL, timeout: -1, - headers: { Authorization: `Bearer ${useGetCookie("JWT-Authorization")}` }, + // 移除靜態 headers }); -// Add a request interceptor -fileInstance.interceptors.request.use( - function (config) { - // Do something before request is sent - const token = useGetCookie("JWT-Authorization"); - config.headers = { - Authorization: `Bearer ${token}`, - }; - return config; - }, - function (error) { - // Do something with request error - return Promise.reject(error); - } -); +// 使用共用的攔截器 +fileInstance.interceptors.request.use(requestInterceptor, requestErrorInterceptor); // Add a response interceptor fileInstance.interceptors.response.use( diff --git a/src/views/login/Login.vue b/src/views/login/Login.vue index 8de3f15..0f1260d 100644 --- a/src/views/login/Login.vue +++ b/src/views/login/Login.vue @@ -6,10 +6,12 @@ import * as yup from "yup"; import useFormErrorMessage from "@/hooks/useFormErrorMessage"; import { useRouter } from "vue-router"; import useUserInfoStore from "@/stores/useUserInfoStore"; +import useBuildingStore from "@/stores/useBuildingStore"; import { useI18n } from "vue-i18n"; const { t } = useI18n(); const { openToast } = inject("app_toast"); const store = useUserInfoStore(); +const storeBuild = useBuildingStore(); const router = useRouter(); let schema = yup.object({ @@ -35,6 +37,7 @@ const doLogin = async () => { const res = await Login(value); if (res.isSuccess) { store.user = res.data; + localStorage.setItem("CviBuildingList", JSON.stringify(res.data.buildingIdList)); router.replace({ path: "/dashboard" }); } else { openToast("error", res.msg);