155 lines
6.1 KiB
Vue
155 lines
6.1 KiB
Vue
<script setup>
|
|
import {
|
|
NFlex, NButton, NDataTable, NAvatar, NTag,
|
|
NText, NEl, NIcon, NInput,
|
|
} from 'naive-ui'
|
|
import { TbUsers, TbUserCheck, TbUserOff, TbSearch, TbPlus, TbPencil, TbLayoutDashboard } from 'vue-icons-plus/tb'
|
|
import AppLayout from '../../../Layouts/AppLayout.vue'
|
|
import AppContainer from '../../../Components/AppContainer.vue'
|
|
import SectionCard from '../../../Components/SectionCard.vue'
|
|
import PageBanner from '../../../Components/PageBanner.vue'
|
|
import { Link } from '@inertiajs/vue3'
|
|
import { computed, h, ref } from 'vue'
|
|
import { useThemeVars } from 'naive-ui'
|
|
|
|
const props = defineProps({
|
|
users: { type: Array, default: () => [] }
|
|
})
|
|
|
|
const themeVars = useThemeVars()
|
|
const search = ref('')
|
|
|
|
const filteredUsers = computed(() => {
|
|
const q = search.value.toLowerCase().trim()
|
|
if (!q) return props.users
|
|
return props.users.filter(u =>
|
|
u.name.toLowerCase().includes(q) || u.login.toLowerCase().includes(q)
|
|
)
|
|
})
|
|
|
|
const activeCount = computed(() => props.users.filter(u => u.is_active).length)
|
|
const blockedCount = computed(() => props.users.filter(u => !u.is_active).length)
|
|
|
|
const initials = (name) =>
|
|
(name ?? '').trim().split(/\s+/).slice(0, 2).map(p => p[0]?.toUpperCase() ?? '').join('')
|
|
|
|
const columns = computed(() => [
|
|
{
|
|
key: 'name',
|
|
title: 'Пользователь',
|
|
render: (row) => h(NFlex, { align: 'center', size: 10 }, () => [
|
|
h(NAvatar, {
|
|
round: true,
|
|
size: 32,
|
|
style: `background: color-mix(in srgb, ${themeVars.value.primaryColor} 20%, transparent); color: ${themeVars.value.primaryColor}; font-size: 12px; font-weight: 600; flex-shrink: 0;`
|
|
}, () => initials(row.name)),
|
|
h(NFlex, { vertical: true, size: 2 }, () => [
|
|
h(NText, { style: 'font-weight: 500; font-size: 14px; line-height: 1.2;' }, () => row.name),
|
|
h(NText, { depth: 3, style: 'font-size: 12px;' }, () => `@${row.login}`),
|
|
]),
|
|
])
|
|
},
|
|
{
|
|
key: 'is_active',
|
|
title: 'Статус',
|
|
width: 140,
|
|
render: (row) => h(NTag, {
|
|
type: row.is_active ? 'success' : 'error',
|
|
size: 'small',
|
|
round: true,
|
|
bordered: false,
|
|
}, () => row.is_active ? 'Активен' : 'Заблокирован')
|
|
},
|
|
{
|
|
key: 'created_at',
|
|
title: 'Создан',
|
|
width: 160,
|
|
render: (row) => h(NText, { depth: 3, style: 'font-size: 13px;' }, () => row.created_at)
|
|
},
|
|
{
|
|
key: 'actions',
|
|
title: '',
|
|
width: 60,
|
|
align: 'center',
|
|
render: (row) => h(NButton, {
|
|
text: true,
|
|
size: 'small',
|
|
tag: Link,
|
|
href: `/admin/users/${row.id}`,
|
|
title: 'Редактировать',
|
|
}, { icon: () => h(NIcon, { size: 18 }, () => h(TbPencil)) })
|
|
},
|
|
])
|
|
</script>
|
|
|
|
<template>
|
|
<AppLayout>
|
|
<AppContainer>
|
|
|
|
<PageBanner
|
|
title="Учётные записи"
|
|
:icon="TbUsers"
|
|
:breadcrumbs="[{ label: 'Администратор', href: '/admin', icon: TbLayoutDashboard, tag: Link }]"
|
|
>
|
|
<template #meta>
|
|
<NFlex align="center" :size="8">
|
|
<NText depth="3" style="font-size: 13px;">
|
|
{{ props.users.length }} {{ props.users.length === 1 ? 'пользователь' : 'пользователей' }}
|
|
</NText>
|
|
<NEl style="width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .3;" />
|
|
<NFlex align="center" :size="4">
|
|
<NIcon size="12" style="color: var(--success-color);"><TbUserCheck /></NIcon>
|
|
<NText depth="3" style="font-size: 13px;">{{ activeCount }} активных</NText>
|
|
</NFlex>
|
|
<template v-if="blockedCount > 0">
|
|
<NEl style="width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .3;" />
|
|
<NFlex align="center" :size="4">
|
|
<NIcon size="12" style="color: var(--error-color);"><TbUserOff /></NIcon>
|
|
<NText style="font-size: 13px; color: var(--error-color);">{{ blockedCount }} заблокировано</NText>
|
|
</NFlex>
|
|
</template>
|
|
</NFlex>
|
|
</template>
|
|
<template #actions>
|
|
<NButton type="primary" :tag="Link" href="/admin/users/new">
|
|
<template #icon><NIcon><TbPlus /></NIcon></template>
|
|
Создать пользователя
|
|
</NButton>
|
|
</template>
|
|
</PageBanner>
|
|
|
|
<!-- Таблица -->
|
|
<SectionCard :icon="TbUsers" title="Список пользователей" no-padding>
|
|
<template #header-extra>
|
|
<NInput
|
|
v-model:value="search"
|
|
size="small"
|
|
placeholder="Поиск..."
|
|
clearable
|
|
style="width: 200px;"
|
|
>
|
|
<template #prefix><NIcon depth="3"><TbSearch /></NIcon></template>
|
|
</NInput>
|
|
</template>
|
|
<NDataTable
|
|
:columns="columns"
|
|
:data="filteredUsers"
|
|
:bordered="false"
|
|
size="small"
|
|
flex-height
|
|
style="height: calc(100vh - 300px); min-height: 200px;"
|
|
/>
|
|
</SectionCard>
|
|
|
|
</AppContainer>
|
|
</AppLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
:deep(.n-data-table-th) { background: transparent !important; }
|
|
:deep(.n-data-table) { background: transparent; }
|
|
:deep(.n-data-table-wrapper) { border-radius: 0; }
|
|
:deep(.n-data-table-th .n-data-table-th__title) { font-size: 12px; }
|
|
:deep(.n-data-table-td) { font-size: 13px; }
|
|
</style>
|