Files
front-pc/pages/personal-Center/index/personal-profile.vue
2025-09-16 22:07:36 +08:00

435 lines
15 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="box-border h-[682px] w-[913px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] px-[30px] py-[21px]">
<!-- <div class="flex items-center justify-between border-b-[1px] border-b-[#eeeeee] border-b-solid pb-[18px]">
<div class="text-[16px] text-[#333333] font-normal">个人资料</div>
<div class="flex items-center">
<img src="~/assets/images/fans.png" alt="" srcset="" />
<span class="ml-[8px] text-[14px] text-[#333333] font-normal"></span>
</div>
</div> -->
<div class="user-profile-container">
<div class="avatar-section">
<el-upload class="avatar-uploader" action="#" :show-file-list="false" :auto-upload="false" :on-change="handleAvatarChange">
<div class="flex flex-col items-center">
<el-avatar :size="100" :src="userForm.avatar" />
<div class="mt-[15px]">
<el-button type="primary" plain>更改头像</el-button>
</div>
</div>
</el-upload>
</div>
<el-form ref="userFormRef" :model="userForm" label-width="120px" class="profile-form" :rules="rules">
<!-- User avatar section -->
<!-- User information section -->
<el-form-item label="用户名:" prop="nickname" :rules="[{ required: true, message: '请输入用户名', trigger: 'blur' }]">
<div class="flex items-center">
<el-input v-model="userForm.nickname" class="w-[247px]" />
<el-button type="primary" class="verify-btn" @click="handleVerify">实名认证</el-button>
</div>
</el-form-item>
<!-- <el-form-item label="真实姓名:" prop="trueName">
<div class="flex items-center">
<el-input v-model="userForm.trueName" placeholder="请输入真实姓名" class="w-247px" />
<el-button type="primary" class="verify-btn" @click="handleVerify">实名认证</el-button>
</div>
</el-form-item> -->
<el-form-item label="手机号:" prop="phone" :rules="[{ required: true, message: '请输入手机号', trigger: 'blur' }]">
<div class="flex items-center">
<el-input v-model="userForm.phone" disabled class="w-[247px]" />
<!-- <el-link type="primary" class="modify-link">修改</el-link> -->
</div>
</el-form-item>
<el-form-item label="电子邮箱:" prop="email" :rules="[{ required: true, message: '请输入电子邮箱', trigger: 'blur' }]">
<div class="flex items-center">
<el-input v-model="userForm.email" placeholder="请输入电子邮箱" class="w-[247px]" />
<!-- <el-link type="primary" class="modify-link">绑定</el-link> -->
</div>
</el-form-item>
<div class="flex items-center">
<el-form-item label="所在地区:" prop="isDomestic" :rules="[{ required: false, message: '请选择所在地区', trigger: 'change' }]">
<el-select v-model="userForm.isDomestic" placeholder="请选择" class="w-[120px]!" @change="handleCountryChange">
<el-option label="国内" :value="1"></el-option>
<el-option label="国外" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label-width="6px" prop="province" :rules="[{ required: false, message: '请选择省份', trigger: 'change' }]">
<el-select v-model="userForm.province" placeholder="请选择省份" class="w-[120px]!">
<el-option v-for="item in provinceList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<!-- <el-form-item label-width="6px" prop="city" :rules="[{ required: true, message: '请选择城市', trigger: 'change' }]">
<el-select v-model="userForm.city" placeholder="请选择城市" class="w-[120px]!" @change="handleCityChange">
<el-option v-for="item in cityList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label-width="6px" prop="county" :rules="[{ required: true, message: '请选择区县', trigger: 'change' }]">
<el-select v-model="userForm.county" placeholder="请选择区县" class="w-[120px]!">
<el-option v-for="item in countyList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item> -->
</div>
<el-form-item label="技能标签:" prop="labels" :rules="[{ required: false, message: '请选择技能标签', trigger: 'change' }]">
<el-select
v-model="userForm.labels"
:remote-method="remoteMethod"
:loading="loading"
multiple
filterable
remote
placeholder="请输入搜索标签"
class="w-[498px]!"
>
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="技能证书:" prop="files" :rules="[{ required: false, message: '请上传技能证书', trigger: 'change' }]">
<KlUploader
v-model:file-list="userForm.files"
list-type="picture-card"
:limit="1000"
:size="1"
tips="上传图片支持jpg/gif/png格式、第一张为封面图片、每张图片大小不得超过1M"
>
<div class="h-[77px] w-[161px] flex items-center justify-center bg-[#fafafa]">
<el-icon class="text-[#999999]"><Plus /></el-icon>
<div class="ml-[4px] mt-[2px] text-[14px] text-[#999999] font-normal">上传图纸</div>
</div>
</KlUploader>
</el-form-item>
<el-form-item label="个人简介:" prop="description" :rules="[{ required: true, message: '请输入个人简介', trigger: 'blur' }]">
<el-input v-model="userForm.description" type="textarea" :rows="5" placeholder="请输入个人简介" class="full-width" />
</el-form-item>
<el-form-item class="relative">
<el-button type="primary" class="w-[120px] !h-[37px]" :loading="submitLoading" @click="submitForm">提交</el-button>
<div class="absolute top-[40px] left-[0] text-[12px] text-[#999999] font-normal">温馨提示更改头像需要重新登录生效</div>
</el-form-item>
</el-form>
</div>
<!-- <div class="flex items-center justify-between border-b-[1px] border-b-[#eeeeee] border-b-solid pb-[18px]">
<div class="text-[16px] text-[#333333] font-normal">社交帐号绑定</div>
</div>
<div class="flex flex-col justify-center text-[14px] text-[#333333] font-normal">
<div class="mt-[30px] flex items-center">
<img src="~/assets/images/qq-v2.png" alt="" srcset="" class="h-[35px] w-[34px]" />
<div class="ml-[19px]">QQ</div>
<div class="ml-[100px] flex items-center"><div class="w-[90px]">QQ昵称</div><div class="w-[180px]">xxx</div></div>
<div class="btn">绑定</div>
</div>
<div class="mt-[30px] flex items-center">
<img src="~/assets/images/weixin-v2.png" alt="" srcset="" class="h-[35px] w-[34px]" />
<div class="ml-[19px]">微信</div>
<div class="ml-[95px] flex items-center"><div class="w-[90px]">微信昵称</div><div class="w-[180px]">xxx</div></div>
<div class="btn">绑定</div>
</div>
</div> -->
</div>
<verifyDialog ref="verifyDialogRef" />
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { Plus } from '@element-plus/icons-vue'
import { tree, upload } from '~/api/common/index'
import { keywords } from '~/api/upnew/index'
import { userExtend, getUserInfo, updateUserExtend } from '~/api/personal-center/index'
import type { UserExtendSaveReqVO } from '~/api/personal-center/types'
import verifyDialog from './verify-dialog.vue'
const userFormRef = ref()
const userForm = reactive<UserExtendSaveReqVO>({
id: undefined,
phone: '',
username: '',
avatar: '',
trueName: '',
city: '',
email: '',
isDomestic: undefined,
area: '',
country: '',
province: '',
county: '',
labels: [],
description: '这个工程师很懒 什么都没写',
authStatus: 0,
files: [],
nickname: '',
})
const rules = {
username: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
city: [{ required: true, message: '请选择城市', trigger: 'change' }],
email: [
{ required: false, message: '请输入电子邮箱', trigger: 'blur' },
{ type: 'email' as const, message: '请输入正确的邮箱格式', trigger: 'blur' },
],
// 标签
labels: [{ required: false, message: '请选择技能标签', trigger: 'change' }],
// 技能证书
files: [{ required: false, message: '请上传技能证书', trigger: 'change' }],
// 所在地区
isDomestic: [{ required: false, message: '请选择国家', trigger: 'change' }],
province: [{ required: false, message: '请选择省份', trigger: 'change' }],
county: [{ required: false, message: '请选择区县', trigger: 'change' }],
}
const verifyDialogRef = ref()
const handleVerify = () => {
verifyDialogRef.value.open()
}
// 省份地址
const provinceList = ref()
// 城市地址
const cityList = ref()
// 区县地址
const countyList = ref()
// 获取地址
const getAdress = async (type: string, val?: any) => {
const res = await tree({
id: val,
})
if (res.code === 0) {
if (type === 'province') {
provinceList.value = res.data
} else if (type === 'city') {
cityList.value = res.data
} else if (type === 'county') {
countyList.value = res.data
}
}
}
// 切换国家
const handleCountryChange = (value: any) => {
userForm.province = ''
userForm.city = ''
userForm.county = ''
provinceList.value = []
cityList.value = []
countyList.value = []
getAdress('province', value)
}
// 监听省份变化
const handleProvinceChange = (value: string) => {
userForm.city = ''
userForm.county = ''
cityList.value = []
countyList.value = []
getAdress('city', value)
}
// 监听城市变化
const handleCityChange = (value: string) => {
userForm.county = ''
countyList.value = []
getAdress('county', value)
}
// 处理头像上传变化
const handleAvatarChange = (file: any) => {
// 这里可以添加文件类型和大小的验证
if (file) {
// 创建本地预览URL
userForm.avatar = URL.createObjectURL(file.raw)
// 这里可以添加实际的上传逻辑
console.log('Avatar file:', file)
// 上传文件到服务器
const formData = new FormData()
formData.append('fieldName', file?.name)
formData.append('file', file.raw as Blob)
upload('/prod-api/app-api/infra/file/upload', formData)
.then((response) => {
if (response.code === 0) {
userForm.avatar = response.data
}
})
.catch((error) => {
console.error('Error uploading avatar:', error)
})
}
}
const loading = ref(false)
/** 获取标签 */
const labelsList = ref<any>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
keywords({
type: 1,
keywords: query,
})
.then((res) => {
labelsList.value = res.data
})
.finally(() => {
loading.value = false
})
} else {
labelsList.value = []
}
}
// 提交表单
const submitLoading = ref(false)
const submitForm = async () => {
if (!userFormRef.value) return
try {
await userFormRef.value.validate()
submitLoading.value = true
const res = userForm.id ? await updateUserExtend(userForm) : await userExtend(userForm)
if (res.code === 0) {
ElMessage.success('个人信息提交成功')
} else {
ElMessage.error('个人信息提交失败')
}
} catch (error) {
console.error('Validation failed:', error)
ElMessage.error('表单验证失败,请检查输入')
} finally {
submitLoading.value = false
}
}
// 初始回显
const init = async () => {
const res = await getUserInfo()
if (res.code === 0) {
userForm.id = res.data.id
userForm.nickname = res.data.nickname
userForm.phone = res.data.mobile
userForm.email = res.data.email
userForm.isDomestic = +res.data.isDomestic
userForm.country = res.data.country
await getAdress('province', userForm.isDomestic)
// @ts-ignore
userForm.province = res.data.province
// await getAdress('city', userForm.province)
// @ts-ignore
userForm.city = +res.data.city
// await getAdress('county', userForm.city)
// @ts-ignore
userForm.county = +res.data.county
userForm.labels = res.data.labels
userForm.description = res.data.description
userForm.avatar = res.data.avatar
userForm.files = res.data.files.map((item: any) => {
return {
...item,
url: item.url,
name: item.title,
uid: item.id,
status: 'success',
}
})
}
}
init()
</script>
<style scoped>
.btn {
width: 91px;
height: 37px;
background: #1a65ff;
border-radius: 4px;
border: 1px solid #1a65ff;
text-align: center;
line-height: 37px;
font-weight: 400;
font-size: 14px;
color: #ffffff;
margin-left: 30px;
}
.user-profile-container {
margin: 0 auto;
padding: 30px;
display: flex;
}
.profile-form {
/* margin-top: 20px; */
}
.avatar-section {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 30px;
}
.update-avatar-btn {
margin-top: 15px;
}
.verify-btn {
margin-left: 10px;
}
.modify-link {
margin-left: 10px;
min-width: fit-content;
white-space: nowrap;
}
.certificate-uploader {
width: 100%;
}
.upload-area {
width: 200px;
height: 100px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}
.upload-area:hover {
border-color: #409eff;
}
.full-width {
width: 100%;
}
.flex {
display: flex;
}
.gap-2 {
gap: 0.5rem;
}
.w-120px {
width: 120px;
}
/* 响应式布局调整 */
@media (max-width: 768px) {
.user-profile-container {
padding: 20px 10px;
}
.verify-btn {
margin-top: 10px;
margin-left: 0;
}
}
</style>