feat : user group

This commit is contained in:
cxymds
2024-12-16 19:02:31 +08:00
parent 6f8dbb84d9
commit 6591ee2675
8 changed files with 526 additions and 0 deletions
+2
View File
@@ -34,6 +34,8 @@ declare module 'vue' {
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
NSwitch: typeof import('naive-ui')['NSwitch']
NTabPane: typeof import('naive-ui')['NTabPane']
NTabs: typeof import('naive-ui')['NTabs']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
+2
View File
@@ -0,0 +1,2 @@
export { default as newUser } from './newUser.vue';
export { default as newGroup } from './newGroup.vue';
+81
View File
@@ -0,0 +1,81 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
interface Props {
visible: boolean;
}
const { visible } = defineProps<Props>();
const message = useMessage();
const { $api } = useNuxtApp();
const emit = defineEmits<Emits>();
const defaultFormModal = {
group: '',
members: [],
};
const formModel = ref({ ...defaultFormModal });
interface Emits {
(e: 'update:visible', visible: boolean): void;
(e: 'search'): void;
}
const users = ref([]);
const modalVisible = computed({
get() {
return visible;
},
set(visible) {
closeModal(visible);
},
});
function closeModal(visible = false) {
emit('update:visible', visible);
}
async function submitForm() {
try {
const res = await $api.post('/group', {
body: formModel.value,
});
message.success('添加成功');
closeModal();
emit('search');
} catch (error) {
message.error('添加失败');
}
}
</script>
<template>
<n-modal
v-model:show="modalVisible"
:mask-closable="false"
preset="card"
title="添加用户组"
class="w-1/2"
:segmented="{
content: true,
action: true,
}">
<n-form label-placement="left" :model="formModel" label-align="right" :label-width="130">
<n-grid :cols="24" :x-gap="18">
<n-form-item-grid-item :span="24" label="名称" path="group">
<n-input v-model:value="formModel.group" />
</n-form-item-grid-item>
<n-form-item-grid-item :span="24" label="用户" path="members">
<n-select v-model:value="formModel.members" multiple :options="users" />
</n-form-item-grid-item>
</n-grid>
</n-form>
<template #action>
<n-space justify="center">
<n-button @click="closeModal()">取消</n-button>
<n-button type="primary" @click="submitForm">提交</n-button>
</n-space>
</template>
</n-modal>
</template>
<style scoped></style>
+7
View File
@@ -0,0 +1,7 @@
<template>
<div></div>
</template>
<script setup></script>
<style lang="scss" scoped></style>
+25
View File
@@ -0,0 +1,25 @@
<template>
<div>
<page-header>
<template #title>
<h1 class="text-2xl font-bold">用户</h1>
</template>
</page-header>
<page-content>
<n-tabs type="card" :size="size">
<n-tab-pane name="users" tab="用户">
<userTab></userTab>
</n-tab-pane>
<n-tab-pane name="userGroup" tab="用户组">
<groupTab></groupTab>
</n-tab-pane>
</n-tabs>
</page-content>
</div>
</template>
<script setup>
import { groupTab, userTab } from './tabs';
</script>
<style lang="scss" scoped></style>
+201
View File
@@ -0,0 +1,201 @@
<template>
<div>
<n-card>
<n-form ref="formRef" :model="searchForm" label-placement="left" :show-feedback="false">
<n-flex justify="space-between">
<n-form-item class="!w-64" label="" path="name">
<n-input placeholder="搜索用户组" @input="filterName" />
</n-form-item>
<n-space>
<NFlex>
<NButton type="error" :disabled="!checkedKeys.length" secondary @click="deleteByList">
<template #icon>
<Icon name="ri:delete-bin-5-line"></Icon>
</template>
删除选中项
</NButton>
<NButton type="info" :disabled="!checkedKeys.length" secondary @click="allocationPolicy">
<template #icon>
<Icon name="ri:group-2-fill"></Icon>
</template>
分配策略
</NButton>
<NButton type="info" secondary @click="addUserGroup">
<template #icon>
<Icon name="ri:add-line"></Icon>
</template>
新增用户组
</NButton>
</NFlex>
</n-space>
</n-flex>
</n-form>
</n-card>
<n-data-table ref="tableRef" :columns="columns" :data="listData" :pagination="false" :bordered="false" max-height="calc(100vh - 320px)" :row-key="rowKey" @update:checked-row-keys="handleCheck" />
</div>
</template>
<script setup lang="ts">
import { type DataTableColumns, type DataTableInst, type DataTableRowKey, NButton, NPopconfirm, NSpace } from 'naive-ui';
import { Icon } from '#components';
// import { ChangePassword, EditItem, NewItem } from './components';
const { $api } = useNuxtApp();
const dialog = useDialog();
const message = useMessage();
const searchForm = reactive({
name: '',
});
interface RowData {
name: string;
}
const columns: DataTableColumns<RowData> = [
{
type: 'selection',
},
{
title: '名称',
align: 'left',
key: 'name',
filter(value, row) {
return !!row.name.includes(value.toString());
},
},
{
title: '操作',
key: 'actions',
width: 180,
render: (row: any) => {
return h(
NSpace,
{
justify: 'center',
},
{
default: () => [
h(
NButton,
{
type: 'info',
size: 'small',
secondary: true,
onClick: () => openEditItem(row),
},
{
default: () => '编辑',
icon: () => h(Icon, { name: 'ri:edit-2-line' }),
}
),
h(
NPopconfirm,
{ onPositiveClick: () => deleteItem(row) },
{
default: () => '确认删除',
trigger: () =>
h(
NButton,
{ type: 'error', size: 'small', secondary: true },
{
default: () => '删除',
icon: () => h(Icon, { name: 'ri:delete-bin-5-line' }),
}
),
}
),
],
}
);
},
},
];
// 搜索过滤
const tableRef = ref<DataTableInst>();
function filterName(value: string) {
tableRef.value &&
tableRef.value.filter({
name: [value],
});
}
const listData = ref<any[]>([]);
onMounted(() => {
getDataList();
});
// 获取数据
const getDataList = async () => {
try {
const res = await $api.get('/groups');
listData.value =
res.groups.map((item: string) => {
return {
name: item,
};
}) || [];
} catch (error) {
message.error('获取数据失败');
}
};
/** **********************************添加 */
const newItemRef = ref();
const newItemVisible = ref(false);
function addUserGroup() {
newItemVisible.value = true;
}
/** **********************************分配策略 */
const allocationPolicy = () => {};
/** **********************************修改 */
const editItemRef = ref();
function openEditItem(row: any) {
editItemRef.value.openDialog(row.accessKey);
}
/** ***********************************删除 */
async function deleteItem(row: any) {
try {
const res = await $api.delete(`/group/${encodeURIComponent(row.name)}`, {});
message.success('删除成功');
getDataList();
} catch (error) {
message.error('删除失败');
}
}
/** ************************************批量删除 */
function rowKey(row: any): string {
return row.name;
}
const checkedKeys = ref<DataTableRowKey[]>([]);
function handleCheck(keys: DataTableRowKey[]) {
checkedKeys.value = keys;
return checkedKeys;
}
function deleteByList() {
dialog.error({
title: '警告',
content: '你确定要删除所有选中的用户组吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
if (!checkedKeys.value.length) {
message.error('请至少选择一项');
return;
}
checkedKeys.value.forEach(async (element: any) => {
const res = await $api.delete(`/group/${encodeURIComponent(element)}`, {});
});
getDataList();
},
});
}
</script>
<style lang="scss" scoped></style>
+2
View File
@@ -0,0 +1,2 @@
export { default as userTab } from './userTab.vue';
export { default as groupTab } from './groupTab.vue';
+206
View File
@@ -0,0 +1,206 @@
<template>
<div>
<n-card>
<n-form ref="formRef" :model="searchForm" label-placement="left" :show-feedback="false">
<n-flex justify="space-between">
<n-form-item class="!w-64" label="" path="name">
<n-input placeholder="搜索访问用户" @input="filterName" />
</n-form-item>
<n-space>
<NFlex>
<NButton type="error" :disabled="true" secondary @click="deleteByList">
<template #icon>
<Icon name="ri:delete-bin-5-line"></Icon>
</template>
删除选中项
</NButton>
<NButton type="info" :disabled="true" secondary @click="addToGroup">
<template #icon>
<Icon name="ri:group-2-fill"></Icon>
</template>
添加到分组
</NButton>
<NButton type="info" secondary @click="addUserItem">
<template #icon>
<Icon name="ri:add-line"></Icon>
</template>
新增用户
</NButton>
</NFlex>
</n-space>
</n-flex>
</n-form>
</n-card>
<n-data-table ref="tableRef" :columns="columns" :data="listData" :pagination="false" :bordered="false" max-height="calc(100vh - 320px)" :row-key="rowKey" @update:checked-row-keys="handleCheck" />
</div>
</template>
<script setup lang="ts">
import { type DataTableColumns, type DataTableInst, type DataTableRowKey, NButton, NPopconfirm, NSpace } from 'naive-ui';
import { Icon } from '#components';
// import { ChangePassword, EditItem, NewItem } from './components';
const { $api } = useNuxtApp();
const dialog = useDialog();
const message = useMessage();
const searchForm = reactive({
name: '',
});
interface RowData {
name: string;
}
const columns: DataTableColumns<RowData> = [
{
type: 'selection',
},
{
title: '名称',
align: 'left',
key: 'name',
},
{
title: '操作',
key: 'actions',
width: 180,
render: (row: any) => {
return h(
NSpace,
{
justify: 'center',
},
{
default: () => [
h(
NButton,
{
type: 'info',
size: 'small',
secondary: true,
onClick: () => openEditItem(row),
},
{
default: () => '编辑',
icon: () => h(Icon, { name: 'ri:edit-2-line' }),
}
),
h(
NPopconfirm,
{ onPositiveClick: () => deleteItem(row) },
{
default: () => '确认删除',
trigger: () =>
h(
NButton,
{ type: 'error', size: 'small', secondary: true },
{
default: () => '删除',
icon: () => h(Icon, { name: 'ri:delete-bin-5-line' }),
}
),
}
),
],
}
);
},
},
];
// 搜索过滤
const tableRef = ref<DataTableInst>();
function filterName(value: string) {
tableRef.value &&
tableRef.value.filter({
name: [value],
});
}
const listData = ref<any[]>([]);
onMounted(() => {
getDataList();
});
// 获取数据
const getDataList = async () => {
try {
const res = await $api.get('service-accounts');
listData.value = res || [];
} catch (error) {
message.error('获取数据失败');
}
};
/** **********************************添加 */
const newItemRef = ref();
const newItemVisible = ref(false);
function addUserItem() {
newItemVisible.value = true;
}
/** **********************************添加到的分组 */
const addToGroup = () => {};
/** **********************************修改 */
const editItemRef = ref();
function openEditItem(row: any) {
editItemRef.value.openDialog(row.accessKey);
}
/** **********************************修改密码 */
const changePasswordModalRef = ref();
const changePasswordVisible = ref(false);
function changePassword() {
changePasswordVisible.value = true;
}
/** ***********************************删除 */
async function deleteItem(row: any) {
try {
const res = await $api.delete('/service-accounts/delete-multi', {
body: [row.accessKey],
});
message.success('删除成功');
getDataList();
} catch (error) {
message.error('删除失败');
}
}
/** ************************************批量删除 */
function rowKey(row: any): string {
return row.accessKey;
}
const checkedKeys = ref<DataTableRowKey[]>([]);
function handleCheck(keys: DataTableRowKey[]) {
checkedKeys.value = keys;
return checkedKeys;
}
function deleteByList() {
dialog.error({
title: '警告',
content: '你确定要删除所有选中的秘钥吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
if (!checkedKeys.value.length) {
message.error('请至少选择一项');
return;
}
try {
const res = await $api.delete('/service-accounts/delete-multi', {
body: checkedKeys.value,
});
message.success('删除成功');
getDataList();
} catch (error) {
message.error('删除失败');
}
},
});
}
</script>
<style lang="scss" scoped></style>