新增管理员密码重置
This commit is contained in:
parent
ece7c82a80
commit
94958d1c83
@ -6,7 +6,7 @@ import type { RunTimeLayoutConfig } from '@umijs/max';
|
|||||||
import { history, Link, request as q } from '@umijs/max';
|
import { history, Link, request as q } from '@umijs/max';
|
||||||
import defaultSettings from '../config/defaultSettings';
|
import defaultSettings from '../config/defaultSettings';
|
||||||
import { errorConfig } from './requestErrorConfig';
|
import { errorConfig } from './requestErrorConfig';
|
||||||
import { GetUserInfo, getCurrentUser as queryCurrentUser } from './services/services/user';
|
import { UserInfo, getCurrentUser as queryCurrentUser } from './services/services/user';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { TokenStorage } from './services/define/tokenStorage';
|
import { TokenStorage } from './services/define/tokenStorage';
|
||||||
import { App, ConfigProvider } from 'antd';
|
import { App, ConfigProvider } from 'antd';
|
||||||
@ -46,7 +46,7 @@ export async function getInitialState(): Promise<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const GetUsrInfo = async (id: number) => {
|
const GetUsrInfo = async (id: number) => {
|
||||||
const userInfo = await GetUserInfo(id);
|
const userInfo = await UserInfo.GetUserInfo(id);
|
||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
src/components/Icon/DiceIcon.tsx
Normal file
15
src/components/Icon/DiceIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon';
|
||||||
|
|
||||||
|
const DiceIcon: React.FC<AntdIconProps> = (props) => {
|
||||||
|
let ico = <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" width="1.5em"
|
||||||
|
height="1.5em" ><path d="M440.88 129.37L288.16 40.62a64.14 64.14 0 0 0-64.33 0L71.12 129.37a4 4 0 0 0 0 6.9L254 243.85a4 4 0 0 0 4.06 0L440.9 136.27a4 4 0 0 0-.02-6.9zM256 152c-13.25 0-24-7.16-24-16s10.75-16 24-16s24 7.16 24 16s-10.75 16-24 16z" fill="currentColor"></path><path d="M238 270.81L54 163.48a4 4 0 0 0-6 3.46v173.92a48 48 0 0 0 23.84 41.39L234 479.48a4 4 0 0 0 6-3.46V274.27a4 4 0 0 0-2-3.46zM96 368c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm96-32c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24z" fill="currentColor"></path><path d="M458 163.51L274 271.56a4 4 0 0 0-2 3.45V476a4 4 0 0 0 6 3.46l162.15-97.23A48 48 0 0 0 464 340.86V167a4 4 0 0 0-6-3.49zM320 424c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm96 32c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24z" fill="currentColor"></path></svg>;
|
||||||
|
return (
|
||||||
|
<Icon {...props} component={() => (
|
||||||
|
ico
|
||||||
|
)} />
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default DiceIcon;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import TemplateContainer from '@/pages/TemplateContainer';
|
import TemplateContainer from '@/pages/TemplateContainer';
|
||||||
import { GetUserAgentInfo, GetUserInfo } from '@/services/services/user';
|
import { UserInfo } from '@/services/services/user';
|
||||||
import { RedoOutlined } from '@ant-design/icons';
|
import { RedoOutlined } from '@ant-design/icons';
|
||||||
import { useModel } from '@umijs/max';
|
import { useModel } from '@umijs/max';
|
||||||
import { Avatar, Badge, Button, Card, Col, FloatButton, Input, message, Modal, Row, Spin, Tag } from 'antd';
|
import { Avatar, Badge, Button, Card, Col, FloatButton, Input, message, Modal, Row, Spin, Tag } from 'antd';
|
||||||
@ -23,10 +23,10 @@ const UserCenter: React.FC = () => {
|
|||||||
// 初始化加载用户信息
|
// 初始化加载用户信息
|
||||||
setTopSpinning(true);
|
setTopSpinning(true);
|
||||||
setTopSpinTip("正在获取用户信息。。。");
|
setTopSpinTip("正在获取用户信息。。。");
|
||||||
GetUserInfo(initialState?.currentUser?.id).then(async (res) => {
|
UserInfo.GetUserInfo(initialState?.currentUser?.id).then(async (res) => {
|
||||||
setInitialState({ ...initialState, currentUser: res });
|
setInitialState({ ...initialState, currentUser: res });
|
||||||
localStorage.setItem('userInfo', JSON.stringify(res));
|
localStorage.setItem('userInfo', JSON.stringify(res));
|
||||||
let agentInfo = await GetUserAgentInfo();
|
let agentInfo = await UserInfo.GetUserAgentInfo();
|
||||||
setUserAgentUserInfo(agentInfo);
|
setUserAgentUserInfo(agentInfo);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
messageApi.error(error.message);
|
messageApi.error(error.message);
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import renderTitle from '../UserRenderList';
|
|||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { useModel } from '@umijs/max';
|
import { useModel } from '@umijs/max';
|
||||||
import { useSoftStore } from '@/store/software';
|
import { useSoftStore } from '@/store/software';
|
||||||
import { EnableAgent, GetUserAgentInfo, GetUserInfo } from '@/services/services/user';
|
import { UserInfo } from '@/services/services/user';
|
||||||
|
|
||||||
type UserCenterAgentMessageProps = {
|
type UserCenterAgentMessageProps = {
|
||||||
userAgentUserInfo: UserModel.UserAgentInfo | undefined;
|
userAgentUserInfo: UserModel.UserAgentInfo | undefined;
|
||||||
@ -28,15 +28,15 @@ const UserCenterAgentMessage: React.FC<UserCenterAgentMessageProps> = ({ userAge
|
|||||||
setTopSpinTip("正在启用代理。。。");
|
setTopSpinTip("正在启用代理。。。");
|
||||||
// 开始调用启用代理的接口
|
// 开始调用启用代理的接口
|
||||||
try {
|
try {
|
||||||
await EnableAgent();
|
await UserInfo.EnableAgent();
|
||||||
messageApi.success("启用代理成功");
|
messageApi.success("启用代理成功");
|
||||||
// 冲i性能加载用户信息
|
// 冲i性能加载用户信息
|
||||||
if (initialState?.currentUser?.id) {
|
if (initialState?.currentUser?.id) {
|
||||||
let res = await GetUserInfo(initialState?.currentUser?.id);
|
let res = await UserInfo.GetUserInfo(initialState?.currentUser?.id);
|
||||||
localStorage.setItem('userInfo', JSON.stringify(res));
|
localStorage.setItem('userInfo', JSON.stringify(res));
|
||||||
setInitialState({ ...initialState, currentUser: res });
|
setInitialState({ ...initialState, currentUser: res });
|
||||||
// 重新加载代理信息
|
// 重新加载代理信息
|
||||||
let agentInfo = await GetUserAgentInfo();
|
let agentInfo = await UserInfo.GetUserAgentInfo();
|
||||||
setUserAgentUserInfo(agentInfo);
|
setUserAgentUserInfo(agentInfo);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { QueryRoleOption } from '@/services/services/role';
|
import { QueryRoleOption } from '@/services/services/role';
|
||||||
import { GetUserInfo, UpdatedUserInfo } from '@/services/services/user';
|
import { UserInfo } from '@/services/services/user';
|
||||||
import { FormatDate } from '@/util/time';
|
import { FormatDate } from '@/util/time';
|
||||||
import { useModel } from '@umijs/max';
|
import { useModel } from '@umijs/max';
|
||||||
import { Button, Col, Form, FormInstance, Input, InputNumber, message, Row, Select, SelectProps, Spin, Tag } from 'antd';
|
import { Button, Col, Form, FormInstance, Input, InputNumber, message, Row, Select, SelectProps, Spin, Tag } from 'antd';
|
||||||
@ -38,7 +38,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
|
|
||||||
GetUserInfo(userId).then((res) => {
|
UserInfo.GetUserInfo(userId).then((res) => {
|
||||||
let tempRes = {
|
let tempRes = {
|
||||||
...res,
|
...res,
|
||||||
createdDate: FormatDate(res.createdDate)
|
createdDate: FormatDate(res.createdDate)
|
||||||
@ -55,7 +55,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setSpinTip("修改中...");
|
setSpinTip("修改中...");
|
||||||
try {
|
try {
|
||||||
await UpdatedUserInfo(values);
|
await UserInfo.UpdatedUserInfo(values);
|
||||||
messageApi.success("用户修改成功");
|
messageApi.success("用户修改成功");
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
messageApi.error(error.message);
|
messageApi.error(error.message);
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Form, Input, Button, message, Tooltip } from 'antd';
|
||||||
|
import DiceIcon from '@/components/Icon/DiceIcon';
|
||||||
|
import { generateRandomPassword } from '@/util/password';
|
||||||
|
import { set, toArray } from 'lodash';
|
||||||
|
import { useSoftStore } from '@/store/software';
|
||||||
|
import { UserInfo } from '@/services/services/user';
|
||||||
|
|
||||||
|
interface ResetUserPasswordProps {
|
||||||
|
userId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResetUserPassword: React.FC<ResetUserPasswordProps> = ({ userId }) => {
|
||||||
|
const [newPassword, setNewPassword] = useState<string>('');
|
||||||
|
const [messageApi, messageHolder] = message.useMessage();
|
||||||
|
const { setTopSpinning, setTopSpinTip } = useSoftStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '16px' }}>
|
||||||
|
<Input
|
||||||
|
value={newPassword}
|
||||||
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
|
placeholder="请输入新密码"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<DiceIcon />}
|
||||||
|
color="primary"
|
||||||
|
variant="filled"
|
||||||
|
onClick={() => {
|
||||||
|
let newPw = generateRandomPassword();
|
||||||
|
console.log(newPw);
|
||||||
|
setNewPassword(newPw);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tooltip title="确认重置用户密码">
|
||||||
|
<Button type="primary" onClick={async () => {
|
||||||
|
setTopSpinning(true);
|
||||||
|
setTopSpinTip('正在重置密码。。。');
|
||||||
|
// 调用重置密码的方法
|
||||||
|
try {
|
||||||
|
await UserInfo.ResetUserPassword(userId, newPassword);
|
||||||
|
} catch (error: any) {
|
||||||
|
messageApi.error('密码重置失败, ' + error.message);
|
||||||
|
} finally {
|
||||||
|
setTopSpinning(false);
|
||||||
|
}
|
||||||
|
messageApi.success('密码重置成功 ' + newPassword);
|
||||||
|
// setPasswordModel(false);
|
||||||
|
}}>
|
||||||
|
确认重置
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
{messageHolder}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResetUserPassword;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { useFormReset } from "@/hooks/useFormReset";
|
import { useFormReset } from "@/hooks/useFormReset";
|
||||||
import TemplateContainer from "@/pages/TemplateContainer";
|
import TemplateContainer from "@/pages/TemplateContainer";
|
||||||
import { QueryRoleOption } from "@/services/services/role";
|
import { QueryRoleOption } from "@/services/services/role";
|
||||||
import { QueryUserList } from "@/services/services/user";
|
import { UserInfo } from "@/services/services/user";
|
||||||
import { FormatDate } from "@/util/time";
|
import { FormatDate } from "@/util/time";
|
||||||
import { useAccess, useModel } from "@umijs/max";
|
import { useAccess, useModel } from "@umijs/max";
|
||||||
import { Button, Dropdown, Form, Input, InputNumber, message, Modal, Select, SelectProps, Table, Tag, Tooltip } from "antd";
|
import { Button, Dropdown, Form, Input, InputNumber, message, Modal, Select, SelectProps, Table, Tag, Tooltip } from "antd";
|
||||||
@ -9,8 +9,12 @@ import { ColumnsType, TablePaginationConfig } from "antd/es/table";
|
|||||||
import { FilterValue, SorterResult, TableCurrentDataSource } from "antd/es/table/interface";
|
import { FilterValue, SorterResult, TableCurrentDataSource } from "antd/es/table/interface";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ModifyUser from "../ModifyUser";
|
import ModifyUser from "../ModifyUser";
|
||||||
import { DeleteOutlined, EditOutlined, MenuOutlined, SyncOutlined } from "@ant-design/icons";
|
import Icon, { DeleteOutlined, EditOutlined, KeyOutlined, MenuOutlined, SyncOutlined } from "@ant-design/icons";
|
||||||
import { SoftwareControl } from "@/services/services/software";
|
import { SoftwareControl } from "@/services/services/software";
|
||||||
|
import { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon";
|
||||||
|
import DiceIcon from "@/components/Icon/DiceIcon";
|
||||||
|
import { generateRandomPassword } from "@/util/password";
|
||||||
|
import ResetUserPassword from "./ResetUserPassword";
|
||||||
|
|
||||||
const UserManagement: React.FC = () => {
|
const UserManagement: React.FC = () => {
|
||||||
type TagRender = SelectProps['tagRender'];
|
type TagRender = SelectProps['tagRender'];
|
||||||
@ -33,6 +37,7 @@ const UserManagement: React.FC = () => {
|
|||||||
const [userId, setUserId] = useState<number>(0);
|
const [userId, setUserId] = useState<number>(0);
|
||||||
const [openModal, setOpenModal] = useState<boolean>(false);
|
const [openModal, setOpenModal] = useState<boolean>(false);
|
||||||
const [roleNames, setRoleNames] = useState<SelectProps['options']>([]);
|
const [roleNames, setRoleNames] = useState<SelectProps['options']>([]);
|
||||||
|
const [modal, modaltHolder] = Modal.useModal();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +53,7 @@ const UserManagement: React.FC = () => {
|
|||||||
messageApi.error(error.message);
|
messageApi.error(error.message);
|
||||||
})
|
})
|
||||||
|
|
||||||
QueryUserList(tableParams, form.getFieldsValue())
|
UserInfo.QueryUserList(tableParams, form.getFieldsValue())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setData(res.collection);
|
setData(res.collection);
|
||||||
setTableParams({
|
setTableParams({
|
||||||
@ -71,7 +76,7 @@ const UserManagement: React.FC = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
let tableParamsParams = pagination ? { pagination } : tableParams;
|
let tableParamsParams = pagination ? { pagination } : tableParams;
|
||||||
let res = await QueryUserList(tableParamsParams, params ?? form.getFieldsValue());
|
let res = await UserInfo.QueryUserList(tableParamsParams, params ?? form.getFieldsValue());
|
||||||
setData(res.collection);
|
setData(res.collection);
|
||||||
setTableParams({
|
setTableParams({
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -89,7 +94,7 @@ const UserManagement: React.FC = () => {
|
|||||||
async function handleTableChange(pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<UserModel.UserCollection> | SorterResult<UserModel.UserCollection>[], extra: TableCurrentDataSource<UserModel.UserCollection>): Promise<void> {
|
async function handleTableChange(pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<UserModel.UserCollection> | SorterResult<UserModel.UserCollection>[], extra: TableCurrentDataSource<UserModel.UserCollection>): Promise<void> {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
let queryUser = await QueryUserList({ pagination }, form.getFieldsValue());
|
let queryUser = await UserInfo.QueryUserList({ pagination }, form.getFieldsValue());
|
||||||
setData(queryUser.collection);
|
setData(queryUser.collection);
|
||||||
setTableParams({
|
setTableParams({
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -136,6 +141,17 @@ const UserManagement: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function ResetUserPasswordFunc(userId: number) {
|
||||||
|
modal.info({
|
||||||
|
title: '重置密码',
|
||||||
|
width: 500,
|
||||||
|
closable: true,
|
||||||
|
content: <ResetUserPassword userId={userId} />,
|
||||||
|
footer: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给指定用户申请软件控制权限
|
* 给指定用户申请软件控制权限
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
@ -246,6 +262,15 @@ const UserManagement: React.FC = () => {
|
|||||||
setOpenModal(true);
|
setOpenModal(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'resetPassword',
|
||||||
|
label: '重置密码',
|
||||||
|
icon: <KeyOutlined />,
|
||||||
|
hidden: !access.isSuperAdmin,
|
||||||
|
onClick: async () => {
|
||||||
|
await ResetUserPasswordFunc(record.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'apply',
|
key: 'apply',
|
||||||
label: '初始化权限',
|
label: '初始化权限',
|
||||||
@ -344,6 +369,7 @@ const UserManagement: React.FC = () => {
|
|||||||
<ModifyUser setFormRef={setFormRef} open={openModal} userId={userId} />
|
<ModifyUser setFormRef={setFormRef} open={openModal} userId={userId} />
|
||||||
</Modal>
|
</Modal>
|
||||||
{messageHolder}
|
{messageHolder}
|
||||||
|
{modaltHolder}
|
||||||
</TemplateContainer>
|
</TemplateContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -91,6 +91,10 @@ async function EnableAgent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的代理信息
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async function GetUserAgentInfo(): Promise<UserModel.UserAgentInfo> {
|
async function GetUserAgentInfo(): Promise<UserModel.UserAgentInfo> {
|
||||||
let res = await request<ApiResponse.SuccessItem<UserModel.UserAgentInfo>>(`/lms/User/GetUserAgentInfo`, {
|
let res = await request<ApiResponse.SuccessItem<UserModel.UserAgentInfo>>(`/lms/User/GetUserAgentInfo`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -101,10 +105,28 @@ async function GetUserAgentInfo(): Promise<UserModel.UserAgentInfo> {
|
|||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
/**
|
||||||
|
* 重置指定用户的用户名和密码
|
||||||
|
* @param userId
|
||||||
|
* @param newPassword
|
||||||
|
*/
|
||||||
|
async function ResetUserPassword(userId: number, newPassword: string): Promise<void> {
|
||||||
|
let res = await request<ApiResponse.SuccessItem<null>>(`/lms/User/ResetPassword/${userId}`, {
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
newPassword: newPassword,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (res.code != 1) {
|
||||||
|
throw new Error(res.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserInfo = {
|
||||||
QueryUserList,
|
QueryUserList,
|
||||||
GetUserInfo,
|
GetUserInfo,
|
||||||
UpdatedUserInfo,
|
UpdatedUserInfo,
|
||||||
EnableAgent,
|
EnableAgent,
|
||||||
GetUserAgentInfo,
|
GetUserAgentInfo,
|
||||||
|
ResetUserPassword,
|
||||||
}
|
}
|
||||||
25
src/util/password.ts
Normal file
25
src/util/password.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export function generateRandomPassword(): string {
|
||||||
|
const upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
const lowerCase = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const numbers = '0123456789';
|
||||||
|
const special = '@$!%*?.&';
|
||||||
|
|
||||||
|
let password = '';
|
||||||
|
|
||||||
|
// Ensure at least one character from each category
|
||||||
|
password += upperCase[Math.floor(Math.random() * upperCase.length)];
|
||||||
|
password += lowerCase[Math.floor(Math.random() * lowerCase.length)];
|
||||||
|
password += numbers[Math.floor(Math.random() * numbers.length)];
|
||||||
|
password += special[Math.floor(Math.random() * special.length)];
|
||||||
|
|
||||||
|
// Fill the rest randomly
|
||||||
|
const allChars = upperCase + lowerCase + numbers + special;
|
||||||
|
for (let i = password.length; i < 12; i++) {
|
||||||
|
password += allChars[Math.floor(Math.random() * allChars.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle the password
|
||||||
|
password = password.split('').sort(() => Math.random() - 0.5).join('');
|
||||||
|
|
||||||
|
return password;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user