新增用户注册功能
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<el-card>
|
||||
<h3 class="title">个人设置</h3>
|
||||
<p class="subtitle">更新个人信息或修改登录密码</p>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" class="form">
|
||||
<el-form-item label="头像">
|
||||
<div class="avatar-row">
|
||||
<el-avatar :size="64" :src="avatarPreview" :alt="form.full_name || form.email">
|
||||
{{ form.full_name?.charAt(0)?.toUpperCase() || form.email?.charAt(0)?.toUpperCase() }}
|
||||
</el-avatar>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:show-file-list="false"
|
||||
action="/api/v1/auth/me/avatar"
|
||||
name="file"
|
||||
:headers="uploadHeaders"
|
||||
:on-success="onAvatarUploaded"
|
||||
:on-error="onAvatarError"
|
||||
>
|
||||
<el-button>上传头像</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="form.email" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="full_name">
|
||||
<el-input v-model="form.full_name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门" prop="department">
|
||||
<el-input v-model="form.department" placeholder="请输入部门" />
|
||||
</el-form-item>
|
||||
<el-divider />
|
||||
<el-form-item label="当前密码" prop="current_password">
|
||||
<el-input v-model="form.current_password" type="password" show-password placeholder="修改密码时必填" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="form.password" type="password" show-password placeholder="不修改请留空" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认新密码" prop="confirmPassword">
|
||||
<el-input v-model="form.confirmPassword" type="password" show-password placeholder="再次输入新密码" />
|
||||
</el-form-item>
|
||||
<div class="actions">
|
||||
<el-button type="primary" :loading="submitting" @click="onSubmit">保存</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { updateProfile, fetchMe } from "../api/auth";
|
||||
import { useAuthStore } from "../store/auth";
|
||||
import { getToken } from "../utils/auth";
|
||||
|
||||
const auth = useAuthStore();
|
||||
const formRef = ref<FormInstance>();
|
||||
const submitting = ref(false);
|
||||
const form = reactive({
|
||||
email: "",
|
||||
full_name: "",
|
||||
department: "",
|
||||
current_password: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
const avatarPreview = ref<string | undefined>();
|
||||
const uploadHeaders = computed<Record<string, string>>(() => ({
|
||||
Authorization: `Bearer ${getToken() || ""}`,
|
||||
}));
|
||||
|
||||
const rules: FormRules<typeof form> = {
|
||||
full_name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
department: [{ required: true, message: "请输入部门", trigger: "blur" }],
|
||||
current_password: [
|
||||
{
|
||||
validator: (_r, value, cb) => {
|
||||
if (form.password && !value) {
|
||||
cb(new Error("修改密码需输入当前密码"));
|
||||
return;
|
||||
}
|
||||
cb();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{
|
||||
validator: (_r, value, cb) => {
|
||||
if (!value) return cb();
|
||||
if (value.length < 8 || !/[A-Za-z]/.test(value) || !/[0-9]/.test(value)) {
|
||||
cb(new Error("至少 8 位且包含字母与数字"));
|
||||
return;
|
||||
}
|
||||
cb();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
confirmPassword: [
|
||||
{
|
||||
validator: (_r, value, cb) => {
|
||||
if (form.password && value !== form.password) {
|
||||
cb(new Error("两次输入的密码不一致"));
|
||||
return;
|
||||
}
|
||||
cb();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const loadProfile = async () => {
|
||||
const { data } = await fetchMe();
|
||||
form.email = data.email;
|
||||
form.full_name = data.full_name;
|
||||
form.department = data.department;
|
||||
avatarPreview.value = data.avatar_url || undefined;
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (!formRef.value) return;
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
submitting.value = true;
|
||||
try {
|
||||
await updateProfile({
|
||||
full_name: form.full_name,
|
||||
department: form.department,
|
||||
current_password: form.password ? form.current_password : undefined,
|
||||
password: form.password || undefined,
|
||||
});
|
||||
await auth.fetchMe();
|
||||
ElMessage.success("已更新");
|
||||
form.current_password = "";
|
||||
form.password = "";
|
||||
form.confirmPassword = "";
|
||||
avatarPreview.value = auth.user?.avatar_url || avatarPreview.value;
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.response?.data?.message || "更新失败");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onAvatarUploaded = async () => {
|
||||
await auth.fetchMe();
|
||||
avatarPreview.value = auth.user?.avatar_url || undefined;
|
||||
ElMessage.success("头像已更新");
|
||||
};
|
||||
|
||||
const onAvatarError = (err: any) => {
|
||||
ElMessage.error(err?.response?.data?.message || "头像上传失败");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadProfile();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
.title {
|
||||
margin: 0;
|
||||
}
|
||||
.subtitle {
|
||||
margin: 6px 0 16px;
|
||||
color: #666;
|
||||
}
|
||||
.form {
|
||||
max-width: 520px;
|
||||
}
|
||||
.actions {
|
||||
margin-top: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user