@@ -55,3 +55,13 @@ export const importURL = (data) => {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 上传文件 暂时用于头像上传
|
||||||
|
export const uploadFile = (data) => {
|
||||||
|
return service({
|
||||||
|
url: "/fileUploadAndDownload/upload",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
120
web/src/components/Avatar/ProfileAvatar.vue
Normal file
120
web/src/components/Avatar/ProfileAvatar.vue
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div class="profile-avatar relative w-[120px] h-[120px] rounded-full overflow-hidden cursor-pointer group">
|
||||||
|
<img
|
||||||
|
v-if="modelValue"
|
||||||
|
class="w-full h-full object-cover"
|
||||||
|
:src="getUrl(modelValue)"
|
||||||
|
alt="头像"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="w-full h-full flex flex-col items-center justify-center bg-gray-100 dark:bg-slate-700"
|
||||||
|
>
|
||||||
|
<el-icon class="text-2xl text-gray-400">
|
||||||
|
<avatar />
|
||||||
|
</el-icon>
|
||||||
|
<span class="mt-2 text-sm text-gray-400">点击上传</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-all duration-300"
|
||||||
|
@click="chooseFile"
|
||||||
|
>
|
||||||
|
<div class="text-center text-white">
|
||||||
|
<el-icon class="text-2xl"><camera-filled /></el-icon>
|
||||||
|
<div class="text-xs mt-1">更换头像</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref="fileInput"
|
||||||
|
type="file"
|
||||||
|
class="hidden"
|
||||||
|
accept="image/*"
|
||||||
|
@change="handleFileChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { getUrl } from '@/utils/image'
|
||||||
|
import service from '@/utils/request'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ProfileAvatar'
|
||||||
|
})
|
||||||
|
|
||||||
|
const { modelValue } = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const fileInput = ref(null)
|
||||||
|
|
||||||
|
const chooseFile = () => {
|
||||||
|
fileInput.value.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileChange = async (e) => {
|
||||||
|
const file = e.target.files[0]
|
||||||
|
if (!file) return
|
||||||
|
|
||||||
|
// 验证文件类型
|
||||||
|
if (!file.type.includes('image/')) {
|
||||||
|
ElMessage.error('请选择图片文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证文件大小(限制为2MB)
|
||||||
|
if (file.size > 2 * 1024 * 1024) {
|
||||||
|
ElMessage.error('图片大小不能超过2MB')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
const res = await service({
|
||||||
|
url: '/fileUploadAndDownload/upload',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 0 && res.data?.file?.url) {
|
||||||
|
emit('update:modelValue', res.data.file.url)
|
||||||
|
ElMessage.success('头像上传成功')
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '上传失败')
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('头像上传失败,请重试')
|
||||||
|
} finally {
|
||||||
|
// 清空input,确保可以重复选择同一文件
|
||||||
|
e.target.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.profile-avatar {
|
||||||
|
img {
|
||||||
|
@apply transition-transform duration-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
img {
|
||||||
|
@apply scale-110;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,171 +1,226 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="gva-form-box">
|
<div class="profile-container">
|
||||||
<div class="grid grid-cols-12 w-full gap-2">
|
<!-- 顶部个人信息卡片 -->
|
||||||
<div class="col-span-3 h-full">
|
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm mb-8">
|
||||||
<div
|
<!-- 顶部背景图 -->
|
||||||
class="w-full h-full bg-white dark:bg-slate-900 px-4 py-8 rounded-lg shadow-lg box-border"
|
<div class="h-48 bg-blue-50 dark:bg-slate-600 relative">
|
||||||
>
|
<div class="absolute inset-0 bg-pattern opacity-7"></div>
|
||||||
<div
|
</div>
|
||||||
class="user-card px-6 text-center bg-white dark:bg-slate-900 shrink-0"
|
|
||||||
>
|
<!-- 个人信息区 -->
|
||||||
<div class="flex justify-center">
|
<div class="px-8 -mt-20 pb-8">
|
||||||
<SelectImage
|
<div class="flex flex-col lg:flex-row items-start gap-8">
|
||||||
v-model="userStore.userInfo.headerImg"
|
<!-- 左侧头像 -->
|
||||||
file-type="image"
|
<div class="profile-avatar-wrapper flex-shrink-0 mx-auto lg:mx-0">
|
||||||
/>
|
<ProfileAvatar
|
||||||
</div>
|
v-model="userStore.userInfo.headerImg"
|
||||||
<div class="py-6 text-center">
|
@update:modelValue="handleAvatarChange"
|
||||||
<p
|
/>
|
||||||
v-if="!editFlag"
|
</div>
|
||||||
class="text-3xl flex justify-center items-center gap-4"
|
|
||||||
>
|
<!-- 右侧信息 -->
|
||||||
{{ userStore.userInfo.nickName }}
|
<div class="flex-1 pt-20 w-full">
|
||||||
<el-icon
|
<div class="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4">
|
||||||
class="cursor-pointer text-sm"
|
<div>
|
||||||
color="#66b1ff"
|
<div class="flex items-center gap-4 mb-4">
|
||||||
@click="openEdit"
|
<div v-if="!editFlag" class="text-2xl font-bold flex items-center gap-3 text-gray-800 dark:text-gray-100">
|
||||||
>
|
{{ userStore.userInfo.nickName }}
|
||||||
<edit />
|
<el-icon
|
||||||
</el-icon>
|
class="cursor-pointer text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors duration-200"
|
||||||
</p>
|
@click="openEdit"
|
||||||
<p v-if="editFlag" class="flex justify-center items-center gap-4">
|
>
|
||||||
<el-input v-model="nickName" />
|
<edit />
|
||||||
<el-icon
|
|
||||||
class="cursor-pointer"
|
|
||||||
color="#67c23a"
|
|
||||||
@click="enterEdit"
|
|
||||||
>
|
|
||||||
<check />
|
|
||||||
</el-icon>
|
|
||||||
<el-icon
|
|
||||||
class="cursor-pointer"
|
|
||||||
color="#f23c3c"
|
|
||||||
@click="closeEdit"
|
|
||||||
>
|
|
||||||
<close />
|
|
||||||
</el-icon>
|
|
||||||
</p>
|
|
||||||
<p class="text-gray-500 mt-2 text-md">
|
|
||||||
这个家伙很懒,什么都没有留下
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-full h-full text-left">
|
|
||||||
<ul class="inline-block h-full w-full">
|
|
||||||
<li class="info-list">
|
|
||||||
<el-icon>
|
|
||||||
<user />
|
|
||||||
</el-icon>
|
|
||||||
{{ userStore.userInfo.nickName }}
|
|
||||||
</li>
|
|
||||||
<el-tooltip
|
|
||||||
class="item"
|
|
||||||
effect="light"
|
|
||||||
content="北京反转极光科技有限公司-技术部-前端事业群"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<li class="info-list">
|
|
||||||
<el-icon>
|
|
||||||
<data-analysis />
|
|
||||||
</el-icon>
|
</el-icon>
|
||||||
北京反转极光科技有限公司-技术部-前端事业群
|
</div>
|
||||||
</li>
|
<div v-else class="flex items-center gap-3">
|
||||||
</el-tooltip>
|
<el-input
|
||||||
<li class="info-list">
|
v-model="nickName"
|
||||||
<el-icon>
|
class="w-48"
|
||||||
<video-camera />
|
size="large"
|
||||||
</el-icon>
|
/>
|
||||||
中国·北京市·朝阳区
|
<el-button type="success" circle @click="enterEdit">
|
||||||
</li>
|
<el-icon><check /></el-icon>
|
||||||
<el-tooltip
|
</el-button>
|
||||||
class="item"
|
<el-button type="danger" circle @click="closeEdit">
|
||||||
effect="light"
|
<el-icon><close /></el-icon>
|
||||||
content="GoLang/JavaScript/Vue/Gorm"
|
</el-button>
|
||||||
placement="top"
|
</div>
|
||||||
>
|
</div>
|
||||||
<li class="info-list">
|
|
||||||
<el-icon>
|
<div class="flex flex-col lg:flex-row items-start lg:items-center gap-4 lg:gap-8 text-gray-500 dark:text-gray-400">
|
||||||
<medal />
|
<div class="flex items-center gap-2">
|
||||||
</el-icon>
|
<el-icon><location /></el-icon>
|
||||||
GoLang/JavaScript/Vue/Gorm
|
<span>中国·北京市·朝阳区</span>
|
||||||
</li>
|
</div>
|
||||||
</el-tooltip>
|
<div class="flex items-center gap-2">
|
||||||
</ul>
|
<el-icon><office-building /></el-icon>
|
||||||
|
<span>北京反转极光科技有限公司</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-icon><user /></el-icon>
|
||||||
|
<span>技术部·前端事业群</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-4 mt-4 lg:mt-0">
|
||||||
|
<el-button type="primary" plain>
|
||||||
|
<el-icon><message /></el-icon>
|
||||||
|
发送消息
|
||||||
|
</el-button>
|
||||||
|
<el-button>
|
||||||
|
<el-icon><share /></el-icon>
|
||||||
|
分享主页
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-9">
|
</div>
|
||||||
<div
|
|
||||||
class="bg-white dark:bg-slate-900 h-full px-4 py-8 rounded-lg shadow-lg box-border"
|
<!-- 主要内容区 -->
|
||||||
>
|
<div class="grid lg:grid-cols-12 md:grid-cols-1 gap-8">
|
||||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
<!-- 左侧信息栏 -->
|
||||||
<el-tab-pane label="账号绑定" name="second">
|
<div class="lg:col-span-4">
|
||||||
<ul>
|
<div class="bg-white dark:bg-slate-800 rounded-xl p-6 mb-6 profile-card">
|
||||||
<li class="borderd">
|
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
|
||||||
<p class="pb-2.5 text-xl text-gray-600">密保手机</p>
|
<el-icon class="text-blue-500"><info-filled /></el-icon>
|
||||||
<p class="pb-2.5 text-lg text-gray-400">
|
基本信息
|
||||||
已绑定手机:{{ userStore.userInfo.phone }}
|
</h2>
|
||||||
<a
|
<div class="space-y-4">
|
||||||
href="javascript:void(0)"
|
<div class="flex items-center gap-3 text-gray-600 dark:text-gray-300">
|
||||||
class="float-right text-blue-400"
|
<el-icon class="text-blue-500"><phone /></el-icon>
|
||||||
@click="changePhoneFlag = true"
|
<span class="font-medium">手机号码:</span>
|
||||||
>立即修改</a
|
<span>{{ userStore.userInfo.phone || '未设置' }}</span>
|
||||||
>
|
<el-button
|
||||||
</p>
|
link
|
||||||
</li>
|
type="primary"
|
||||||
<li class="borderd pt-2.5">
|
class="ml-auto"
|
||||||
<p class="pb-2.5 text-xl text-gray-600">密保邮箱</p>
|
@click="changePhoneFlag = true"
|
||||||
<p class="pb-2.5 text-lg text-gray-400">
|
>
|
||||||
已绑定邮箱:{{ userStore.userInfo.email }}
|
修改
|
||||||
<a
|
</el-button>
|
||||||
href="javascript:void(0)"
|
</div>
|
||||||
class="float-right text-blue-400"
|
<div class="flex items-center gap-3 text-gray-600 dark:text-gray-300">
|
||||||
@click="changeEmailFlag = true"
|
<el-icon class="text-green-500"><message /></el-icon>
|
||||||
>立即修改</a
|
<span class="font-medium">邮箱地址:</span>
|
||||||
>
|
<span>{{ userStore.userInfo.email || '未设置' }}</span>
|
||||||
</p>
|
<el-button
|
||||||
</li>
|
link
|
||||||
<li class="borderd pt-2.5">
|
type="primary"
|
||||||
<p class="pb-2.5 text-xl text-gray-600">密保问题</p>
|
class="ml-auto"
|
||||||
<p class="pb-2.5 text-lg text-gray-400">
|
@click="changeEmailFlag = true"
|
||||||
未设置密保问题
|
>
|
||||||
<a
|
修改
|
||||||
href="javascript:void(0)"
|
</el-button>
|
||||||
class="float-right text-blue-400"
|
</div>
|
||||||
>去设置</a
|
<div class="flex items-center gap-3 text-gray-600 dark:text-gray-300">
|
||||||
>
|
<el-icon class="text-purple-500"><lock /></el-icon>
|
||||||
</p>
|
<span class="font-medium">账号密码:</span>
|
||||||
</li>
|
<span>已设置</span>
|
||||||
<li class="borderd pt-2.5">
|
<el-button
|
||||||
<p class="pb-2.5 text-xl text-gray-600">修改密码</p>
|
link
|
||||||
<p class="pb-2.5 text-lg text-gray-400">
|
type="primary"
|
||||||
修改个人密码
|
class="ml-auto"
|
||||||
<a
|
@click="showPassword = true"
|
||||||
href="javascript:void(0)"
|
>
|
||||||
class="float-right text-blue-400"
|
修改
|
||||||
@click="showPassword = true"
|
</el-button>
|
||||||
>修改密码</a
|
</div>
|
||||||
>
|
</div>
|
||||||
</p>
|
</div>
|
||||||
</li>
|
|
||||||
</ul>
|
<div class="bg-white dark:bg-slate-800 rounded-xl p-6 profile-card">
|
||||||
|
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
|
||||||
|
<el-icon class="text-blue-500"><medal /></el-icon>
|
||||||
|
技能特长
|
||||||
|
</h2>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<el-tag effect="plain" type="success">GoLang</el-tag>
|
||||||
|
<el-tag effect="plain" type="warning">JavaScript</el-tag>
|
||||||
|
<el-tag effect="plain" type="danger">Vue</el-tag>
|
||||||
|
<el-tag effect="plain" type="info">Gorm</el-tag>
|
||||||
|
<el-button link class="text-sm">
|
||||||
|
<el-icon><plus /></el-icon>
|
||||||
|
添加技能
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧内容区 -->
|
||||||
|
<div class="lg:col-span-8">
|
||||||
|
<div class="bg-white dark:bg-slate-800 rounded-xl p-6 profile-card">
|
||||||
|
<el-tabs class="custom-tabs">
|
||||||
|
<el-tab-pane>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-icon><data-line /></el-icon>
|
||||||
|
数据统计
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 lg:gap-6 py-6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="text-2xl lg:text-4xl font-bold text-blue-500 mb-2">138</div>
|
||||||
|
<div class="text-gray-500 text-sm">项目参与</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="text-2xl lg:text-4xl font-bold text-green-500 mb-2">2.3k</div>
|
||||||
|
<div class="text-gray-500 text-sm">代码提交</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="text-2xl lg:text-4xl font-bold text-purple-500 mb-2">95%</div>
|
||||||
|
<div class="text-gray-500 text-sm">任务完成</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="text-2xl lg:text-4xl font-bold text-yellow-500 mb-2">12</div>
|
||||||
|
<div class="text-gray-500 text-sm">获得勋章</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-icon><calendar /></el-icon>
|
||||||
|
近期动态
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="py-6">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="(activity, index) in activities"
|
||||||
|
:key="index"
|
||||||
|
:type="activity.type"
|
||||||
|
:timestamp="activity.timestamp"
|
||||||
|
:hollow="true"
|
||||||
|
class="pb-6"
|
||||||
|
>
|
||||||
|
<h3 class="text-base font-medium mb-1">{{ activity.title }}</h3>
|
||||||
|
<p class="text-gray-500 text-sm">{{ activity.content }}</p>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 弹窗 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="showPassword"
|
v-model="showPassword"
|
||||||
title="修改密码"
|
title="修改密码"
|
||||||
width="360px"
|
width="400px"
|
||||||
|
class="custom-dialog"
|
||||||
@close="clearPassword"
|
@close="clearPassword"
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
ref="modifyPwdForm"
|
ref="modifyPwdForm"
|
||||||
:model="pwdModify"
|
:model="pwdModify"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
label-width="80px"
|
label-width="90px"
|
||||||
|
class="py-4"
|
||||||
>
|
>
|
||||||
<el-form-item :minlength="6" label="原密码" prop="password">
|
<el-form-item :minlength="6" label="原密码" prop="password">
|
||||||
<el-input v-model="pwdModify.password" show-password />
|
<el-input v-model="pwdModify.password" show-password />
|
||||||
@@ -185,72 +240,91 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-model="changePhoneFlag" title="绑定手机" width="600px">
|
<el-dialog
|
||||||
<el-form :model="phoneForm">
|
v-model="changePhoneFlag"
|
||||||
<el-form-item label="手机号" label-width="120px">
|
title="修改手机号"
|
||||||
<el-input
|
width="400px"
|
||||||
v-model="phoneForm.phone"
|
class="custom-dialog"
|
||||||
placeholder="请输入手机号"
|
>
|
||||||
autocomplete="off"
|
<el-form :model="phoneForm" label-width="80px" class="py-4">
|
||||||
/>
|
<el-form-item label="手机号">
|
||||||
|
<el-input v-model="phoneForm.phone" placeholder="请输入新的手机号码">
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon><phone /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="验证码" label-width="120px">
|
<el-form-item label="验证码">
|
||||||
<div class="flex w-full gap-4">
|
<div class="flex gap-4">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="phoneForm.code"
|
v-model="phoneForm.code"
|
||||||
|
placeholder="请输入验证码"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
autocomplete="off"
|
>
|
||||||
placeholder="请自行设计短信服务,此处为模拟随便写"
|
<template #prefix>
|
||||||
style="width: 300px"
|
<el-icon><key /></el-icon>
|
||||||
/>
|
</template>
|
||||||
<el-button type="primary" :disabled="time > 0" @click="getCode">{{
|
</el-input>
|
||||||
time > 0 ? `(${time}s)后重新获取` : '获取验证码'
|
<el-button
|
||||||
}}</el-button>
|
type="primary"
|
||||||
|
:disabled="time > 0"
|
||||||
|
class="w-32"
|
||||||
|
@click="getCode"
|
||||||
|
>
|
||||||
|
{{ time > 0 ? `${time}s` : '获取验证码' }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="closeChangePhone">取消</el-button>
|
<el-button @click="closeChangePhone">取 消</el-button>
|
||||||
<el-button type="primary" @click="changePhone">更改</el-button>
|
<el-button type="primary" @click="changePhone">确 定</el-button>
|
||||||
</span>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-model="changeEmailFlag" title="绑定邮箱" width="600px">
|
<el-dialog
|
||||||
<el-form :model="emailForm">
|
v-model="changeEmailFlag"
|
||||||
<el-form-item label="邮箱" label-width="120px">
|
title="修改邮箱"
|
||||||
<el-input
|
width="400px"
|
||||||
v-model="emailForm.email"
|
class="custom-dialog"
|
||||||
placeholder="请输入邮箱"
|
>
|
||||||
autocomplete="off"
|
<el-form :model="emailForm" label-width="80px" class="py-4">
|
||||||
/>
|
<el-form-item label="邮箱">
|
||||||
|
<el-input v-model="emailForm.email" placeholder="请输入新的邮箱地址">
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon><message /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="验证码" label-width="120px">
|
<el-form-item label="验证码">
|
||||||
<div class="flex w-full gap-4">
|
<div class="flex gap-4">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="emailForm.code"
|
v-model="emailForm.code"
|
||||||
|
placeholder="请输入验证码"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
placeholder="请自行设计邮件服务,此处为模拟随便写"
|
>
|
||||||
autocomplete="off"
|
<template #prefix>
|
||||||
style="width: 300px"
|
<el-icon><key /></el-icon>
|
||||||
/>
|
</template>
|
||||||
|
</el-input>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:disabled="emailTime > 0"
|
:disabled="emailTime > 0"
|
||||||
|
class="w-32"
|
||||||
@click="getEmailCode"
|
@click="getEmailCode"
|
||||||
>{{
|
|
||||||
emailTime > 0 ? `(${emailTime}s)后重新获取` : '获取验证码'
|
|
||||||
}}</el-button
|
|
||||||
>
|
>
|
||||||
|
{{ emailTime > 0 ? `${emailTime}s` : '获取验证码' }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="closeChangeEmail">取消</el-button>
|
<el-button @click="closeChangeEmail">取 消</el-button>
|
||||||
<el-button type="primary" @click="changeEmail">更改</el-button>
|
<el-button type="primary" @click="changeEmail">确 定</el-button>
|
||||||
</span>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -258,16 +332,22 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { setSelfInfo, changePassword } from '@/api/user.js'
|
import { setSelfInfo, changePassword } from '@/api/user.js'
|
||||||
import { reactive, ref, watch } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useUserStore } from '@/pinia/modules/user'
|
import { useUserStore } from '@/pinia/modules/user'
|
||||||
import SelectImage from '@/components/selectImage/selectImage.vue'
|
import ProfileAvatar from '@/components/Avatar/ProfileAvatar.vue'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Person'
|
name: 'Person'
|
||||||
})
|
})
|
||||||
|
|
||||||
const activeName = ref('second')
|
const userStore = useUserStore()
|
||||||
|
const modifyPwdForm = ref(null)
|
||||||
|
const showPassword = ref(false)
|
||||||
|
const pwdModify = ref({})
|
||||||
|
const nickName = ref('')
|
||||||
|
const editFlag = ref(false)
|
||||||
|
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
password: [
|
password: [
|
||||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
@@ -293,12 +373,6 @@
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const modifyPwdForm = ref(null)
|
|
||||||
const showPassword = ref(false)
|
|
||||||
const pwdModify = ref({})
|
|
||||||
const nickName = ref('')
|
|
||||||
const editFlag = ref(false)
|
|
||||||
const savePassword = async () => {
|
const savePassword = async () => {
|
||||||
modifyPwdForm.value.validate((valid) => {
|
modifyPwdForm.value.validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
@@ -311,8 +385,6 @@
|
|||||||
}
|
}
|
||||||
showPassword.value = false
|
showPassword.value = false
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -323,23 +395,9 @@
|
|||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmPassword: ''
|
confirmPassword: ''
|
||||||
}
|
}
|
||||||
modifyPwdForm.value.clearValidate()
|
modifyPwdForm.value?.clearValidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => userStore.userInfo.headerImg,
|
|
||||||
async (val) => {
|
|
||||||
const res = await setSelfInfo({ headerImg: val })
|
|
||||||
if (res.code === 0) {
|
|
||||||
userStore.ResetUserInfo({ headerImg: val })
|
|
||||||
ElMessage({
|
|
||||||
type: 'success',
|
|
||||||
message: '设置成功'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const openEdit = () => {
|
const openEdit = () => {
|
||||||
nickName.value = userStore.userInfo.nickName
|
nickName.value = userStore.userInfo.nickName
|
||||||
editFlag.value = true
|
editFlag.value = true
|
||||||
@@ -356,19 +414,12 @@
|
|||||||
})
|
})
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
userStore.ResetUserInfo({ nickName: nickName.value })
|
userStore.ResetUserInfo({ nickName: nickName.value })
|
||||||
ElMessage({
|
ElMessage.success('修改成功')
|
||||||
type: 'success',
|
|
||||||
message: '设置成功'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
nickName.value = ''
|
nickName.value = ''
|
||||||
editFlag.value = false
|
editFlag.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClick = (tab, event) => {
|
|
||||||
console.log(tab, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const changePhoneFlag = ref(false)
|
const changePhoneFlag = ref(false)
|
||||||
const time = ref(0)
|
const time = ref(0)
|
||||||
const phoneForm = reactive({
|
const phoneForm = reactive({
|
||||||
@@ -434,17 +485,118 @@
|
|||||||
closeChangeEmail()
|
closeChangeEmail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
const handleAvatarChange = async (newUrl) => {
|
||||||
.borderd {
|
const res = await setSelfInfo({ headerImg: newUrl })
|
||||||
@apply border-b-2 border-solid border-gray-100 dark:border-gray-500 border-t-0 border-r-0 border-l-0;
|
if (res.code === 0) {
|
||||||
&:last-child {
|
userStore.ResetUserInfo({ headerImg: newUrl })
|
||||||
@apply border-b-0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-list {
|
// 添加活动数据
|
||||||
@apply w-full whitespace-nowrap overflow-hidden text-ellipsis py-3 text-lg text-gray-700;
|
const activities = [
|
||||||
|
{
|
||||||
|
timestamp: '2024-01-10',
|
||||||
|
title: '完成项目里程碑',
|
||||||
|
content: '成功完成第三季度主要项目开发任务,获得团队一致好评',
|
||||||
|
type: 'primary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: '2024-01-11',
|
||||||
|
title: '代码审核完成',
|
||||||
|
content: '完成核心模块代码审核,提出多项改进建议并获采纳',
|
||||||
|
type: 'success'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: '2024-01-12',
|
||||||
|
title: '技术分享会',
|
||||||
|
content: '主持团队技术分享会,分享前端性能优化经验',
|
||||||
|
type: 'warning'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: '2024-01-13',
|
||||||
|
title: '新功能上线',
|
||||||
|
content: '成功上线用户反馈的新特性,显著提升用户体验',
|
||||||
|
type: 'danger'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.profile-container {
|
||||||
|
@apply p-4 lg:p-6 min-h-screen bg-gray-50 dark:bg-slate-900;
|
||||||
|
|
||||||
|
.bg-pattern {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23000000' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card {
|
||||||
|
@apply shadow-sm hover:shadow-md transition-shadow duration-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-action-btn {
|
||||||
|
@apply bg-white/10 hover:bg-white/20 border-white/20;
|
||||||
|
.el-icon {
|
||||||
|
@apply mr-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
@apply p-4 lg:p-6 rounded-lg bg-gray-50 dark:bg-slate-700/50 text-center hover:shadow-md transition-all duration-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs {
|
||||||
|
:deep(.el-tabs__nav-wrap::after) {
|
||||||
|
@apply h-0.5 bg-gray-100 dark:bg-gray-700;
|
||||||
|
}
|
||||||
|
:deep(.el-tabs__active-bar) {
|
||||||
|
@apply h-0.5 bg-blue-500;
|
||||||
|
}
|
||||||
|
:deep(.el-tabs__item) {
|
||||||
|
@apply text-base font-medium px-6;
|
||||||
|
.el-icon {
|
||||||
|
@apply mr-1 text-lg;
|
||||||
|
}
|
||||||
|
&.is-active {
|
||||||
|
@apply text-blue-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.el-timeline-item__node--normal) {
|
||||||
|
@apply left-[-2px];
|
||||||
|
}
|
||||||
|
:deep(.el-timeline-item__wrapper) {
|
||||||
|
@apply pl-8;
|
||||||
|
}
|
||||||
|
:deep(.el-timeline-item__timestamp) {
|
||||||
|
@apply text-gray-400 text-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dialog {
|
||||||
|
:deep(.el-dialog__header) {
|
||||||
|
@apply mb-0 pb-4 border-b border-gray-100 dark:border-gray-700;
|
||||||
|
}
|
||||||
|
:deep(.el-dialog__footer) {
|
||||||
|
@apply mt-0 pt-4 border-t border-gray-100 dark:border-gray-700;
|
||||||
|
}
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
@apply shadow-none;
|
||||||
|
}
|
||||||
|
:deep(.el-input__prefix) {
|
||||||
|
@apply mr-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-input {
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
@apply bg-white/10 border-white/20 shadow-none;
|
||||||
|
input {
|
||||||
|
@apply text-white;
|
||||||
|
&::placeholder {
|
||||||
|
@apply text-white/60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Reference in New Issue
Block a user