Files
lucia-frontend/src/views/AccountManagement/AccountAdmin/index.vue
2024-06-21 11:14:49 +08:00

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>