274 lines
11 KiB
Vue
274 lines
11 KiB
Vue
<template>
|
|
<div class="flex justify-center pt-2">
|
|
<div class="flex w-[1216px] flex-col items-center">
|
|
<div class="flex w-full justify-end py-2">
|
|
<SearchBar/>
|
|
</div>
|
|
<div id="acct_mgmt_data_grid" class="flex w-full overflow-y-auto h-[570px]" @scroll="handleScroll">
|
|
<DataTable :value="internalInfiniteAcctData" dataKey="username" tableClass="w-full mt-4 text-sm relative table-fixed"
|
|
>
|
|
<template #body="slotProps">
|
|
<div class="flex w-full h-full whole-row"
|
|
:class="{
|
|
'hover:bg-[#CBD5E1]': slotProps.data.isRowHovered,
|
|
}"
|
|
@mouseover="handleRowMouseOver(slotProps.data.username)"
|
|
@mouseout="handleRowMouseOut(slotProps.data.username)"
|
|
> <!--處理一整列的互動監聽-->
|
|
</div>
|
|
</template>
|
|
|
|
<Column field="username" :header="i18next.t('AcctMgmt.Account')" bodyClass="font-medium" sortable>
|
|
<template #body="slotProps">
|
|
<div @dblclick="onAcctDoubleClick()" class="cursor-pointer">
|
|
{{ slotProps.data.username }}
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
<Column field="name" :header="i18next.t('AcctMgmt.FullName')" bodyClass="text-neutral-500" sortable></Column>
|
|
<Column field="is_admin" :header="i18next.t('AcctMgmt.AdminRights')" bodyClass="text-neutral-500 flex justify-center"
|
|
headerClass="header-center">
|
|
<template #body="slotProps">
|
|
<img v-if="slotProps.data.is_admin" src="@/assets/radioOn.svg" alt="Radio On" class="cursor-pointer flex"
|
|
@click="onAdminRightsBtnClick(true)"
|
|
/>
|
|
<img v-else src="@/assets/radioOff.svg" alt="Radio Off" class="cursor-pointer flex"
|
|
@click="onAdminRightsBtnClick(false)"
|
|
/>
|
|
</template>
|
|
</Column>
|
|
<Column field="is_active" :header="i18next.t('AcctMgmt.AccountActivation')" bodyClass="text-neutral-500"
|
|
headerClass="header-center">
|
|
<template #body="slotProps">
|
|
<div class="w-full flex justify-center">
|
|
<img v-if="slotProps.data.is_active" src="@/assets/radioOn.svg" alt="Radio On" class="cursor-pointer flex"/>
|
|
<img v-else src="@/assets/radioOff.svg" alt="Radio Off" class="cursor-pointer flex"/>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
<Column :header="i18next.t('AcctMgmt.Detail')" bodyClass="text-neutral-500">
|
|
<template #body="slotProps">
|
|
<img src="@/assets/icon-detail-card.svg" alt="Detail" class="cursor-pointer" @click="onDetailBtnClick(slotProps.data.username)"/>
|
|
</template>
|
|
</Column>
|
|
<Column :header="i18next.t('AcctMgmt.Edit')" bodyClass="text-neutral-500">
|
|
<template #body="slotProps">
|
|
<img src="@/assets/icon-edit.svg" alt="Edit" class="cursor-pointer" @click="onEditBtnClick(slotProps.data.username)"/>
|
|
</template>
|
|
</Column>
|
|
<Column :header="i18next.t('AcctMgmt.Delete')" bodyClass="text-neutral-500">
|
|
<template #body="slotProps">
|
|
<img :src="slotProps.data.isDeleteHovered ? iconDeleteRed : iconDeleteGray"
|
|
:alt="slotProps.data.isDeleteHovered ? 'hovered' : 'not-hovered'"
|
|
class="cursor-pointer"
|
|
@mouseover="handleDeleteMouseOver(slotProps.data.username)"
|
|
@mouseout="handleDeleteMouseOut(slotProps.data.username)"
|
|
@click="onDeleteBtnClick(slotProps.data.username)"
|
|
>
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, computed, onBeforeMount, watch, } from 'vue';
|
|
import { storeToRefs, mapState, mapActions, } from 'pinia';
|
|
import LoadingStore from '@/stores/loading.js';
|
|
import { useModalStore } from '@/stores/modal.js';
|
|
import useAcctMgmtStore from '@/stores/acctMgmt.js';
|
|
import piniaLoginStore from '@/stores/login.js';
|
|
import SearchBar from '../../../components/AccountMenu/SearchBar.vue';
|
|
import i18next from '@/i18n/i18n.js';
|
|
import {
|
|
MODAL_CREATE_NEW,
|
|
MODAL_ACCT_EDIT,
|
|
MODAL_ACCT_INFO,
|
|
accountList,
|
|
} from "@/constants/constants.js";
|
|
import iconDeleteGray from '@/assets/icon-delete-gray.svg';
|
|
import iconDeleteRed from '@/assets/icon-delete-red.svg';
|
|
|
|
const ONCE_RENDER_NUM_OF_DATA = 9;
|
|
|
|
function repeatAccountList(accountList, N) {
|
|
const repeatedList = [];
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
// 复制每次的对象并将其添加到新数组中
|
|
accountList.forEach(account => {
|
|
// 创建一个新的对象,避免直接引用
|
|
const newAccount = { ...account, id: account.id + i };
|
|
repeatedList.push(newAccount);
|
|
});
|
|
}
|
|
|
|
return repeatedList;
|
|
}
|
|
|
|
// 這是製作假資料,在正式環境不會用到
|
|
const repeatedAccountList = repeatAccountList(accountList, 20);
|
|
|
|
export default {
|
|
setup(props) {
|
|
const acctMgmtStore = useAcctMgmtStore();
|
|
const loadingStore = LoadingStore();
|
|
const modalStore = useModalStore();
|
|
const { isLoading } = storeToRefs(loadingStore);
|
|
const loginStore = piniaLoginStore();
|
|
const internalInfiniteAcctData = ref([]);
|
|
|
|
const loginUserData = ref(null);
|
|
const allUserAccoutList = computed(() => acctMgmtStore.allUserAccoutList);
|
|
|
|
const fetchLoginUserData = async () => {
|
|
await loginStore.getUserData();
|
|
loginUserData.value = loginStore.userData;
|
|
};
|
|
|
|
const moveCurrentLoginUserToFirstRow = () => {
|
|
const currentLoginUsername = loginUserData.value.username;
|
|
if(internalInfiniteAcctData.value && internalInfiniteAcctData.value.length){
|
|
const index = internalInfiniteAcctData.value.findIndex(user => user.username === currentLoginUsername);
|
|
|
|
if (index !== -1) {
|
|
// 移除匹配的對象(現正登入的使用者)並將其插入到陣列的第一位"
|
|
const [user] = internalInfiniteAcctData.value.splice(index, 1);
|
|
internalInfiniteAcctData.value.unshift(user);
|
|
}
|
|
}
|
|
};
|
|
|
|
const getFirstPageUserData = async() => {
|
|
await acctMgmtStore.getAllUserAccounts();
|
|
internalInfiniteAcctData.value = allUserAccoutList.value.slice(0, ONCE_RENDER_NUM_OF_DATA)
|
|
};
|
|
|
|
const handleDeleteMouseOver = (username) => {
|
|
acctMgmtStore.changeIsDeleteHoveredByUser(username, true);
|
|
};
|
|
|
|
const handleDeleteMouseOut = (username) => {
|
|
acctMgmtStore.changeIsDeleteHoveredByUser(username, false);
|
|
};
|
|
|
|
const handleRowMouseOver = (username) => {
|
|
acctMgmtStore.changeIsRowHoveredByUser(username, true);
|
|
};
|
|
|
|
const handleRowMouseOut = (username) => {
|
|
acctMgmtStore.changeIsRowHoveredByUser(username, false);
|
|
};
|
|
|
|
|
|
const onDeleteBtnClick = (usernameToDelete) => {
|
|
|
|
};
|
|
|
|
onBeforeMount(async () => {
|
|
await fetchLoginUserData();
|
|
await getFirstPageUserData();
|
|
moveCurrentLoginUserToFirstRow();
|
|
});
|
|
|
|
return {
|
|
isLoading,
|
|
modalStore,
|
|
loginUserData,
|
|
internalInfiniteAcctData,
|
|
onDeleteBtnClick,
|
|
handleDeleteMouseOver,
|
|
handleDeleteMouseOut,
|
|
handleRowMouseOver,
|
|
handleRowMouseOut,
|
|
iconDeleteGray,
|
|
iconDeleteRed,
|
|
};
|
|
},
|
|
data() {
|
|
return {
|
|
i18next: i18next,
|
|
repeatedAccountList: repeatedAccountList,
|
|
infiniteAcctData: [],
|
|
infiniteStart: 0,
|
|
isInfiniteFinish: true,
|
|
isInfinitMaxItemsMet: false,
|
|
};
|
|
},
|
|
components: {
|
|
SearchBar,
|
|
},
|
|
computed: {
|
|
...mapState(useAcctMgmtStore, ['allUserAccoutList']),
|
|
},
|
|
methods: {
|
|
onAcctDoubleClick(){
|
|
|
|
},
|
|
onAdminRightsBtnClick(isOn){
|
|
|
|
},
|
|
/**
|
|
* 無限滾動: 監聽 scroll 有沒有滾到底部
|
|
* @param {element} event 滾動傳入的事件
|
|
*/
|
|
handleScroll(event) {
|
|
if(this.infinitMaxItems || this.infiniteAcctData.length < ONCE_RENDER_NUM_OF_DATA || this.isInfiniteFinish === false) {
|
|
return;
|
|
}
|
|
|
|
const container = event.target;
|
|
const smallValue = 4;
|
|
|
|
const overScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight - smallValue;
|
|
|
|
if(overScrollHeight){
|
|
this.fetchMoreData();
|
|
}
|
|
},
|
|
/**
|
|
* 無限滾動: 滾到底後,要載入數據
|
|
*/
|
|
async fetchMoreData() {
|
|
this.isLoading = true;
|
|
this.infiniteFinish = false;
|
|
this.infiniteStart += ONCE_RENDER_NUM_OF_DATA;
|
|
// await this.acctMgmtStore.getAccountDetail();
|
|
this.infiniteAcctData = await [...this.infiniteAcctData, ...this.allUserAccoutList.slice(
|
|
this.infiniteStart, this.infiniteStart + ONCE_RENDER_NUM_OF_DATA)];
|
|
this.isInfiniteFinish = true;
|
|
this.isLoading = false;
|
|
|
|
},
|
|
onDetailBtnClick(dataKey){
|
|
this.openModal(MODAL_ACCT_INFO);
|
|
this.setCurrentViewingUser(dataKey);
|
|
},
|
|
onEditBtnClick(dataKey){
|
|
this.openModal(MODAL_ACCT_EDIT);
|
|
this.setCurrentViewingUser(dataKey);
|
|
},
|
|
moveCurrentLoginUserToFirstRow(){
|
|
|
|
},
|
|
...mapActions(useModalStore, ['openModal']),
|
|
...mapActions(useAcctMgmtStore, [
|
|
'setCurrentViewingUser',
|
|
'getAllUserAccounts',
|
|
]),
|
|
},
|
|
created() {
|
|
},
|
|
mounted() {
|
|
this.isLoading = false; //TODO:
|
|
},
|
|
};
|
|
</script>
|
|
<style>
|
|
/*為了讓 radio 按鈕可以置中,所以讓欄位的文字也置中 */
|
|
.header-center .p-column-header-content{
|
|
justify-content: center;
|
|
}
|
|
</style> |