mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-01-30 15:19:32 +08:00
commit
73750b1070
@ -1,5 +1,5 @@
|
||||
import { useDefineApi } from "@/stores/useDefineApi";
|
||||
import type { InstanceDetail, NodeStatus, Settings } from "@/types";
|
||||
import type { InstanceDetail, NodeStatus, Settings, UserInfo } from "@/types";
|
||||
import type { BaseUserInfo } from "@/types/user";
|
||||
import type { IPanelOverviewResponse } from "../../../../common/global";
|
||||
|
||||
@ -71,6 +71,48 @@ export const setSettingInfo = useDefineApi<
|
||||
method: "PUT"
|
||||
});
|
||||
|
||||
// 用户管理
|
||||
// 用户管理 获取信息
|
||||
export const getUserInfo = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
userName: string;
|
||||
page: number;
|
||||
page_size: number;
|
||||
};
|
||||
},
|
||||
{ total: number; pageSize: number; page: number; maxPage: number; data: UserInfo[] }
|
||||
>({
|
||||
url: "/api/auth/search",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
// 用户管理 删除用户
|
||||
export const deleteUser = useDefineApi<
|
||||
{
|
||||
data: string[];
|
||||
},
|
||||
any
|
||||
>({
|
||||
url: "/api/auth",
|
||||
method: "DELETE"
|
||||
});
|
||||
|
||||
// 用户管理 新增用户
|
||||
export const addUser = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
username: string;
|
||||
password: string;
|
||||
permission: number;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/auth",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 获取总览
|
||||
|
||||
// 获取设置信息
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import type { IGlobalInstanceConfig } from "./../../../common/global";
|
||||
import type { LayoutCardHeight } from "@/config/originLayoutConfig";
|
||||
|
||||
@ -85,3 +86,15 @@ export interface Settings {
|
||||
quickInstallAddr: string;
|
||||
redisUrl: string;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
uuid: string;
|
||||
userName: string;
|
||||
registerTime: string;
|
||||
permission: number;
|
||||
passWordType: number;
|
||||
loginTime: string;
|
||||
isInit: boolean;
|
||||
instances: any[];
|
||||
apiKey: string;
|
||||
}
|
||||
|
@ -1,103 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import CardPanel from "@/components/CardPanel.vue";
|
||||
import type { LayoutCard } from "@/types/index";
|
||||
import { ref, computed } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { DownOutlined, SearchOutlined, UserOutlined } from "@ant-design/icons-vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import { DownOutlined } from "@ant-design/icons-vue";
|
||||
import { throttle } from "lodash";
|
||||
|
||||
import CardPanel from "@/components/CardPanel.vue";
|
||||
import BetweenMenus from "@/components/BetweenMenus.vue";
|
||||
|
||||
import { useScreen } from "../hooks/useScreen";
|
||||
import { arrayFilter } from "../tools/array";
|
||||
import { useAppRouters } from "@/hooks/useAppRouters";
|
||||
import { getUserInfo, deleteUser as deleteUserApi, addUser as addUserApi } from "@/services/apis";
|
||||
|
||||
export interface UserInfo {
|
||||
key?: string | number;
|
||||
name: string;
|
||||
level: number;
|
||||
time: number;
|
||||
registerTime: number;
|
||||
}
|
||||
import type { LayoutCard, UserInfo } from "@/types/index";
|
||||
|
||||
defineProps<{
|
||||
card: LayoutCard;
|
||||
}>();
|
||||
|
||||
interface dataType {
|
||||
total: number;
|
||||
pageSize: number;
|
||||
page: number;
|
||||
maxPage: number;
|
||||
data: UserInfo[];
|
||||
}
|
||||
|
||||
const { execute } = getUserInfo();
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { getRouteParamsUrl, toPage } = useAppRouters();
|
||||
const screen = useScreen();
|
||||
|
||||
const operationForm = ref({
|
||||
name: ""
|
||||
name: "",
|
||||
currentPage: 1,
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
const handleToUserConfig = (user: UserInfo) => {
|
||||
console.log(user);
|
||||
toPage({
|
||||
path: "/users/config",
|
||||
query: {
|
||||
uuid: "XXXZZZ123"
|
||||
}
|
||||
});
|
||||
const permissionList = {
|
||||
"1": t("普通用户"),
|
||||
"10": t("管理员"),
|
||||
"-1": t("被封禁")
|
||||
};
|
||||
|
||||
const handleDeleteUser = (user: UserInfo) => {};
|
||||
|
||||
const dataSource: UserInfo[] = [
|
||||
{
|
||||
key: "1",
|
||||
name: "Admin",
|
||||
level: 10,
|
||||
time: new Date().getTime(),
|
||||
registerTime: new Date().getTime()
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
name: "Admin",
|
||||
level: 10,
|
||||
time: new Date().getTime(),
|
||||
registerTime: new Date().getTime()
|
||||
},
|
||||
{
|
||||
key: "3",
|
||||
name: "Admin",
|
||||
level: 10,
|
||||
time: new Date().getTime(),
|
||||
registerTime: new Date().getTime()
|
||||
},
|
||||
{
|
||||
key: "4",
|
||||
name: "Admin",
|
||||
level: 10,
|
||||
time: new Date().getTime(),
|
||||
registerTime: new Date().getTime()
|
||||
}
|
||||
];
|
||||
|
||||
const columns = computed(() => {
|
||||
return arrayFilter([
|
||||
{
|
||||
align: "center",
|
||||
title: "用户名",
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
title: t("用户名"),
|
||||
dataIndex: "userName",
|
||||
key: "userName",
|
||||
minWidth: "200px"
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: "角色",
|
||||
dataIndex: "level",
|
||||
key: "level",
|
||||
minWidth: "200px"
|
||||
title: t("权限"),
|
||||
dataIndex: "permission",
|
||||
key: "permission",
|
||||
minWidth: "200px",
|
||||
customRender: (e: { text: "1" | "10" | "-1" }) => {
|
||||
return permissionList[e.text] || e.text;
|
||||
}
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: "最后上线时间",
|
||||
dataIndex: "time",
|
||||
key: "time",
|
||||
title: t("最后登录时间"),
|
||||
dataIndex: "loginTime",
|
||||
key: "loginTime",
|
||||
minWidth: "200px",
|
||||
condition: () => !screen.isPhone.value
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: "注册时间",
|
||||
title: t("注册时间"),
|
||||
dataIndex: "registerTime",
|
||||
key: "registerTime",
|
||||
minWidth: "200px",
|
||||
@ -105,17 +82,187 @@ const columns = computed(() => {
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: "操作",
|
||||
title: t("操作"),
|
||||
key: "action",
|
||||
minWidth: "200px"
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
const rowSelection = () => {};
|
||||
const total = ref(0);
|
||||
const data = ref<dataType>();
|
||||
const dataSource = computed(() => data?.value?.data);
|
||||
const selectedUsers = ref<string[]>([]);
|
||||
|
||||
const handleToUserConfig = (user: any) => {
|
||||
toPage({
|
||||
path: "/users/config",
|
||||
query: {
|
||||
uuid: user.uuid
|
||||
}
|
||||
});
|
||||
};
|
||||
const fetchData = async () => {
|
||||
if (operationForm.value.currentPage < 1) {
|
||||
operationForm.value.currentPage = 1;
|
||||
}
|
||||
const res = await execute({
|
||||
params: {
|
||||
userName: operationForm.value.name,
|
||||
page: operationForm.value.currentPage,
|
||||
page_size: operationForm.value.pageSize
|
||||
}
|
||||
});
|
||||
data.value = res.value!;
|
||||
total.value = res.value?.total ?? 0;
|
||||
};
|
||||
|
||||
const reload = throttle(() => {
|
||||
fetchData();
|
||||
}, 2000);
|
||||
|
||||
const deleteUser = async (userList: string[]) => {
|
||||
try {
|
||||
const { execute } = deleteUserApi();
|
||||
await execute({
|
||||
data: userList
|
||||
});
|
||||
} catch (error: any) {
|
||||
message.error(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteUser = async (user: UserInfo) => {
|
||||
await deleteUser([user.uuid]);
|
||||
};
|
||||
|
||||
const handleBatchDelete = async () => {
|
||||
if (selectedUsers.value.length === 0) {
|
||||
return message.warn(t("请选择要删除的用户"));
|
||||
}
|
||||
await deleteUser(selectedUsers.value);
|
||||
};
|
||||
|
||||
const { execute: addUserExecute, isLoading: addUserIsLoading } = addUserApi();
|
||||
|
||||
const newUserDialog = ref({
|
||||
status: false,
|
||||
title: t("新增用户"),
|
||||
permissionList: [
|
||||
{
|
||||
lable: t("普通权限"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
lable: t("最高权限"),
|
||||
value: 10
|
||||
}
|
||||
],
|
||||
data: {
|
||||
username: "",
|
||||
password: "",
|
||||
permission: 1
|
||||
},
|
||||
show: () => {
|
||||
newUserDialog.value.status = true;
|
||||
},
|
||||
hidden: () => {
|
||||
newUserDialog.value.clear();
|
||||
newUserDialog.value.status = false;
|
||||
},
|
||||
clear: () => {
|
||||
newUserDialog.value.data = {
|
||||
username: "",
|
||||
password: "",
|
||||
permission: 1
|
||||
};
|
||||
},
|
||||
resolve: async () => {
|
||||
if (newUserDialog.value.data.username == "" || newUserDialog.value.data.password == "")
|
||||
return message.error(t("请正确填写表单"));
|
||||
|
||||
try {
|
||||
await addUserExecute({
|
||||
data: newUserDialog.value.data
|
||||
});
|
||||
message.success(t("新增用户成功"));
|
||||
newUserDialog.value.hidden();
|
||||
} catch (error: any) {
|
||||
message.error(t("新增用户失败:") + error.message);
|
||||
}
|
||||
fetchData();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
fetchData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="new">
|
||||
<a-modal
|
||||
v-model:open="newUserDialog.status"
|
||||
destroy-on-close="true"
|
||||
:title="newUserDialog.title"
|
||||
:confirm-loading="addUserIsLoading"
|
||||
@ok="newUserDialog.resolve()"
|
||||
>
|
||||
<div class="mb-20">
|
||||
<a-typography-title :level="5">{{ t("用户名") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("必填,6 到 12 个字符,支持中文,英文和字符") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input v-model:value="newUserDialog.data.username" :placeholder="t('请输入内容')" />
|
||||
</div>
|
||||
|
||||
<div class="mb-20">
|
||||
<a-typography-title :level="5">{{ t("用户密码") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("必填,9 到 36 个字符,必须包含大小写字母和数字") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input v-model:value="newUserDialog.data.password" :placeholder="t('请输入内容')" />
|
||||
</div>
|
||||
|
||||
<div class="mb-20">
|
||||
<a-typography-title :level="5">{{ t("权限") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("普通权限适用于商业用户,最高权限适用于管理人员") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-select v-model:value="newUserDialog.data.permission" style="max-width: 320px">
|
||||
<a-select-option
|
||||
v-for="item in newUserDialog.permissionList"
|
||||
:key="item"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.lable }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
|
||||
<div class="mb-20">
|
||||
<a-typography-title :level="5">{{ t("注意事项") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{
|
||||
t(
|
||||
"若您从事出租商业活动,请务必保证应用实例运行在 Linux 的 Docker 虚拟容器中,否则将有安全隐患。"
|
||||
)
|
||||
}}
|
||||
<br />
|
||||
<a href="https://docs.mcsmanager.com/" target="_blank">具体信息参考</a>
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
|
||||
<div style="height: 100%" class="container">
|
||||
<a-row :gutter="[24, 24]" style="height: 100%">
|
||||
<a-col :span="24">
|
||||
@ -129,20 +276,29 @@ const rowSelection = () => {};
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1">新增用户</a-menu-item>
|
||||
<a-menu-item key="2">删除用户</a-menu-item>
|
||||
<a-menu-item key="3">封禁用户</a-menu-item>
|
||||
<a-menu-item key="1" @click="newUserDialog.show()">
|
||||
{{ t("新增用户") }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="handleBatchDelete()">
|
||||
{{ t("删除用户") }}
|
||||
</a-menu-item>
|
||||
<!-- <a-menu-item key="3">{{ t("封禁用户") }}</a-menu-item> -->
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="primary">
|
||||
用户操作
|
||||
{{ t("用户操作") }}
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template #center>
|
||||
<div class="search-input">
|
||||
<a-input v-model:value="operationForm.name" :placeholder="t('根据用户名搜索')">
|
||||
<a-input
|
||||
v-model:value="operationForm.name"
|
||||
:placeholder="t('根据用户名搜索')"
|
||||
@change="reload()"
|
||||
@press-enter="fetchData()"
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined />
|
||||
</template>
|
||||
@ -155,7 +311,19 @@ const rowSelection = () => {};
|
||||
<a-col :span="24">
|
||||
<CardPanel style="height: 100%">
|
||||
<template #body>
|
||||
<a-table :row-selection="rowSelection" :data-source="dataSource" :columns="columns">
|
||||
<a-table
|
||||
:row-selection="{
|
||||
selectedRowKeys: selectedUsers,
|
||||
onChange: (selectedRowKeys: string[], selectedRows: UserInfo[]) => {
|
||||
selectedUsers = selectedRowKeys;
|
||||
}
|
||||
}"
|
||||
:data-source="dataSource"
|
||||
:columns="columns"
|
||||
:pagination="false"
|
||||
:preserve-selected-row-keys="true"
|
||||
:row-key="(record: UserInfo) => record.uuid"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-dropdown>
|
||||
@ -167,6 +335,9 @@ const rowSelection = () => {};
|
||||
<a-menu-item key="2" @click="handleDeleteUser(record)">
|
||||
{{ t("删除用户") }}
|
||||
</a-menu-item>
|
||||
<!-- <a-menu-item key="3">
|
||||
{{ t("封禁用户") }}
|
||||
</a-menu-item> -->
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
@ -177,6 +348,15 @@ const rowSelection = () => {};
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="flex justify-end mt-24">
|
||||
<a-pagination
|
||||
v-model:current="operationForm.currentPage"
|
||||
v-model:pageSize="operationForm.pageSize"
|
||||
:total="total"
|
||||
show-size-changer
|
||||
@change="fetchData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</CardPanel>
|
||||
</a-col>
|
||||
@ -189,17 +369,13 @@ const rowSelection = () => {};
|
||||
transition: all 0.4s;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.search-input {
|
||||
transition: all 0.4s;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.search-input:hover {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user