ユーザー管理ページを追加しました。
This commit is contained in:
36
frontend/src/components/UserList.vue
Normal file
36
frontend/src/components/UserList.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<q-table :rows="rows" :columns="columns" row-key="id" :filter="props.filter" :loading="loading"
|
||||
:pagination="pagination" selection="single" v-model:selected="selected"></q-table>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { api } from 'boot/axios';
|
||||
const props = defineProps<{filter:string}>()
|
||||
const columns = [
|
||||
{ name: 'id', label: 'ID', field: 'id', align: 'left', sortable: true },
|
||||
{ name: 'firstName', label: '氏名', field: 'firstName', align: 'left', sortable: true },
|
||||
{ name: 'lastName', label: '苗字', field: 'lastName', align: 'left', sortable: true },
|
||||
{ name: 'email', label: '電子メール', field: 'email', align: 'left', sortable: true },
|
||||
];
|
||||
|
||||
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 10 });
|
||||
const rows = ref([]);
|
||||
const loading = ref(false);
|
||||
const selected = ref([]);
|
||||
defineExpose({
|
||||
selected
|
||||
})
|
||||
const getUsers = async (filter = () => true) => {
|
||||
loading.value = true;
|
||||
const result = await api.get(`api/v1/users`);
|
||||
rows.value = result.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 }
|
||||
}).filter(filter);
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getUsers();
|
||||
})
|
||||
</script>
|
||||
302
frontend/src/pages/UserManagement.vue
Normal file
302
frontend/src/pages/UserManagement.vue
Normal file
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<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="addRow" />
|
||||
<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 :props="p">
|
||||
<q-btn-group flat>
|
||||
<q-btn flat color="primary" padding="xs" size="1em" icon="edit_note" @click="editRow(p.row)" />
|
||||
<q-btn flat color="negative" padding="xs" size="1em" icon="delete_outline" @click="removeRow(p.row)" />
|
||||
</q-btn-group>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<q-dialog :model-value="show" persistent>
|
||||
<q-card style="min-width: 36em">
|
||||
<q-form class="q-gutter-md" @submit="onSubmit" autocomplete="off">
|
||||
<q-card-section>
|
||||
<div class="text-h6 q-ma-sm">K-True Account</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none q-mt-none">
|
||||
<div class="q-gutter-lg">
|
||||
|
||||
<q-input filled v-model="firstName" label="氏名 *" hint="ユーザーの氏名を入力してください" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'ユーザーの氏名を入力してください。']" />
|
||||
|
||||
<q-input filled v-model="lastName" label="苗字 *" hint="ユーザーの苗字を入力してください" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'ユーザーの苗字を入力してください']" />
|
||||
|
||||
<q-input filled type="email" v-model="email" label="電子メール *" hint="電子メール、ログインとしても使用" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || '電子メールを入力してください']" autocomplete="new-password" />
|
||||
|
||||
<q-input v-if="isCreate" v-model="pwd" filled :type="isPwd ? 'password' : 'text'" hint="パスワード"
|
||||
label="パスワード" :disable="!isCreate" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'Please type something']" autocomplete="new-password">
|
||||
<template v-slot:append>
|
||||
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
|
||||
@click="isPwd = !isPwd" />
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-item tag="label" class="q-pl-sm q-pr-none q-py-xs">
|
||||
<q-item-section>
|
||||
<q-item-label>システム管理者</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section avatar>
|
||||
<q-toggle v-model="isSuperuser" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item tag="label" class="q-pl-sm q-pr-none q-py-xs">
|
||||
<q-item-section>
|
||||
<q-item-label>使用可能</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section avatar>
|
||||
<q-toggle v-model="isActive" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<div class="q-gutter-y-md" v-if="!isCreate">
|
||||
<q-separator />
|
||||
|
||||
<q-item tag="label" class="q-pl-sm q-pr-none q-py-xs">
|
||||
<q-item-section>
|
||||
<q-item-label>パスワードリセット</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section avatar>
|
||||
<q-toggle v-model="resetPsw" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-input v-model="pwd" filled :type="isPwd ? 'password' : 'text'" hint="パスワードを入力してください" label="パスワード"
|
||||
:disable="!resetPsw" lazy-rules :rules="[val => val && val.length > 0 || 'Please type something']"
|
||||
autocomplete="new-password">
|
||||
<template v-slot:append>
|
||||
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
|
||||
@click="isPwd = !isPwd" />
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- <q-btn label="asdf"/> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</q-card-section>
|
||||
<q-card-actions align="right" class="text-primary q-mb-md q-mx-sm">
|
||||
<q-btn label="保存" type="submit" color="primary" />
|
||||
<q-btn label="キャンセル" type="cancel" color="primary" flat class="q-ml-sm" @click="closeDg()" />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card>
|
||||
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="confirm" persistent>
|
||||
<q-card>
|
||||
<q-card-section class="row items-center">
|
||||
<q-icon name="warning" color="warning" size="2em" />
|
||||
<span class="q-ml-sm">削除してもよろしいですか?</span>
|
||||
</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" v-close-popup @click="deleteUser()" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
const columns = [
|
||||
{ name: 'id', label: 'ID', field: 'id', align: 'left', sortable: true },
|
||||
{ name: 'firstName', label: '氏名', field: 'firstName', align: 'left', sortable: true },
|
||||
{ name: 'lastName', label: '苗字', field: 'lastName', 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 pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
|
||||
|
||||
const loading = ref(false);
|
||||
const filter = ref('');
|
||||
const statusFilter = ref('全データ');
|
||||
const rows = ref([]);
|
||||
const show = ref(false);
|
||||
const confirm = ref(false);
|
||||
const resetPsw = ref(false);
|
||||
|
||||
const firstName = ref('');
|
||||
const lastName = ref('');
|
||||
const email = ref('');
|
||||
const isSuperuser = ref(false);
|
||||
const isActive = ref(true);
|
||||
|
||||
const isPwd = ref(true);
|
||||
const pwd = ref('');
|
||||
const isCreate = ref(true);
|
||||
let editId = ref(0);
|
||||
|
||||
const getUsers = async (filter = () => true) => {
|
||||
loading.value = true;
|
||||
const result = await api.get(`api/v1/users`);
|
||||
rows.value = result.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 }
|
||||
}).filter(filter);
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
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 () => {
|
||||
await getUsers();
|
||||
})
|
||||
|
||||
const options = ['全データ', 'システム管理者のみ', '使用可能', '使用不可']
|
||||
|
||||
// emulate fetching data from server
|
||||
const addRow = () => {
|
||||
// editId.value
|
||||
onReset();
|
||||
show.value = true;
|
||||
}
|
||||
|
||||
const removeRow = (row) => {
|
||||
confirm.value = true;
|
||||
editId.value = row.id;
|
||||
}
|
||||
|
||||
const deleteUser = () => {
|
||||
api.delete(`api/v1/users/${editId.value}`).then(() => {
|
||||
getUsers();
|
||||
})
|
||||
editId.value = 0;
|
||||
};
|
||||
|
||||
const editRow = (row) => {
|
||||
isCreate.value = false
|
||||
editId.value = row.id;
|
||||
|
||||
firstName.value = row.firstName;
|
||||
lastName.value = row.lastName;
|
||||
email.value = row.email;
|
||||
pwd.value = row.password;
|
||||
|
||||
isSuperuser.value = row.isSuperuser;
|
||||
isActive.value = row.isActive;
|
||||
|
||||
isPwd.value = true;
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
const closeDg = () => {
|
||||
show.value = false;
|
||||
onReset();
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
if (editId.value !== 0) {
|
||||
api.put(`api/v1/users/${editId.value}`, {
|
||||
'first_name': firstName.value,
|
||||
'last_name': lastName.value,
|
||||
'is_superuser': isSuperuser.value,
|
||||
'is_active': isActive.value,
|
||||
'email': email.value,
|
||||
...(isCreate.value || resetPsw.value ? { password: pwd.value } : {})
|
||||
}).then(() => {
|
||||
getUsers();
|
||||
closeDg();
|
||||
onReset();
|
||||
})
|
||||
}
|
||||
else {
|
||||
api.post(`api/v1/users`, {
|
||||
'id': 0,
|
||||
'first_name': firstName.value,
|
||||
'last_name': lastName.value,
|
||||
'is_superuser': isSuperuser.value,
|
||||
'is_active': isActive.value,
|
||||
'email': email.value,
|
||||
'password': pwd.value
|
||||
}).then(() => {
|
||||
getUsers();
|
||||
closeDg();
|
||||
onReset();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
firstName.value = '';
|
||||
lastName.value = '';
|
||||
email.value = '';
|
||||
pwd.value = '';
|
||||
isActive.value = true;
|
||||
isSuperuser.value = false;
|
||||
isPwd.value = true;
|
||||
editId.value = 0;
|
||||
isCreate.value = true;
|
||||
resetPsw.value = false
|
||||
}
|
||||
</script>
|
||||
@@ -26,6 +26,7 @@ const routes: RouteRecordRaw[] = [
|
||||
{ path: 'right', component: () => import('pages/testRight.vue') },
|
||||
{ path: 'domain', component: () => import('pages/TenantDomain.vue') },
|
||||
{ path: 'userdomain', component: () => import('pages/UserDomain.vue')},
|
||||
{ path: 'user', component: () => import('pages/UserManagement.vue')},
|
||||
{ path: 'condition', component: () => import('pages/conditionPage.vue') }
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user