Skip to content

个人中心

image-20250611080335686

个人中心

介绍

个人中心页面实现了一个用户个人信息页面,包含左侧用户信息卡片和右侧可切换的用户基本信息与修改密码选项卡。

  • 代码:src\views\Profile\Index.vue
vue
<template>
  <div class="flex">
    <el-card class="user w-1/3" shadow="hover">
      <template #header>
        <div class="card-header">
          <span>{{ t('profile.user.title') }}</span>
        </div>
      </template>
      <ProfileUser />
    </el-card>
    <el-card class="user ml-3 w-2/3" shadow="hover">
      <div>
        <el-tabs v-model="activeName" class="profile-tabs" style="height: 400px" tab-position="top">
          <el-tab-pane :label="t('profile.info.basicInfo')" name="basicInfo">
            <BasicInfo />
          </el-tab-pane>
          <el-tab-pane :label="t('profile.info.resetPwd')" name="resetPwd">
            <ResetPwd />
          </el-tab-pane>
        </el-tabs>
      </div>
    </el-card>
  </div>
</template>

个人信息子组件

ProfileUser 该 Vue 组件用于展示用户个人信息,从接口获取数据并展示用户名、手机号、邮箱、部门、岗位、角色及创建时间等字段,并使用了国际化([t()](javascript:void(0)))和时间格式化(formatDate)功能。

  • 代码:src\views\Profile\components\ProfileUser.vue
vue
<template>
  <div>
    <div class="text-center">
      <UserAvatar :img="userInfo?.avatar" />
    </div>
    <ul class="list-group list-group-striped">
      <li class="list-group-item">
        <Icon class="mr-5px" icon="ep:user" />
        {{ t('profile.user.username') }}
        <div class="pull-right">{{ userInfo?.username }}</div>
      </li>
      <li class="list-group-item">
        <Icon class="mr-5px" icon="ep:phone" />
        {{ t('profile.user.mobile') }}
        <div class="pull-right">{{ userInfo?.mobile }}</div>
      </li>
...
  • 包含了 UserAvatar 头像上传组件。代码:src\views\Profile\components\UserAvatar.vue
vue
<template>
  <div class="change-avatar">
    <CropperAvatar
      ref="cropperRef"
      :btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
      :showBtn="false"
      :value="img"
      width="120px"
      @change="handelUpload"
    />
  </div>
</template>
  • 包含了 CropperAvatar 头像裁剪组件。代码:src\components\Cropper\src\CropperAvatar.vue
vue
<template>
  <div class="user-info-head" @click="open()">
    <el-avatar v-if="sourceValue" :src="sourceValue" alt="avatar" class="img-circle img-lg" />
    <el-avatar v-if="!sourceValue" :src="avatar" alt="avatar" class="img-circle img-lg" />
    <el-button v-if="showBtn" :class="`${prefixCls}-upload-btn`" @click="open()">
      {{ btnText ? btnText : t('cropper.selectImage') }}
    </el-button>
    <CopperModal
      ref="cropperModelRef"
      :srcValue="sourceValue"
      @upload-success="handleUploadSuccess"
    />
  </div>
</template>

基本设置子组件

BasicInfo 该 Vue 组件实现用户基本信息的展示与编辑功能,使用了自定义 Form 组件和 Element Plus 的表单验证机制。

  • 模板部分:
    • 使用 <Form> 组件绑定表单数据 schema 和校验规则 rules。
    • 自定义插槽 #sex 渲染性别选择的单选按钮组(男、女)。
    • 页面底部显示“保存”和“重置”按钮,分别触发 submit() 和 init() 方法。
  • 脚本部分:
    • 定义国际化文案 [t](javascript:void(0))、消息提示 message 和用户仓库 userStore。
    • rules 定义昵称、邮箱、手机号的校验规则。
    • schema 定义表单项结构,包括字段名、标签和组件类型。
    • formRef 获取表单实例,用于操作表单数据。
    • submit() 方法进行表单验证并提交更新用户信息。
    • init() 方法加载用户信息并填充到表单中。
    • onMounted 钩子在页面加载时初始化表单数据。
  • 代码:src\views\Profile\components\BasicInfo.vue
vue
<template>
  <Form ref="formRef" :labelWidth="200" :rules="rules" :schema="schema">
    <template #sex="form">
      <el-radio-group v-model="form['sex']">
        <el-radio :value="1">{{ t('profile.user.man') }}</el-radio>
        <el-radio :value="2">{{ t('profile.user.woman') }}</el-radio>
      </el-radio-group>
    </template>
  </Form>
  <div style="text-align: center">
    <XButton :title="t('common.save')" type="primary" @click="submit()" />
    <XButton :title="t('common.reset')" type="danger" @click="init()" />
  </div>
</template>
<script lang="ts" setup>
import type { FormRules } from 'element-plus'
import { FormSchema } from '@/types/form'
import type { FormExpose } from '@/components/Form'
import {
  getUserProfile,
  updateUserProfile,
  UserProfileUpdateReqVO
} from '@/api/system/user/profile'
import { useUserStore } from '@/store/modules/user'

defineOptions({ name: 'BasicInfo' })

const { t } = useI18n()
const message = useMessage() // 消息弹窗
const userStore = useUserStore()
// 表单校验
const rules = reactive<FormRules>({
  nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }],
  email: [
    { required: true, message: t('profile.rules.mail'), trigger: 'blur' },
    {
      type: 'email',
      message: t('profile.rules.truemail'),
      trigger: ['blur', 'change']
    }
  ],
  mobile: [
    { required: true, message: t('profile.rules.phone'), trigger: 'blur' },
    {
      pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
      message: t('profile.rules.truephone'),
      trigger: 'blur'
    }
  ]
})
const schema = reactive<FormSchema[]>([
  {
    field: 'nickname',
    label: t('profile.user.nickname'),
    component: 'Input'
  },
  {
    field: 'mobile',
    label: t('profile.user.mobile'),
    component: 'Input'
  },
  {
    field: 'email',
    label: t('profile.user.email'),
    component: 'Input'
  },
  {
    field: 'sex',
    label: t('profile.user.sex'),
    component: 'InputNumber',
    value: 0
  }
])
const formRef = ref<FormExpose>() // 表单 Ref
const submit = () => {
  const elForm = unref(formRef)?.getElFormRef()
  if (!elForm) return
  elForm.validate(async (valid) => {
    if (valid) {
      const data = unref(formRef)?.formModel as UserProfileUpdateReqVO
      await updateUserProfile(data)
      message.success(t('common.updateSuccess'))
      const profile = await init()
      userStore.setUserNicknameAction(profile.nickname)
    }
  })
}
const init = async () => {
  const res = await getUserProfile()
  unref(formRef)?.setValues(res)
  return res
}
onMounted(async () => {
  await init()
})
</script>

密码设置子组件

ResetPwd 该 Vue 组件实现了用户修改密码的功能,包含表单验证和提交逻辑:

  • 使用 el-form 构建表单,包含旧密码、新密码、确认密码三项和保存/重置按钮;
  • 通过 [rules](javascript:void(0)) 定义表单校验规则,确保密码长度及一致性(确认密码需与新密码一致);
  • [submit](javascript:void(0)) 方法验证表单并调用接口更新密码;
  • [reset](javascript:void(0)) 方法用于重置表单内容。
vue
<template>
  <el-form ref="formRef" :model="password" :rules="rules" :label-width="200">
    <el-form-item :label="t('profile.password.oldPassword')" prop="oldPassword">
      <InputPassword v-model="password.oldPassword" />
    </el-form-item>
    <el-form-item :label="t('profile.password.newPassword')" prop="newPassword">
      <InputPassword v-model="password.newPassword" strength />
    </el-form-item>
    <el-form-item :label="t('profile.password.confirmPassword')" prop="confirmPassword">
      <InputPassword v-model="password.confirmPassword" strength />
    </el-form-item>
    <el-form-item>
      <XButton :title="t('common.save')" type="primary" @click="submit(formRef)" />
      <XButton :title="t('common.reset')" type="danger" @click="reset(formRef)" />
    </el-form-item>
  </el-form>
</template>

参考资料