mirror of
https://github.com/MCSManager/MCSManager.git
synced 2024-12-21 07:49:08 +08:00
Feat: edit user info
This commit is contained in:
parent
6f41802c9a
commit
994d540b9e
@ -1,6 +1,6 @@
|
||||
import { useDefineApi } from "@/stores/useDefineApi";
|
||||
import type { InstanceDetail, NodeStatus, Settings } from "@/types";
|
||||
import type { BaseUserInfo, UserInstance } from "@/types/user";
|
||||
import type { BaseUserInfo, LoginUserInfo, EditUserInfo, UserInstance } from "@/types/user";
|
||||
import type { IPanelOverviewResponse } from "../../../../common/global";
|
||||
|
||||
export const panelInstall = useDefineApi<
|
||||
@ -63,7 +63,7 @@ export const logoutUser = useDefineApi<any, any>({
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
export const userInfoApi = useDefineApi<any, BaseUserInfo>({
|
||||
export const userInfoApi = useDefineApi<any, LoginUserInfo>({
|
||||
url: "/api/auth/"
|
||||
});
|
||||
|
||||
@ -155,6 +155,19 @@ export const addUser = useDefineApi<
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
export const editUserInfo = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
config: EditUserInfo;
|
||||
uuid: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/auth",
|
||||
method: "PUT"
|
||||
});
|
||||
|
||||
export const updateUserInstance = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
|
@ -2,10 +2,10 @@ import { computed, reactive } from "vue";
|
||||
import { createGlobalState } from "@vueuse/core";
|
||||
import _ from "lodash";
|
||||
import { userInfoApi } from "@/services/apis";
|
||||
import type { BaseUserInfo } from "@/types/user";
|
||||
import type { LoginUserInfo } from "@/types/user";
|
||||
|
||||
interface AppStateInfo {
|
||||
userInfo: BaseUserInfo | null;
|
||||
userInfo: LoginUserInfo | null;
|
||||
language: string;
|
||||
isInstall: boolean;
|
||||
versionChanged: boolean;
|
||||
@ -28,7 +28,7 @@ export const useAppStateStore = createGlobalState(() => {
|
||||
|
||||
const isAdmin = computed(() => state.userInfo?.permission === 10);
|
||||
|
||||
const updateUserInfo = async (userInfo?: BaseUserInfo) => {
|
||||
const updateUserInfo = async (userInfo?: LoginUserInfo) => {
|
||||
if (userInfo) {
|
||||
state.userInfo = userInfo;
|
||||
} else {
|
||||
|
@ -13,7 +13,14 @@ export interface BaseUserInfo {
|
||||
registerTime: string;
|
||||
instances: UserInstance[];
|
||||
permission: number;
|
||||
token: string;
|
||||
apiKey: string;
|
||||
isInit: boolean;
|
||||
}
|
||||
|
||||
export interface EditUserInfo extends BaseUserInfo {
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export interface LoginUserInfo extends BaseUserInfo {
|
||||
token: string;
|
||||
}
|
||||
|
@ -1,17 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { message } from "ant-design-vue";
|
||||
import { message, type FormInstance } from "ant-design-vue";
|
||||
import { DownOutlined, UserOutlined, SearchOutlined } from "@ant-design/icons-vue";
|
||||
import type { Rule } from "ant-design-vue/es/form";
|
||||
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";
|
||||
import {
|
||||
getUserInfo,
|
||||
deleteUser as deleteUserApi,
|
||||
addUser as addUserApi,
|
||||
editUserInfo
|
||||
} from "@/services/apis";
|
||||
import type { LayoutCard } from "@/types/index";
|
||||
import type { BaseUserInfo } from "@/types/user";
|
||||
import type { BaseUserInfo, EditUserInfo } from "@/types/user";
|
||||
import _ from "lodash";
|
||||
|
||||
defineProps<{
|
||||
card: LayoutCard;
|
||||
@ -90,12 +97,6 @@ const data = ref<dataType>();
|
||||
const dataSource = computed(() => data?.value?.data);
|
||||
const selectedUsers = ref<string[]>([]);
|
||||
|
||||
const handleEditUserConfig = (user: BaseUserInfo) => {
|
||||
console.log(user);
|
||||
|
||||
// TXT_CODE_dbc9f7b2
|
||||
};
|
||||
|
||||
const handleToUserResources = (user: BaseUserInfo) => {
|
||||
toPage({
|
||||
path: "/users/resources",
|
||||
@ -155,116 +156,180 @@ const handleBatchDelete = async () => {
|
||||
await deleteUser(selectedUsers.value);
|
||||
};
|
||||
|
||||
const { execute: addUserExecute, isLoading: addUserIsLoading } = addUserApi();
|
||||
|
||||
const newUserDialog = ref({
|
||||
const isAddMode = ref(true);
|
||||
const userDialog = ref({
|
||||
status: false,
|
||||
title: t("TXT_CODE_e83ffa03"),
|
||||
permissionList: [
|
||||
{
|
||||
lable: t("TXT_CODE_a778451f"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
lable: t("TXT_CODE_b438b517"),
|
||||
value: 10
|
||||
}
|
||||
],
|
||||
data: {
|
||||
username: "",
|
||||
password: "",
|
||||
permission: 1
|
||||
},
|
||||
confirmBtnLoading: false,
|
||||
show: () => {
|
||||
newUserDialog.value.status = true;
|
||||
},
|
||||
hidden: () => {
|
||||
newUserDialog.value.clear();
|
||||
newUserDialog.value.status = false;
|
||||
},
|
||||
clear: () => {
|
||||
newUserDialog.value.data = {
|
||||
username: "",
|
||||
password: "",
|
||||
permission: 1
|
||||
};
|
||||
userDialog.value.status = true;
|
||||
},
|
||||
resolve: async () => {
|
||||
if (newUserDialog.value.data.username == "" || newUserDialog.value.data.password == "")
|
||||
return message.error(t("TXT_CODE_633415e2"));
|
||||
|
||||
try {
|
||||
await addUserExecute({
|
||||
data: newUserDialog.value.data
|
||||
});
|
||||
message.success(t("TXT_CODE_c855fc29"));
|
||||
newUserDialog.value.hidden();
|
||||
await formRef.value?.validateFields();
|
||||
userDialog.value.confirmBtnLoading = true;
|
||||
if (isAddMode.value) {
|
||||
await addUserApi().execute({
|
||||
data: {
|
||||
username: formData.value.userName,
|
||||
password: formData.value.password!,
|
||||
permission: formData.value.permission
|
||||
}
|
||||
});
|
||||
message.success(t("TXT_CODE_c855fc29"));
|
||||
} else {
|
||||
await editUserInfo().execute({
|
||||
data: {
|
||||
config: formData.value,
|
||||
uuid: formData.value.uuid
|
||||
}
|
||||
});
|
||||
message.success(t("编辑成功"));
|
||||
}
|
||||
userDialog.value.status = false;
|
||||
formData.value = _.cloneDeep(formDataOrigin);
|
||||
} catch (error: any) {
|
||||
message.error(t("TXT_CODE_5246d704") + error.message);
|
||||
return message.error(error.message);
|
||||
} finally {
|
||||
fetchData();
|
||||
userDialog.value.confirmBtnLoading = false;
|
||||
}
|
||||
fetchData();
|
||||
}
|
||||
});
|
||||
|
||||
const formDataOrigin: EditUserInfo = {
|
||||
uuid: "",
|
||||
userName: "",
|
||||
password: "",
|
||||
loginTime: "",
|
||||
registerTime: "",
|
||||
instances: [],
|
||||
permission: 1,
|
||||
apiKey: "",
|
||||
isInit: false
|
||||
};
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const formData = ref<EditUserInfo>(_.cloneDeep(formDataOrigin));
|
||||
const baseRules: Record<string, Rule[]> = {
|
||||
userName: [
|
||||
{ required: true, message: t("请输入用户名") },
|
||||
{ min: 3, max: 12, message: t("长度只能 3 ~ 12 个字符"), trigger: "blur" }
|
||||
],
|
||||
permission: [{ required: true, message: t("TXT_CODE_3bb646e4") }]
|
||||
};
|
||||
const addUserRules: Record<string, Rule[]> = {
|
||||
...baseRules,
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: t("请输入密码")
|
||||
},
|
||||
{
|
||||
min: 9,
|
||||
max: 36,
|
||||
validator: async (_rule: Rule, value: string) => {
|
||||
if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[0-9A-Za-z]{12,}$/.test(value))
|
||||
throw new Error(t("9 到 36 个字符,必须包含大小写字母和数字"));
|
||||
},
|
||||
trigger: "blur"
|
||||
}
|
||||
]
|
||||
};
|
||||
const editUserRules: Record<string, Rule[]> = {
|
||||
...baseRules,
|
||||
password: [
|
||||
{
|
||||
required: false
|
||||
},
|
||||
{
|
||||
min: 9,
|
||||
max: 36,
|
||||
validator: async (_rule: Rule, value: string) => {
|
||||
if (value && !/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[0-9A-Za-z]{12,}$/.test(value))
|
||||
throw new Error(t("9 到 36 个字符,必须包含大小写字母和数字"));
|
||||
},
|
||||
trigger: "blur"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Add user
|
||||
const handleAddUser = async () => {
|
||||
userDialog.value.title = t("TXT_CODE_e83ffa03");
|
||||
formData.value = _.cloneDeep(formDataOrigin);
|
||||
isAddMode.value = true;
|
||||
userDialog.value.show();
|
||||
};
|
||||
|
||||
// Edit user
|
||||
const handleEditUser = (user: BaseUserInfo) => {
|
||||
userDialog.value.title = t("编辑用户");
|
||||
formData.value = _.cloneDeep(user);
|
||||
isAddMode.value = false;
|
||||
userDialog.value.show();
|
||||
};
|
||||
|
||||
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()"
|
||||
<a-modal
|
||||
v-model:open="userDialog.status"
|
||||
centered
|
||||
:destroy-on-close="true"
|
||||
:title="userDialog.title"
|
||||
:confirm-loading="userDialog.confirmBtnLoading"
|
||||
@ok="userDialog.resolve()"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:rules="isAddMode ? addUserRules : editUserRules"
|
||||
:model="formData"
|
||||
layout="vertical"
|
||||
>
|
||||
<div class="mb-20">
|
||||
<a-form-item name="userName">
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_eb9fcdad") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_1987587b") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input
|
||||
v-model:value="newUserDialog.data.username"
|
||||
:placeholder="t('TXT_CODE_4ea93630')"
|
||||
/>
|
||||
</div>
|
||||
<a-input v-model:value="formData.userName" :placeholder="t('TXT_CODE_4ea93630')" />
|
||||
</a-form-item>
|
||||
|
||||
<div class="mb-20">
|
||||
<a-form-item name="password">
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_5c605130") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_1f2062c7") }}
|
||||
{{ !isAddMode ? t("不填写则不更变原有值") : t("TXT_CODE_1f2062c7") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input
|
||||
v-model:value="newUserDialog.data.password"
|
||||
:placeholder="t('TXT_CODE_4ea93630')"
|
||||
/>
|
||||
</div>
|
||||
<a-input v-model:value="formData.password" :placeholder="t('TXT_CODE_4ea93630')" />
|
||||
</a-form-item>
|
||||
|
||||
<div class="mb-20">
|
||||
<a-form-item name="permission">
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_511aea70") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_21b8b71a") }}
|
||||
</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 v-model:value="formData.permission">
|
||||
<a-select-option v-for="(item, key, i) in permissionList" :key="i" :value="Number(key)">
|
||||
{{ item }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<div class="mb-20">
|
||||
<a-form-item v-if="!isAddMode">
|
||||
<a-typography-title :level="5">APIKEY</a-typography-title>
|
||||
<a-input v-model:value="formData.apiKey" :placeholder="t('TXT_CODE_d7dbc7c2')" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="isAddMode">
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_ef0ce2e") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
@ -275,9 +340,9 @@ onMounted(async () => {
|
||||
</a>
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<div style="height: 100%" class="container">
|
||||
<a-row :gutter="[24, 24]" style="height: 100%">
|
||||
@ -293,7 +358,7 @@ onMounted(async () => {
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="newUserDialog.show()">
|
||||
<a-menu-item key="1" @click="handleAddUser">
|
||||
{{ t("TXT_CODE_e83ffa03") }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="handleBatchDelete()">
|
||||
@ -352,7 +417,7 @@ onMounted(async () => {
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="handleEditUserConfig(record)">
|
||||
<a-menu-item key="1" @click="handleEditUser(record)">
|
||||
{{ t("TXT_CODE_236f70aa") }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="handleToUserResources(record)">
|
||||
|
@ -235,7 +235,7 @@
|
||||
"TXT_CODE_175b570d": "starting",
|
||||
"TXT_CODE_181f2f08": "Unable to get remote node information",
|
||||
"TXT_CODE_190ecd56": "Linux load",
|
||||
"TXT_CODE_1987587b": "Required, 6 to 12 characters, supports Chinese, English and characters",
|
||||
"TXT_CODE_1987587b": "Required, 3 to 12 characters, supports Chinese, English and characters",
|
||||
"TXT_CODE_1d67c9c6": "Limit on the number of logins with the same IP",
|
||||
"TXT_CODE_1d9d0746": "User ID",
|
||||
"TXT_CODE_1deaa2dd": "user",
|
||||
@ -1325,7 +1325,6 @@
|
||||
"TXT_CODE_da8bb476": "Plug-in update folder, put the folder name of the new version of the plug-in, this folder will automatically update the plug-in when the server restarts",
|
||||
"TXT_CODE_db04e635": "When there is only one default server, the server will be shown to the player's Motd. \nThis has no effect when ping_passthrough is enabled",
|
||||
"TXT_CODE_db815f4a": "Text filtering rules configuration file (may not work properly)",
|
||||
"TXT_CODE_dbc9f7b2": "User permission settings",
|
||||
"TXT_CODE_dc570cf2": "Leave blank to make no changes",
|
||||
"TXT_CODE_dce87e42": "Filling in 50 means that the usage of all cores is limited to 50%, and filling in 200 means that the total usage of all cores is allowed to be 200%.",
|
||||
"TXT_CODE_dd002fae": "Whether to enable whitelist",
|
||||
|
@ -447,7 +447,7 @@
|
||||
"TXT_CODE_b438b517": "最高权限",
|
||||
"TXT_CODE_c855fc29": "新增用户成功",
|
||||
"TXT_CODE_5246d704": "新增用户失败:",
|
||||
"TXT_CODE_1987587b": "必填,6 到 12 个字符,支持任何语言",
|
||||
"TXT_CODE_1987587b": "必填,3 到 12 个字符,支持任何语言",
|
||||
"TXT_CODE_5c605130": "用户密码",
|
||||
"TXT_CODE_1f2062c7": "必填,9 到 36 个字符,必须包含大小写字母和数字",
|
||||
"TXT_CODE_21b8b71a": "普通权限适用于商业用户,最高权限适用于管理人员",
|
||||
@ -1475,7 +1475,6 @@
|
||||
"TXT_CODE_ecf17071": "暂时不可使用的实例数",
|
||||
"TXT_CODE_1c45f7fe": "编辑服务端配置文件",
|
||||
"TXT_CODE_97d17cce": "用户列表",
|
||||
"TXT_CODE_dbc9f7b2": "用户权限设定",
|
||||
"TXT_CODE_3fe97dcc": "系统设置",
|
||||
"TXT_CODE_7411336e": "实例总计",
|
||||
"TXT_CODE_d655beec": "实例列表",
|
||||
|
Loading…
Reference in New Issue
Block a user