WIP account mgmt data-table prototype and modal prototype.
This commit is contained in:
@@ -4,15 +4,46 @@
|
||||
<div class="flex w-full justify-end py-2">
|
||||
<SearchBar/>
|
||||
</div>
|
||||
<div id="acct_mgmt_data_grid" class="flex w-full">
|
||||
<DataTable dataKey="id" tableClass="w-full mt-4 text-sm cursor-pointer relative table-fixed"
|
||||
<div id="acct_mgmt_data_grid" class="flex w-full overflow-y-auto h-[570px]" @scroll="handleScroll">
|
||||
<DataTable :value="infiniteAcctData" dataKey="id" tableClass="w-full mt-4 text-sm relative table-fixed"
|
||||
>
|
||||
<Column field="account" :header="i18next.t('AcctMgmt.Account')" bodyClass="font-medium" sortable></Column>
|
||||
<Column field="account" :header="i18next.t('AcctMgmt.Account')" bodyClass="font-medium" sortable>
|
||||
<template #body="slotProps">
|
||||
<div @dblclick="onAcctDoubleClick()" class="cursor-pointer">
|
||||
{{ slotProps.data.account }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="fullName" :header="i18next.t('AcctMgmt.FullName')" bodyClass="text-neutral-500" sortable></Column>
|
||||
<Column field="adminRights" :header="i18next.t('AcctMgmt.AdminRights')" bodyClass="text-neutral-500"></Column>
|
||||
<Column field="accountActivation" :header="i18next.t('AcctMgmt.AccountActivation')" bodyClass="text-neutral-500"></Column>
|
||||
<Column field="detail" :header="i18next.t('AcctMgmt.Detail')" bodyClass="text-neutral-500"></Column>
|
||||
<Column field="edit" :header="i18next.t('AcctMgmt.Edit')" bodyClass="text-neutral-500"></Column>
|
||||
<Column field="adminRights" :header="i18next.t('AcctMgmt.AdminRights')" bodyClass="text-neutral-500 flex justify-center"
|
||||
headerClass="header-center">
|
||||
<template #body="slotProps">
|
||||
<img v-if="slotProps.data.adminRights" 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="accountActivation" :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.accountActivation" 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 field="detail" :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.id)"/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="edit" :header="i18next.t('AcctMgmt.Edit')" bodyClass="text-neutral-500">
|
||||
<template #body="slotProps">
|
||||
<img src="@/assets/icon-edit.svg" alt="Edit" class="cursor-pointer"/>
|
||||
</template></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
@@ -20,28 +51,125 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { storeToRefs, mapState, } from 'pinia';
|
||||
import { storeToRefs, mapState, mapActions, } from 'pinia';
|
||||
import LoadingStore from '@/stores/loading.js';
|
||||
import { useModalStore } from '@/stores/modal.js';
|
||||
import SearchBar from '../../../components/AccountMenu/SearchBar.vue';
|
||||
import i18next from '@/i18n/i18n.js';
|
||||
|
||||
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 };
|
||||
repeatedList.push(newAccount);
|
||||
});
|
||||
}
|
||||
|
||||
return repeatedList;
|
||||
}
|
||||
|
||||
const accountList = [
|
||||
{
|
||||
id: 12345,
|
||||
account: "REDACTED-USER1",
|
||||
fullName: "Alice Zheng",
|
||||
adminRights: true,
|
||||
accountActivation: true,
|
||||
detail: "abcde",
|
||||
},
|
||||
{
|
||||
id: 345,
|
||||
account: "REDACTED-USER2",
|
||||
fullName: "Mike Chen",
|
||||
adminRights: true,
|
||||
accountActivation: true,
|
||||
detail: "abcde",
|
||||
},
|
||||
{
|
||||
id: 88,
|
||||
account: "REDACTED-USER3",
|
||||
fullName: "Tory Cheng",
|
||||
adminRights: true,
|
||||
accountActivation: true,
|
||||
detail: "abcde",
|
||||
},
|
||||
];
|
||||
|
||||
const repeatedAccountList = repeatAccountList(accountList, 20);
|
||||
export default {
|
||||
setup() {
|
||||
const loadingStore = LoadingStore();
|
||||
const modalStore = useModalStore();
|
||||
const { isLoading } = storeToRefs(loadingStore);
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
modalStore,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
i18next: i18next,
|
||||
repeatedAccountList: repeatedAccountList,
|
||||
infiniteAcctData: repeatedAccountList.slice(0, ONCE_RENDER_NUM_OF_DATA),
|
||||
infiniteStart: 0,
|
||||
isInfiniteFinish: true,
|
||||
isInfinitMaxItemsMet: false,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
SearchBar,
|
||||
},
|
||||
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.repeatedAccountList.slice(
|
||||
this.infiniteStart, this.infiniteStart + ONCE_RENDER_NUM_OF_DATA)];
|
||||
this.isInfiniteFinish = true;
|
||||
this.isLoading = false;
|
||||
|
||||
},
|
||||
onDetailBtnClick(dataId){
|
||||
this.openModal();
|
||||
},
|
||||
...mapActions(useModalStore, ['openModal']),
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
@@ -50,3 +178,9 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/*為了讓 radio 按鈕可以置中,所以讓欄位的文字也置中 */
|
||||
.header-center .p-column-header-content{
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
33
src/views/AccountManagement/ModalContainer.vue
Normal file
33
src/views/AccountManagement/ModalContainer.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div id="modal_container" v-if="modalStore.isModalOpen" class="fixed w-screen h-screen bg-gray-800
|
||||
flex justify-center items-center">
|
||||
<button @click="modalStore.openModal" class="flex px-4 py-2 bg-blue-500 text-white rounded">
|
||||
Open Modal
|
||||
</button>
|
||||
|
||||
<button @click="modalStore.closeModal" class="flex px-4 py-2 bg-blue-500 text-white rounded">
|
||||
Close Modal
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useModalStore } from '@/stores/modal.js';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const modalStore = useModalStore();
|
||||
return {
|
||||
modalStore,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
#modal_container {
|
||||
z-index: 9999;
|
||||
background-color: rgb(254,254,254);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user