Add role page
This commit is contained in:
254
frontend/src/pages/RoleManagement.vue
Normal file
254
frontend/src/pages/RoleManagement.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-sm row items-start">
|
||||
<q-breadcrumbs>
|
||||
<q-breadcrumbs-el icon="work" label="ロール管理" />
|
||||
</q-breadcrumbs>
|
||||
</div>
|
||||
|
||||
<div class="row" style="min-height: 80vh;">
|
||||
|
||||
<div class="col-auto">
|
||||
<div class="q-pa-md" style="width: 250px">
|
||||
<q-list bordered separator class="rounded-borders">
|
||||
<q-item active v-if="allLoading" active-class="menu-active">
|
||||
<q-item-section class="text-weight-bold"> 読み込み中... </q-item-section>
|
||||
</q-item>
|
||||
<q-item v-else v-for="item in roles" :key="item.id" clickable v-ripple :active="selected.id === item.id" @click="roleClicked(item)" active-class="menu-active">
|
||||
<q-item-section class="text-weight-bold"> {{ item.label }} </q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
<q-table title="ユーザーリスト" :rows="rows" :columns="columns" row-key="id" :filter="filter" :loading="loading"
|
||||
:pagination="pagination" >
|
||||
|
||||
<template v-slot:top>
|
||||
<q-btn color="primary" :disable="loading" label="追加" @click="showAddRoleDialog" />
|
||||
<q-space />
|
||||
<q-input borderless dense filled debounce="300" v-model="filter" placeholder="検索">
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-status="props">
|
||||
<q-td :props="props">
|
||||
<div class="row">
|
||||
<div v-if="props.row.isActive">
|
||||
<q-chip square color="positive" text-color="white" icon="done" label="使用可能" size="sm" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<q-chip square color="negative" text-color="white" icon="block" label="使用不可" size="sm" />
|
||||
</div>
|
||||
|
||||
<q-chip v-if="props.row.isSuperuser" square color="accent" text-color="white" icon="admin_panel_settings"
|
||||
label="システム管理者" size="sm" />
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<template v-slot:header-cell-status="p">
|
||||
<q-th :props="p">
|
||||
<div class="row items-center">
|
||||
<label class="q-mr-md">{{ p.col.label }}</label>
|
||||
<q-select v-model="statusFilter" :options="options" @update:model-value="updateStatusFilter" borderless
|
||||
dense options-dense style="font-size: 12px; padding-top: 1px;" />
|
||||
</div>
|
||||
</q-th>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-actions="p">
|
||||
<q-td auto-width :props="p">
|
||||
<table-action-menu :row="p.row" minWidth="180px" max-width="200px" :actions="actionList" />
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<show-dialog v-model:visible="showSelectUser" name="ユーザー選択" @close="closeSelectUserDialog" min-width="50vw" min-height="50vh" :ok-btn-auto-close="false" :ok-btn-loading="isAdding">
|
||||
<template v-slot:toolbar>
|
||||
<q-input dense debounce="300" v-model="dgFilter" placeholder="検索" clearable>
|
||||
<template v-slot:before>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<user-select-box ref="userDialog" name="ユーザー" type="single" :filter="dgFilter" :filterInitRowsFunc="filterInitRows" />
|
||||
</show-dialog>
|
||||
|
||||
<q-dialog v-model="deleteDialog" persistent>
|
||||
<q-card>
|
||||
<q-card-section class="row items-center">
|
||||
<q-icon name="warning" color="warning" size="2em" />
|
||||
<div class="q-ml-sm text-weight-bold">ロールメンバーを削除</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-py-none">
|
||||
<!-- <span class="q-ml-sm">この役割を与えられたユーザーは、メンバー役に再配置されます。</span> -->
|
||||
<div class="q-mx-sm">ユーザー「{{targetRow.email}}」を「{{selected.label}}」の役割から</div>
|
||||
<div class="q-mx-sm">本当に外しますか?</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn flat label="Cancel" color="primary" v-close-popup />
|
||||
<q-btn flat label="OK" color="primary" :loading="deleteUserRoleLoading" @click="deleteUserRole" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { IUser } from 'src/types/UserTypes';
|
||||
import ShowDialog from 'src/components/ShowDialog.vue';
|
||||
import UserSelectBox from 'src/components/dialog/UserSelectBox.vue';
|
||||
import TableActionMenu from 'components/TableActionMenu.vue';
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
const columns = [
|
||||
{ name: 'id', label: 'ID', field: 'id', align: 'left', sortable: true },
|
||||
{ name: 'lastName', label: '氏名', field: 'lastName', align: 'left', sortable: true },
|
||||
{ name: 'firstName', label: '苗字', field: 'firstName', align: 'left', sortable: true },
|
||||
{ name: 'email', label: '電子メール', field: 'email', align: 'left', sortable: true },
|
||||
{ name: 'status', label: '状況', field: 'status', align: 'left' },
|
||||
{ name: 'actions', label: '操作', field: 'actions' }
|
||||
];
|
||||
|
||||
const roles = ref([])
|
||||
const selected = ref();
|
||||
|
||||
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
|
||||
|
||||
const allLoading = ref(true);
|
||||
const loading = ref(false);
|
||||
const filter = ref('');
|
||||
const statusFilter = ref('全データ');
|
||||
const dgFilter = ref('');
|
||||
const allRows = ref([]);
|
||||
const targetRow = ref<IUser>();
|
||||
|
||||
const userDialog = ref();
|
||||
const showSelectUser=ref(false);
|
||||
const isAdding = ref(false);
|
||||
const deleteDialog = ref(false);
|
||||
const deleteUserRoleLoading = ref(false);
|
||||
|
||||
const actionList = [
|
||||
// { label: '移動', icon: 'account_tree', action: toEditFlowPage },
|
||||
{ separator: true },
|
||||
{ label: '削除', icon: 'delete_outline', class: 'text-red', action: removeRow },
|
||||
];
|
||||
|
||||
const options = ['全データ', 'システム管理者のみ', '使用可能', '使用不可']
|
||||
|
||||
const roleClicked = async (role) => {
|
||||
selected.value = role;
|
||||
}
|
||||
|
||||
const rows = computed(() => allRows.value.filter((item) => item.roles?.includes(selected.value.id) ));
|
||||
const rowIds = computed(() => new Set(rows.value?.map((item) => item.id)));
|
||||
|
||||
const getUsers = async (filter = () => true) => {
|
||||
loading.value = true;
|
||||
allRows.value = []
|
||||
const result = await api.get(`api/v1/users`);
|
||||
allRows.value = result.data.data.map((item) => {
|
||||
return { id: item.id, firstName: item.first_name, lastName: item.last_name, email: item.email, isSuperuser: item.is_superuser, isActive: item.is_active, roles: item.roles.map(role => role.id) }
|
||||
}).filter(filter);
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
const getRoles = async () => {
|
||||
const result = await api.get(`api/v1/roles`);
|
||||
roles.value = result.data.data.map((item) => {
|
||||
return { id: item.id, label: item.description }
|
||||
})
|
||||
selected.value = roles.value[0];
|
||||
}
|
||||
|
||||
const updateStatusFilter = (status) => {
|
||||
switch (status) {
|
||||
case 'システム管理者のみ':
|
||||
getUsers((row) => row.isSuperuser)
|
||||
break;
|
||||
case '使用可能':
|
||||
getUsers((row) => row.isActive)
|
||||
break;
|
||||
case '使用不可':
|
||||
getUsers((row) => !row.isActive)
|
||||
break;
|
||||
default:
|
||||
getUsers()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
allLoading.value = true;
|
||||
await Promise.all([
|
||||
getRoles(),
|
||||
getUsers()
|
||||
]);
|
||||
allLoading.value = false;
|
||||
});
|
||||
|
||||
const filterInitRows = (row: {id: string}) => {
|
||||
return !rowIds.value.has(row.id);
|
||||
}
|
||||
|
||||
const showAddRoleDialog = () => {
|
||||
showSelectUser.value = true;
|
||||
isAdding.value = false;
|
||||
dgFilter.value = ''
|
||||
}
|
||||
|
||||
const closeSelectUserDialog = async (val: 'OK'|'Cancel') => {
|
||||
showSelectUser.value = true;
|
||||
if (val == 'OK' && userDialog.value.selected[0]) {
|
||||
isAdding.value = true;
|
||||
const user = userDialog.value.selected[0];
|
||||
const result = await api.post(`api/v1/userrole`, {
|
||||
'userid': user.id,
|
||||
'roleids': user.roles.concat(selected.value.id)
|
||||
});
|
||||
await getUsers();
|
||||
}
|
||||
showSelectUser.value = false;
|
||||
isAdding.value = false;
|
||||
}
|
||||
|
||||
function removeRow(user:IUser) {
|
||||
targetRow.value = user;
|
||||
deleteDialog.value = true;
|
||||
}
|
||||
|
||||
const deleteUserRole = async () => {
|
||||
if (targetRow.value?.id) {
|
||||
deleteUserRoleLoading.value = true;
|
||||
// TODO 返回不需要重新读
|
||||
const result = await api.post(`api/v1/userrole`, {
|
||||
'userid': targetRow.value.id,
|
||||
'roleids': targetRow.value.roles.filter(e => e !== selected.value.id)
|
||||
});
|
||||
await getUsers();
|
||||
deleteUserRoleLoading.value = false;
|
||||
deleteDialog.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.menu-active {
|
||||
color: white;
|
||||
background: var(--q-primary)
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user