From b55cc0a6d65a972d82e5919607ea25b2dad36243 Mon Sep 17 00:00:00 2001 From: Cindy Chang Date: Mon, 24 Jun 2024 09:07:11 +0800 Subject: [PATCH] vue3 infinite scroll --- src/i18n/en.json | 3 +- src/stores/acctMgmt.js | 22 +++++++- .../AccountManagement/AccountAdmin/index.vue | 56 +++++++++++-------- .../AccountManagement/ModalAccountEdit.vue | 55 +++++++++++------- 4 files changed, 90 insertions(+), 46 deletions(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index 9dd109b..e39e399 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -34,7 +34,8 @@ "ActivateNow": "Activate now.", "DelteQuestion": "Are you sure to delete ?", "DeleteFirstClause": "deletion is irreversible !", - "DeleteSecondClause": "You will delete all data and content on this account." + "DeleteSecondClause": "You will delete all data and content on this account.", + "MsgAccountAdded": "Account added." }, "Compare": { "timeUsage": "Time Usage", diff --git a/src/stores/acctMgmt.js b/src/stores/acctMgmt.js index 7e87799..0ac69e7 100644 --- a/src/stores/acctMgmt.js +++ b/src/stores/acctMgmt.js @@ -5,7 +5,9 @@ export default defineStore('acctMgmtStore', { state: () => ({ allUserAccoutList: [], isAcctMenuOpen: false, - currentViewingUser: {} + currentViewingUser: { + detail: null, + } }), getters: { }, @@ -85,10 +87,24 @@ export default defineStore('acctMgmtStore', { try{ const response = await this.$axios.post(apiCreateAccount, userToCreate); - console.log('TODO: response:', response, response.status); } catch(error) { - apiError(error, 'Failed to add a new account'); + apiError(error, 'Failed to add a new account.'); + }; + }, + /** + * Get user detail by unique username. + * @param {string} uniqueUsername + */ + async getUserDetail(uniqueUsername) { + const apiUserDetail = `/api/users/${uniqueUsername}`; + + try{ + const response = await this.$axios.get(apiUserDetail); + this.currentViewingUser.detail = response.data; + } + catch(error) { + apiError(error, 'Failed to get user detail.'); }; }, /** diff --git a/src/views/AccountManagement/AccountAdmin/index.vue b/src/views/AccountManagement/AccountAdmin/index.vue index e47461b..178df5a 100644 --- a/src/views/AccountManagement/AccountAdmin/index.vue +++ b/src/views/AccountManagement/AccountAdmin/index.vue @@ -9,7 +9,7 @@
- @@ -149,7 +149,8 @@ export default { const modalStore = useModalStore(); const { isLoading } = storeToRefs(loadingStore); const loginStore = piniaLoginStore(); - const internalInfiniteAcctData = ref([]); + const infiniteAcctData = ref([]); + const infiniteStart = ref(0); const loginUserData = ref(null); const allUserAccoutList = computed(() => acctMgmtStore.allUserAccoutList); @@ -161,13 +162,13 @@ export default { const moveCurrentLoginUserToFirstRow = () => { const currentLoginUsername = loginUserData.value.username; - if(internalInfiniteAcctData.value && internalInfiniteAcctData.value.length){ - const index = internalInfiniteAcctData.value.findIndex(user => user.username === currentLoginUsername); + if(infiniteAcctData.value && infiniteAcctData.value.length){ + const index = infiniteAcctData.value.findIndex(user => user.username === currentLoginUsername); if (index !== -1) { // 移除匹配的對象(現正登入的使用者)並將其插入到陣列的第一位" - const [user] = internalInfiniteAcctData.value.splice(index, 1); - internalInfiniteAcctData.value.unshift(user); + const [user] = infiniteAcctData.value.splice(index, 1); + infiniteAcctData.value.unshift(user); } } }; @@ -179,7 +180,7 @@ export default { const getFirstPageUserData = async() => { await acctMgmtStore.getAllUserAccounts(); - internalInfiniteAcctData.value = allUserAccoutList.value.slice(0, ONCE_RENDER_NUM_OF_DATA) + infiniteAcctData.value = allUserAccoutList.value.slice(0, ONCE_RENDER_NUM_OF_DATA) }; const isInfiniteFinish = ref(true); @@ -234,34 +235,45 @@ export default { /** * 無限滾動: 監聽 scroll 有沒有滾到底部 * @param {element} event 滾動傳入的事件 + + scrollTop,表示容器的垂直滾動位置。具體來說,它是以像素為單位的數值, + 表示當前內容視窗(可見區域)的頂部距離整個可滾動內容的頂部的距離。 + 簡單來說,scrollTop 指的是滾動條的位置:當滾動條在最上面時,scrollTop 為 0; + 當滾動條向下移動時,scrollTop 會增加。 + 可是作為:我們目前已經滾動了多少。 + + clientHeight:表示容器的可見高度(不包括滾動條的高度)。它是以像素為單位的數值, + 表示容器內部的可見區域的高度。 + 與 offsetHeight 不同的是,clientHeight 不包含邊框、內邊距和滾動條的高度,只計算內容區域的高度。 + + scrollHeight:表示容器內部的總內容高度。它是以像素為單位的數值, + 包括看不見的(需要滾動才能看到的)部分。 + 簡單來說,scrollHeight 是整個可滾動內容的總高度,包括可見區域和需要滾動才能看到的部分。 */ + const handleScroll = (event) => { - if(internalInfiniteAcctData.value.length < ONCE_RENDER_NUM_OF_DATA || isInfiniteFinish.value === false) { - return; - } - const container = event.target; - const smallValue = 4; + const smallValue = 3; - const overScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight - smallValue; - - if(overScrollHeight){ + const isOverScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight - smallValue; + if(isOverScrollHeight){ fetchMoreDataVue3(); } }; const fetchMoreDataVue3 = () => { infiniteStart.value += ONCE_RENDER_NUM_OF_DATA; - internalInfiniteAcctData.value = [internalInfiniteAcctData.value, acctMgmtStore.allUserAccoutList.slice( - infiniteStart.value, this.infiniteStart + ONCE_RENDER_NUM_OF_DATA)]; - isInfiniteFinish.value = true; + if(infiniteAcctData.value.length < acctMgmtStore.allUserAccoutList.length) { + infiniteAcctData.value = [...infiniteAcctData.value, ...acctMgmtStore.allUserAccoutList.slice( + infiniteStart.value, infiniteStart.value + ONCE_RENDER_NUM_OF_DATA)]; + } }; return { isLoading, modalStore, loginUserData, - internalInfiniteAcctData, + infiniteAcctData, onCreateNewClick, handleScroll, getRowClass, @@ -286,7 +298,7 @@ export default { return { i18next: i18next, repeatedAccountList: repeatedAccountList, - infiniteAcctData: [], + infiniteAcctDataVue2: [], infiniteStart: 0, isInfiniteFinish: true, isInfinitMaxItemsMet: false, @@ -311,7 +323,7 @@ export default { * @param {element} event 滾動傳入的事件 */ handleScrollVue2(event) { - if(this.infinitMaxItems || this.infiniteAcctData.length < ONCE_RENDER_NUM_OF_DATA || this.isInfiniteFinish === false) { + if(this.infinitMaxItems || this.infiniteAcctDataVue2.length < ONCE_RENDER_NUM_OF_DATA || this.isInfiniteFinish === false) { return; } @@ -332,7 +344,7 @@ export default { this.infiniteFinish = false; this.infiniteStart += ONCE_RENDER_NUM_OF_DATA; // await this.acctMgmtStore.getAccountDetail(); - this.infiniteAcctData = await [...this.infiniteAcctData, ...this.allUserAccoutList.slice( + this.infiniteAcctDataVue2 = await [...this.infiniteAcctDataVue2, ...this.allUserAccoutList.slice( this.infiniteStart, this.infiniteStart + ONCE_RENDER_NUM_OF_DATA)]; this.isInfiniteFinish = true; this.isLoading = false; diff --git a/src/views/AccountManagement/ModalAccountEdit.vue b/src/views/AccountManagement/ModalAccountEdit.vue index 84e181b..7285c09 100644 --- a/src/views/AccountManagement/ModalAccountEdit.vue +++ b/src/views/AccountManagement/ModalAccountEdit.vue @@ -119,6 +119,8 @@ import { defineComponent, computed, ref, watch, } from 'vue'; import i18next from "@/i18n/i18n.js"; import { mapActions, } from 'pinia'; import { useModalStore } from '@/stores/modal.js'; +import { useRouter } from 'vue-router'; +import { useToast } from 'vue-toast-notification'; import useAcctMgmtStore from '@/stores/acctMgmt.js'; import ModalHeader from "./ModalHeader.vue"; import IconChecked from "@/components/icons/IconChecked.vue"; @@ -129,6 +131,9 @@ export default defineComponent({ const acctMgmtStore = useAcctMgmtStore(); const modalStore = useModalStore(); + const router = useRouter(); + const toast = useToast(); + const currentViewingUser = computed(() => acctMgmtStore.currentViewingUser); const isPwdEyeOn = ref(false); const isPwdConfirmEyeOn = ref(false); @@ -184,27 +189,37 @@ export default defineComponent({ return validateResult && isPwdMatched.value; } - const onConfirmBtnClick = () => { - const validateResult = validateConfirmPwd(); - if(!validateResult){ - return; + const onConfirmBtnClick = async () => { + + switch(whichCurrentModal.value) { + case MODAL_CREATE_NEW: + const validateResult = validateConfirmPwd(); + if(!validateResult){ + return; + } + //TODO: 這邊要記得回來加一個判斷帳號是否已經存在的邏輯 + checkAccountIsUnique(); + console.log('input content to feed in', + inputUserAccount.value, + inputPwd.value, + inputName.value, + isSetAsAdminChecked.value, + isSetActivedChecked.value, + ); + await acctMgmtStore.createNewAccount({ + username: inputUserAccount.value, + password: inputPwd.value, + name: inputName.value, + is_admin: isSetAsAdminChecked.value, + is_active: isSetActivedChecked.value, + }); + await toast.success(i18next.t("AcctMgmt.MsgAccountAdded")); + await modalStore.closeModal(); + await router.push('/account/account-admin'); + break; + default: + break; } - //TODO: 這邊要記得回來加一個判斷帳號是否已經存在的邏輯 - checkAccountIsUnique(); - console.log('input content to feed in', - inputUserAccount.value, - inputPwd.value, - inputName.value, - isSetAsAdminChecked.value, - isSetActivedChecked.value, - ); - acctMgmtStore.createNewAccount({ - username: inputUserAccount.value, - password: inputPwd.value, - name: inputName.value, - is_admin: isSetAsAdminChecked.value, - is_active: isSetActivedChecked.value, - }); }