个人信息界面编辑信息功能完善
This commit is contained in:
@@ -1,251 +1,3 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
ElCard,
|
||||
ElDescriptions,
|
||||
ElDescriptionsItem,
|
||||
ElTag,
|
||||
ElButton,
|
||||
ElMessage,
|
||||
ElDialog,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElAvatar,
|
||||
} from "element-plus";
|
||||
import type { FormRules } from "element-plus";
|
||||
import {
|
||||
Iphone,
|
||||
Message,
|
||||
Male,
|
||||
Female,
|
||||
UserFilled,
|
||||
Edit,
|
||||
Lock,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { getUserProfile, updateUserProfile } from "@/apis/user";
|
||||
import { useUserStore } from "@/stores/UserStore";
|
||||
import type { UserInfo } from "@/types/user";
|
||||
import UserLayout from "@/layouts/UserLayout.vue";
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref<UserInfo | null>(null);
|
||||
const loading = ref(true);
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 编辑信息相关
|
||||
const showEditDialog = ref(false);
|
||||
const editForm = ref({
|
||||
nickname: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
gender: 0,
|
||||
avatar: "",
|
||||
});
|
||||
|
||||
// 修改密码相关
|
||||
const showPasswordDialog = ref(false);
|
||||
const passwordForm = ref({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const editRules: FormRules = {
|
||||
nickname: [
|
||||
{ required: true, message: "请输入昵称", trigger: "blur" },
|
||||
{ min: 2, max: 20, message: "昵称长度在2-20个字符之间", trigger: "blur" },
|
||||
],
|
||||
phone: [
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: "请输入正确的手机号",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const passwordRules: FormRules = {
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "密码长度至少6位", trigger: "blur" },
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
validator: (
|
||||
_rule: unknown,
|
||||
value: string,
|
||||
callback: (arg0?: Error) => void,
|
||||
) => {
|
||||
if (value !== passwordForm.value.newPassword) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 性别映射
|
||||
const genderMap: { [key: number]: string } = {
|
||||
0: "未知",
|
||||
1: "男",
|
||||
2: "女",
|
||||
};
|
||||
|
||||
// 状态映射
|
||||
const statusMap: {
|
||||
[key: number]: { label: string; type: "danger" | "success" | "info" };
|
||||
} = {
|
||||
0: { label: "禁用", type: "danger" },
|
||||
1: { label: "正常", type: "success" },
|
||||
};
|
||||
|
||||
// 格式化时间显示
|
||||
const formatDateTime = (
|
||||
dateTime: string | undefined | null | Array<number>,
|
||||
): string => {
|
||||
if (!dateTime) return "-";
|
||||
|
||||
// 如果是数组格式的日期时间 [2025, 12, 17, 15, 13, 19]
|
||||
if (Array.isArray(dateTime)) {
|
||||
const [year, month, day, hour, minute, second] = dateTime;
|
||||
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")} ${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:${String(second).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// 如果是完整的日期时间字符串,可以直接返回
|
||||
return dateTime.toString();
|
||||
};
|
||||
|
||||
// 获取用户信息
|
||||
const fetchUserProfile = async () => {
|
||||
try {
|
||||
if (!userStore.userInfo?.id) {
|
||||
ElMessage.error("用户信息不存在");
|
||||
router.push("/user/login");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await getUserProfile(userStore.userInfo.id);
|
||||
if (response.code === 200) {
|
||||
userInfo.value = response.data;
|
||||
} else {
|
||||
ElMessage.error(response.message || "获取用户信息失败");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
ElMessage.error("获取用户信息失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑个人信息
|
||||
const handleEditProfile = () => {
|
||||
if (userInfo.value) {
|
||||
editForm.value = {
|
||||
nickname: userInfo.value.nickname || "",
|
||||
phone: userInfo.value.phone || "",
|
||||
email: userInfo.value.email || "",
|
||||
gender: userInfo.value.gender || 0,
|
||||
avatar: userInfo.value.avatar || "",
|
||||
};
|
||||
showEditDialog.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 保存编辑的个人信息
|
||||
const saveEditProfile = async () => {
|
||||
try {
|
||||
// 添加用户ID到编辑表单数据中
|
||||
const updateData = {
|
||||
...editForm.value,
|
||||
id: userStore.userInfo?.id, // 添加用户ID
|
||||
};
|
||||
const response = await updateUserProfile(updateData);
|
||||
if (response.code === 200) {
|
||||
ElMessage.success("更新成功");
|
||||
showEditDialog.value = false;
|
||||
fetchUserProfile(); // 重新获取用户信息
|
||||
} else {
|
||||
ElMessage.error(response.message || "更新失败");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
ElMessage.error("更新失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 修改密码
|
||||
const handleChangePassword = () => {
|
||||
passwordForm.value = {
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
};
|
||||
showPasswordDialog.value = true;
|
||||
};
|
||||
|
||||
// 保存新密码
|
||||
const saveNewPassword = async () => {
|
||||
try {
|
||||
// 通过更新用户信息接口来修改密码
|
||||
const response = await updateUserProfile({
|
||||
id: userStore.userInfo?.id, // 确保ID已传递
|
||||
password: passwordForm.value.newPassword,
|
||||
oldPassword: passwordForm.value.oldPassword,
|
||||
});
|
||||
if (response.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
showPasswordDialog.value = false;
|
||||
// 退出登录并跳转到登录页
|
||||
userStore.clearUserInfo();
|
||||
router.push("/user/login");
|
||||
} else {
|
||||
ElMessage.error(response.message || "密码修改失败");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
ElMessage.error("密码修改失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
userStore.clearUserInfo();
|
||||
ElMessage.success("退出登录成功");
|
||||
router.push("/user/login");
|
||||
};
|
||||
|
||||
// 获取性别文本
|
||||
const getGenderText = (gender: number | undefined | null): string => {
|
||||
if (gender === undefined || gender === null) return "-";
|
||||
return genderMap[gender] || "未知";
|
||||
};
|
||||
|
||||
// 获取状态标签信息
|
||||
const getStatusInfo = (
|
||||
status: number | undefined,
|
||||
): { label: string; type: "danger" | "success" | "info" } => {
|
||||
if (status === undefined) return { label: "未知", type: "info" };
|
||||
return statusMap[status] || { label: "未知", type: "info" };
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchUserProfile();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UserLayout>
|
||||
<div class="profile-page">
|
||||
@@ -387,7 +139,21 @@ onMounted(() => {
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
<el-input v-model="editForm.avatar" placeholder="请输入头像URL" />
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-change="handleAvatarChange"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
accept="image/*"
|
||||
>
|
||||
<template v-if="editForm.avatar">
|
||||
<img :src="editForm.avatar" class="avatar" alt="头像预览" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button type="primary" plain>选择头像</el-button>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -438,6 +204,304 @@ onMounted(() => {
|
||||
</UserLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
ElCard,
|
||||
ElDescriptions,
|
||||
ElDescriptionsItem,
|
||||
ElTag,
|
||||
ElButton,
|
||||
ElMessage,
|
||||
ElDialog,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElAvatar,
|
||||
ElUpload,
|
||||
} from "element-plus";
|
||||
import type { FormRules, UploadFile } from "element-plus";
|
||||
import {
|
||||
Iphone,
|
||||
Message,
|
||||
Male,
|
||||
Female,
|
||||
UserFilled,
|
||||
Edit,
|
||||
Lock,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { getUserProfile, updateUserProfile } from "@/apis/user";
|
||||
import { uploadFile } from "@/apis/product";
|
||||
import { useUserStore } from "@/stores/UserStore";
|
||||
import type { UserInfo } from "@/types/user";
|
||||
import UserLayout from "@/layouts/UserLayout.vue";
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref<UserInfo | null>(null);
|
||||
const loading = ref(true);
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 编辑信息相关
|
||||
const showEditDialog = ref(false);
|
||||
const editForm = ref({
|
||||
nickname: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
gender: 0,
|
||||
avatar: "",
|
||||
});
|
||||
const pendingAvatarFile = ref<File | null>(null); // 暂存待上传的头像文件
|
||||
|
||||
// 修改密码相关
|
||||
const showPasswordDialog = ref(false);
|
||||
const passwordForm = ref({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const editRules: FormRules = {
|
||||
nickname: [
|
||||
{ required: true, message: "请输入昵称", trigger: "blur" },
|
||||
{ min: 2, max: 20, message: "昵称长度在2-20个字符之间", trigger: "blur" },
|
||||
],
|
||||
phone: [
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: "请输入正确的手机号",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const passwordRules: FormRules = {
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "密码长度至少6位", trigger: "blur" },
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
validator: (
|
||||
_rule: unknown,
|
||||
value: string,
|
||||
callback: (arg0?: Error) => void,
|
||||
) => {
|
||||
if (value !== passwordForm.value.newPassword) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 性别映射
|
||||
const genderMap: { [key: number]: string } = {
|
||||
0: "未知",
|
||||
1: "男",
|
||||
2: "女",
|
||||
};
|
||||
|
||||
// 状态映射
|
||||
const statusMap: {
|
||||
[key: number]: { label: string; type: "danger" | "success" | "info" };
|
||||
} = {
|
||||
0: { label: "禁用", type: "danger" },
|
||||
1: { label: "正常", type: "success" },
|
||||
};
|
||||
|
||||
// 格式化时间显示
|
||||
const formatDateTime = (
|
||||
dateTime: string | undefined | null | Array<number>,
|
||||
): string => {
|
||||
if (!dateTime) return "-";
|
||||
|
||||
// 如果是数组格式的日期时间 [2025, 12, 17, 15, 13, 19]
|
||||
if (Array.isArray(dateTime)) {
|
||||
const [year, month, day, hour, minute, second] = dateTime;
|
||||
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")} ${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:${String(second).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// 如果是完整的日期时间字符串,可以直接返回
|
||||
return dateTime.toString();
|
||||
};
|
||||
|
||||
// 获取用户信息
|
||||
const fetchUserProfile = async () => {
|
||||
try {
|
||||
if (!userStore.userInfo?.id) {
|
||||
ElMessage.error("用户信息不存在");
|
||||
router.push("/user/login");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await getUserProfile(userStore.userInfo.id);
|
||||
if (response.code === 200) {
|
||||
userInfo.value = response.data;
|
||||
} else {
|
||||
ElMessage.error(response.message || "获取用户信息失败");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
ElMessage.error("获取用户信息失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑个人信息
|
||||
const handleEditProfile = () => {
|
||||
if (userInfo.value) {
|
||||
editForm.value = {
|
||||
nickname: userInfo.value.nickname || "",
|
||||
phone: userInfo.value.phone || "",
|
||||
email: userInfo.value.email || "",
|
||||
gender: userInfo.value.gender || 0,
|
||||
avatar: userInfo.value.avatar || "",
|
||||
};
|
||||
pendingAvatarFile.value = null; // 重置暂存的头像文件
|
||||
showEditDialog.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 上传头像前的钩子
|
||||
const beforeAvatarUpload = (file: File) => {
|
||||
const isImage = file.type.startsWith("image/");
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
|
||||
if (!isImage) {
|
||||
ElMessage.error("上传头像只能是图片格式!");
|
||||
}
|
||||
if (!isLt2M) {
|
||||
ElMessage.error("上传头像大小不能超过 2MB!");
|
||||
}
|
||||
return isImage && isLt2M;
|
||||
};
|
||||
|
||||
// 处理头像选择变化
|
||||
const handleAvatarChange = (uploadFile: UploadFile) => {
|
||||
if (!uploadFile.raw || !beforeAvatarUpload(uploadFile.raw)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 暂存文件,等待提交时上传
|
||||
pendingAvatarFile.value = uploadFile.raw;
|
||||
|
||||
// 创建预览URL用于显示
|
||||
const previewUrl = URL.createObjectURL(uploadFile.raw);
|
||||
editForm.value.avatar = previewUrl;
|
||||
};
|
||||
|
||||
// 保存编辑的个人信息
|
||||
const saveEditProfile = async () => {
|
||||
try {
|
||||
// 如果有待上传的头像文件,先上传
|
||||
if (pendingAvatarFile.value) {
|
||||
try {
|
||||
const response = await uploadFile(pendingAvatarFile.value);
|
||||
if (response.code === 200) {
|
||||
editForm.value.avatar = response.data.fileUrl;
|
||||
ElMessage.success("头像上传成功");
|
||||
} else {
|
||||
ElMessage.error(response.message || "头像上传失败");
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("头像上传错误:", error);
|
||||
ElMessage.error("头像上传失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加用户ID到编辑表单数据中
|
||||
const updateData = {
|
||||
...editForm.value,
|
||||
id: userStore.userInfo?.id, // 添加用户ID
|
||||
};
|
||||
const response = await updateUserProfile(updateData);
|
||||
if (response.code === 200) {
|
||||
ElMessage.success("更新成功");
|
||||
showEditDialog.value = false;
|
||||
fetchUserProfile(); // 重新获取用户信息
|
||||
} else {
|
||||
ElMessage.error(response.message || "更新失败");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
ElMessage.error("更新失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 修改密码
|
||||
const handleChangePassword = () => {
|
||||
passwordForm.value = {
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
};
|
||||
showPasswordDialog.value = true;
|
||||
};
|
||||
|
||||
// 保存新密码
|
||||
const saveNewPassword = async () => {
|
||||
try {
|
||||
// 通过更新用户信息接口来修改密码
|
||||
const response = await updateUserProfile({
|
||||
id: userStore.userInfo?.id, // 确保ID已传递
|
||||
password: passwordForm.value.newPassword,
|
||||
oldPassword: passwordForm.value.oldPassword,
|
||||
});
|
||||
if (response.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
showPasswordDialog.value = false;
|
||||
// 退出登录并跳转到登录页
|
||||
userStore.clearUserInfo();
|
||||
router.push("/user/login");
|
||||
} else {
|
||||
ElMessage.error(response.message || "密码修改失败");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
ElMessage.error("密码修改失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
userStore.clearUserInfo();
|
||||
ElMessage.success("退出登录成功");
|
||||
router.push("/user/login");
|
||||
};
|
||||
|
||||
// 获取性别文本
|
||||
const getGenderText = (gender: number | undefined | null): string => {
|
||||
if (gender === undefined || gender === null) return "-";
|
||||
return genderMap[gender] || "未知";
|
||||
};
|
||||
|
||||
// 获取状态标签信息
|
||||
const getStatusInfo = (
|
||||
status: number | undefined,
|
||||
): { label: string; type: "danger" | "success" | "info" } => {
|
||||
if (status === undefined) return { label: "未知", type: "info" };
|
||||
return statusMap[status] || { label: "未知", type: "info" };
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchUserProfile();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
@@ -470,4 +534,11 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-uploader .avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user