220 lines
5.5 KiB
Vue
220 lines
5.5 KiB
Vue
<script setup lang="ts">
|
|
import {watchDebounced} from '@vueuse/core'
|
|
import * as Yup from "yup";
|
|
import {useToast} from "vue-toastification";
|
|
import type {UserInfoListType} from "~/types/UserType";
|
|
const toast = useToast()
|
|
const avatarImgUrl = ref("")
|
|
const errors = ref<string[]>([]);
|
|
const props=defineProps({
|
|
closeBack: {
|
|
type: Function,
|
|
default: () => {}
|
|
}
|
|
})
|
|
//随机生成密码
|
|
const randomPassword = () => {
|
|
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+';
|
|
let password = '';
|
|
for (let i = 0; i < 24; i++) {
|
|
password += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
}
|
|
return password
|
|
}
|
|
const form = reactive({
|
|
fullName: '',
|
|
userName: '',
|
|
position: '',
|
|
email: 'examp@examp.com',
|
|
phone: '1888888888',
|
|
password: randomPassword().toString(),
|
|
role: 'user',
|
|
});
|
|
|
|
const schema = Yup.object().shape({
|
|
fullName: Yup.string().required('请输入姓名'),
|
|
userName: Yup.string().required('请输入用户名').matches(/^[A-Za-z0-9_]+$/,"用户名必须为英文,且只能包含英文字母、数字和下划线"),
|
|
position: Yup.string().required('请输入职位'),
|
|
email: Yup.string().email('请输入正确的邮箱').required('请输入邮箱'),
|
|
phone: Yup.string().required('请输入手机号'),
|
|
password: Yup.string().required('请输入密码').min(6, '密码至少需要6个字符')
|
|
.matches(/(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#$%^&*()_+])[0-9a-zA-Z!@#$%^&*()_+]{6,}/, '密码必须包含字母、数字和至少一个特殊字符'),
|
|
role: Yup.string().required('请选择权限'),
|
|
});
|
|
|
|
watchDebounced(
|
|
() => form.userName,
|
|
() => {
|
|
avatarImgUrl.value = `https://api.multiavatar.com/${form.userName}.svg`
|
|
},
|
|
{debounce: 2000, maxWait: 4000},
|
|
)
|
|
const handleSubmit = async () => {
|
|
try {
|
|
// 验证表单
|
|
await schema.validate(form, {abortEarly: false});
|
|
errors.value = []; // 清空错误信息
|
|
// 执行创建用户的逻辑
|
|
$fetch('Api/Account/Register', {
|
|
method: "POST",
|
|
body: form,
|
|
headers:{
|
|
"Authorization": "Bearer " + useCookie("token").value
|
|
},
|
|
baseURL:useRuntimeConfig().public.baseUrl
|
|
}).then(res => {
|
|
toast.success(res)
|
|
//调用copy
|
|
const { text, copy, copied, isSupported } = useClipboard({
|
|
source: JSON.stringify(form),
|
|
copiedDuring: 2000,
|
|
})
|
|
if(!isSupported.value){
|
|
toast.error('浏览器不支持复制')
|
|
setTimeout(()=>{
|
|
props.closeBack()
|
|
return
|
|
},2000)
|
|
}
|
|
copy()
|
|
toast.success('账号已复制到剪贴板')
|
|
//检查是否拷贝成功
|
|
if (!copied.value) {
|
|
toast.error('复制失败')
|
|
}
|
|
setTimeout(()=>{
|
|
props.closeBack()
|
|
},2000)
|
|
}).catch(
|
|
err => {
|
|
toast.error(err)
|
|
}
|
|
)
|
|
} catch (error) {
|
|
// 处理验证错误
|
|
if (error instanceof Yup.ValidationError) {
|
|
errors.value = error.inner.map(e =>{
|
|
toast.error(e.message)
|
|
return e.message
|
|
} );
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="create-user-layout">
|
|
<Avatar size="xlarge" shape="circle" class="avatar" :image="avatarImgUrl"/>
|
|
<div class="header">
|
|
<Icon name="User" :stroke-width="2" :size="32"></Icon>
|
|
<h2>创建新的账户</h2>
|
|
</div>
|
|
<form class="form" @submit.prevent="handleSubmit">
|
|
<div>
|
|
<input placeholder="用户名" type="text" v-model="form.userName"/>
|
|
<input placeholder="姓名" type="text" v-model="form.fullName"/>
|
|
</div>
|
|
<div>
|
|
<input placeholder="职位" type="text" v-model="form.position"/>
|
|
<select v-model="form.role">>
|
|
<option value="user">普通运维人员</option>
|
|
<option value="admin">管理员</option>
|
|
</select>
|
|
</div>
|
|
<input placeholder="邮箱" type="email" v-model="form.email"/>
|
|
<input placeholder="手机号" type="text" v-model="form.phone"/>
|
|
<input placeholder="密码" type="text" v-model="form.password"/>
|
|
<div class="active">
|
|
<button @click.prevent="closeBack()">取消</button>
|
|
<button type="submit">创建</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
@import "base";
|
|
|
|
.create-user-layout {
|
|
background: $light-bg-color;
|
|
width: 400px;
|
|
min-height: 600px;
|
|
border-radius: $radius;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $gap*4;
|
|
flex-direction: column;
|
|
padding: $padding*6 $padding*2 $padding*2;
|
|
box-shadow: 0 0 10px rgba($light-bg-color, .4);
|
|
}
|
|
|
|
.avatar {
|
|
position: absolute;
|
|
top: -50px;
|
|
box-shadow: 0 0 10px rgba($light-bg-color, .4);
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 120px;
|
|
height: 120px;
|
|
background: #2F3F53;
|
|
color: #fff;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $gap;
|
|
}
|
|
|
|
.form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100%;
|
|
gap: $gap;
|
|
|
|
div {
|
|
display: flex;
|
|
gap: $gap;
|
|
align-items: center;
|
|
|
|
input, select {
|
|
flex: 1;
|
|
width: 100%;
|
|
}
|
|
|
|
}
|
|
|
|
input, select {
|
|
border: 2px solid rgb(212, 217, 221);
|
|
padding: $padding;
|
|
border-radius: $radius;
|
|
height: 60px;
|
|
}
|
|
}
|
|
|
|
.active{
|
|
display: flex;
|
|
width: 100%;
|
|
padding-top:$gap*2;
|
|
button {
|
|
flex: 1;
|
|
height: 60px;
|
|
border: 2px solid rgb(212, 217, 221);
|
|
border-radius: $radius;
|
|
background: $primary-color;
|
|
color:#fff;
|
|
font-size: 16px;
|
|
|
|
&:first-child {
|
|
background: unset;
|
|
color: rgb(212, 217, 221);
|
|
&:hover {
|
|
border-color: $light-text-color;
|
|
color: $light-text-color;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
</style> |