mirror of
https://github.com/rustfs/console.git
synced 2026-09-19 01:47:44 +08:00
feat : user group
This commit is contained in:
Vendored
+2
@@ -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']
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as newUser } from './newUser.vue';
|
||||
export { default as newGroup } from './newGroup.vue';
|
||||
@@ -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>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as userTab } from './userTab.vue';
|
||||
export { default as groupTab } from './groupTab.vue';
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user