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>
|
||||
@@ -179,7 +179,11 @@
|
||||
</div>
|
||||
<!-- All Files type of grid -->
|
||||
<ul class="flex justify-start items-start gap-4 flex-wrap overflow-y-scroll overflow-x-hidden max-h-[calc(100vh_-_440px)] scrollbar" v-else>
|
||||
<li class="w-[216px] h-[168px] p-4 border rounded border-neutral-300 hover:bg-primary/10 hover:border-primary duration-300 flex flex-col justify-between cursor-pointer" v-for="(file, index) in allFiles" :key="file.id" :class="{ 'bg-primary/10 border-primary': isActive === index}" @dblclick="enterDiscover(file)" :title="file.name" @contextmenu="onRightClick($event, file)" @click="onGridCardClick(file, index)" :id="'li' + index">
|
||||
<li class="w-[216px] h-[168px] p-4 border rounded border-neutral-300 hover:bg-primary/10 hover:border-primary duration-300
|
||||
flex flex-col justify-between cursor-pointer"
|
||||
v-for="(file, index) in allFiles" :key="file.id" :class="{ 'bg-primary/10 border-primary': isActive === index}"
|
||||
@dblclick="enterDiscover(file)" :title="file.name" @contextmenu="onRightClick($event, file)"
|
||||
@click="onGridCardClick(file, index)" :id="'li' + index">
|
||||
<div>
|
||||
<span class="material-symbols-outlined mb-2 !text-[32px] block">
|
||||
{{ file.icon }}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<ModalContainer/>
|
||||
<header class="sticky inset-x-0 top-0 w-full bg-neutral-10 z-10">
|
||||
<Header/>
|
||||
<Navbar/>
|
||||
</header>
|
||||
<main class="w-full">
|
||||
<Loading v-if="loadingStore.isLoading" />
|
||||
<Loading v-if="loadingStore.isLoading" />
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
</template>
|
||||
@@ -21,6 +22,7 @@ import { leaveFilter, leaveConformance } from '@/module/alertModal.js';
|
||||
import PageAdminStore from '@/stores/pageAdmin.js';
|
||||
import LoginStore from "@/stores/login.js";
|
||||
import { getCookie } from "../utils/cookieUtil.js";
|
||||
import ModalContainer from './AccountManagement/ModalContainer.vue';
|
||||
|
||||
export default {
|
||||
name: 'MainContainer',
|
||||
@@ -41,7 +43,8 @@ export default {
|
||||
components: {
|
||||
Header,
|
||||
Navbar,
|
||||
Loading
|
||||
Loading,
|
||||
ModalContainer,
|
||||
},
|
||||
computed: {
|
||||
...mapState(PageAdminStore, [
|
||||
|
||||
Reference in New Issue
Block a user