功能模块:添加演示和个人中心页面
- 添加演示页面(src/views/demo/) - 功能演示示例 - 添加个人中心页面(src/views/personal-center/) - 个人信息管理 - 账号设置
This commit is contained in:
parent
387e5f75d1
commit
5e42eb9930
14
src/views/demo/editor/md/index.vue
Normal file
14
src/views/demo/editor/md/index.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<n-card title="MarkDown编辑器">
|
||||
<n-space vertical :size="12">
|
||||
<n-alert title="基于 md-editor-v3 封装" type="success" />
|
||||
<MarkDownEditor v-model="text" />
|
||||
</n-space>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const text = ref('# Hello Editor ')
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
38
src/views/demo/editor/rich/index.vue
Normal file
38
src/views/demo/editor/rich/index.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<n-card title="富文本编辑器">
|
||||
<n-space vertical :size="12">
|
||||
<n-alert title="基于 Quill 封装" type="success" />
|
||||
<n-switch v-model:value="active">
|
||||
<template #checked>
|
||||
禁用
|
||||
</template>
|
||||
<template #unchecked>
|
||||
启用
|
||||
</template>
|
||||
</n-switch>
|
||||
<n-space :size="12">
|
||||
<div class="h-300px">
|
||||
<RichTextEditor v-model="text" :disabled="active" />
|
||||
</div>
|
||||
<div>
|
||||
<n-h2>v-html 预览</n-h2>
|
||||
<div v-html="text" />
|
||||
</div>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const text = ref('')
|
||||
// 模拟 ajax 异步获取内容
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
text.value = '<p>模拟 Ajax 异步设置内容</p>'
|
||||
}, 1500)
|
||||
})
|
||||
|
||||
const active = ref(false)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
32
src/views/demo/icons/index.vue
Normal file
32
src/views/demo/icons/index.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-card title="图标选择器">
|
||||
<icon-select />
|
||||
</n-card>
|
||||
<n-card title="自动导入图标">
|
||||
<div>
|
||||
正常:<icon-park-outline-apple />
|
||||
</div>
|
||||
<div>
|
||||
大:<icon-park-outline-apple class="text-2em" />
|
||||
</div>
|
||||
<div>
|
||||
大大大:<icon-park-outline-apple class="text-4em" />
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="自动导入svg图标">
|
||||
<div>
|
||||
正常:<svg-icons-cool />
|
||||
</div>
|
||||
<div>
|
||||
大:<svg-icons-cool class="text-2em" />
|
||||
</div>
|
||||
<div>
|
||||
大大大:<svg-icons-cool class="text-4em" />
|
||||
</div>
|
||||
<div>
|
||||
CoiIcon组件加载:<CoiIcon icon="local:cool" />
|
||||
</div>
|
||||
</n-card>
|
||||
</n-space>
|
||||
</template>
|
||||
661
src/views/personal-center/index.vue
Normal file
661
src/views/personal-center/index.vue
Normal file
@ -0,0 +1,661 @@
|
||||
<template>
|
||||
<div class="p-1 bg-gray-50 h-screen flex flex-col">
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-100 flex-1 overflow-auto">
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<!-- 左侧个人信息卡片 -->
|
||||
<div class="lg:col-span-1">
|
||||
<n-card title="个人信息" class="h-fit">
|
||||
<n-spin :show="loading">
|
||||
<div class="text-center mb-6">
|
||||
<!-- 头像 -->
|
||||
<div class="relative inline-block">
|
||||
<n-avatar
|
||||
:size="100"
|
||||
:src="personalData.avatar"
|
||||
round
|
||||
class="border-4 border-blue-100 cursor-pointer hover:border-blue-300 transition-colors"
|
||||
@click="handleAvatarClick"
|
||||
>
|
||||
<template #placeholder>
|
||||
<icon-park-outline-user class="text-4xl" />
|
||||
</template>
|
||||
</n-avatar>
|
||||
<!-- 头像上传图标 -->
|
||||
<div
|
||||
class="absolute bottom-0 right-0 w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center cursor-pointer hover:bg-blue-600 transition-colors shadow-md"
|
||||
@click="handleAvatarClick"
|
||||
>
|
||||
<icon-park-outline-camera class="text-white text-sm" />
|
||||
</div>
|
||||
<!-- 上传中遮罩 -->
|
||||
<div
|
||||
v-if="uploading"
|
||||
class="absolute inset-0 bg-black bg-opacity-50 rounded-full flex items-center justify-center"
|
||||
>
|
||||
<n-spin size="small" stroke="white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 隐藏的文件输入 -->
|
||||
<input
|
||||
ref="avatarUploadRef"
|
||||
type="file"
|
||||
accept="image/jpeg,image/jpg,image/png,image/gif"
|
||||
class="hidden"
|
||||
@change="handleAvatarChange"
|
||||
>
|
||||
|
||||
<!-- 头像上传提示 -->
|
||||
<p class="text-xs text-gray-400 mt-4">
|
||||
点击头像上传新头像
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 详细信息 -->
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">登录账号</span>
|
||||
<span class="text-gray-900 text-sm">{{ personalData.loginName || '未设置' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">用户姓名</span>
|
||||
<span class="text-gray-900 text-sm">{{ personalData.userName || '未设置' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">用户角色</span>
|
||||
<div>
|
||||
<n-tag
|
||||
v-for="(roleName, index) in getRoleNames()"
|
||||
:key="index"
|
||||
type="primary"
|
||||
size="small"
|
||||
class="mr-1"
|
||||
>
|
||||
{{ roleName }}
|
||||
</n-tag>
|
||||
<span v-if="getRoleNames().length === 0" class="text-gray-500">未分配角色</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-gray-600">邮箱</span>
|
||||
<span class="text-gray-900">{{ personalData.email || '未设置' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">手机号</span>
|
||||
<span class="text-gray-900">{{ personalData.phone || '未设置' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">性别</span>
|
||||
<span class="text-gray-900">{{ getGenderText(personalData.sex || '3') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">状态</span>
|
||||
<DictTag dict-type="sys_switch_status" :value="personalData.userStatus || '0'" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center py-2">
|
||||
<span class="text-gray-600">创建日期</span>
|
||||
<span class="text-gray-900 text-sm">{{ formatDate(personalData.createTime || '') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</n-spin>
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
<!-- 右侧操作区域 -->
|
||||
<div class="lg:col-span-2">
|
||||
<n-card>
|
||||
<n-tabs type="line" animated>
|
||||
<!-- 基本资料 -->
|
||||
<n-tab-pane name="basic" tab="基本资料">
|
||||
<div class="pt-4">
|
||||
<!-- 基本资料表单 -->
|
||||
<n-form
|
||||
ref="basicFormRef"
|
||||
:model="basicForm"
|
||||
:rules="basicRules"
|
||||
label-placement="left"
|
||||
label-width="100px"
|
||||
require-mark-placement="right-hanging"
|
||||
>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<n-form-item label="用户名" path="userName">
|
||||
<n-input v-model:value="basicForm.userName" placeholder="请输入用户名" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="性别" path="sex">
|
||||
<n-select
|
||||
v-model:value="basicForm.sex"
|
||||
:options="genderOptions"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="邮箱地址" path="email">
|
||||
<n-input v-model:value="basicForm.email" placeholder="请输入邮箱地址" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="手机号码" path="phone">
|
||||
<n-input v-model:value="basicForm.phone" placeholder="请输入手机号码" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
</n-form>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex justify-start mt-6">
|
||||
<n-space>
|
||||
<n-button :loading="loading" @click="handleBasicReset">
|
||||
<template #icon>
|
||||
<icon-park-outline-refresh />
|
||||
</template>
|
||||
重置
|
||||
</n-button>
|
||||
<n-button type="primary" :loading="loading" @click="handleBasicSave">
|
||||
<template #icon>
|
||||
<icon-park-outline-save />
|
||||
</template>
|
||||
保存
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
|
||||
<!-- 修改密码 -->
|
||||
<n-tab-pane name="password" tab="修改密码">
|
||||
<div class="pt-4">
|
||||
<!-- 密码安全提示 -->
|
||||
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
||||
<div class="flex items-start">
|
||||
<icon-park-outline-info class="text-yellow-600 text-lg mt-0.5 mr-3" />
|
||||
<div>
|
||||
<h4 class="text-yellow-800 font-medium">
|
||||
密码安全提示
|
||||
</h4>
|
||||
<p class="text-yellow-700 text-sm mt-1">
|
||||
为了您的账户安全,建议定期更换密码。新密码应包含字母、数字,长度不少于6位。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 密码修改表单 -->
|
||||
<n-form
|
||||
ref="passwordFormRef"
|
||||
:model="passwordForm"
|
||||
:rules="passwordRules"
|
||||
label-placement="left"
|
||||
label-width="100px"
|
||||
require-mark-placement="right-hanging"
|
||||
>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<n-form-item label="当前密码" path="password">
|
||||
<n-input
|
||||
v-model:value="passwordForm.password"
|
||||
type="password"
|
||||
show-password-on="click"
|
||||
placeholder="请输入当前密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="新密码" path="newPassword">
|
||||
<n-input
|
||||
v-model:value="passwordForm.newPassword"
|
||||
type="password"
|
||||
show-password-on="click"
|
||||
placeholder="请输入新密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="确认新密码" path="confirmPassword" class="md:col-span-2">
|
||||
<n-input
|
||||
v-model:value="passwordForm.confirmPassword"
|
||||
type="password"
|
||||
show-password-on="click"
|
||||
placeholder="请再次输入新密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
</div>
|
||||
</n-form>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex justify-start mt-6">
|
||||
<n-space>
|
||||
<n-button @click="handlePasswordReset">
|
||||
<template #icon>
|
||||
<icon-park-outline-refresh />
|
||||
</template>
|
||||
重置
|
||||
</n-button>
|
||||
<n-button type="warning" :loading="loading" @click="handlePasswordSubmit">
|
||||
<template #icon>
|
||||
<icon-park-outline-lock />
|
||||
</template>
|
||||
修改密码
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import { coiMsgError, coiMsgSuccess, coiMsgWarning } from '@/utils/coi'
|
||||
import { getPersonalData, updateBasicData, updatePassword, uploadAvatar } from '@/service/api/personal'
|
||||
import type { PersonalDataVo, UpdatePasswordBo, UpdatePersonalBo } from '@/service/api/personal'
|
||||
import { serviceConfig } from '@/../service.config'
|
||||
import DictTag from '@/components/common/DictTag.vue'
|
||||
import { useDict } from '@/hooks'
|
||||
|
||||
// 获取用户信息
|
||||
const authStore = useAuthStore()
|
||||
|
||||
// 页面数据
|
||||
const loading = ref(false)
|
||||
const uploading = ref(false)
|
||||
const personalData = ref<PersonalDataVo>({
|
||||
userId: 0,
|
||||
userName: '',
|
||||
loginName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
sex: '1',
|
||||
avatar: '',
|
||||
userStatus: '0',
|
||||
createTime: '',
|
||||
roleNames: [],
|
||||
roleName: '',
|
||||
})
|
||||
|
||||
// 表单引用
|
||||
const basicFormRef = ref()
|
||||
const passwordFormRef = ref()
|
||||
const avatarUploadRef = ref()
|
||||
|
||||
// 编辑基本信息表单
|
||||
const basicForm = ref<UpdatePersonalBo>({
|
||||
userName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
sex: '1',
|
||||
avatar: '',
|
||||
})
|
||||
|
||||
// 修改密码表单
|
||||
const passwordForm = ref<UpdatePasswordBo>({
|
||||
password: '',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const basicRules = {
|
||||
userName: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
],
|
||||
email: [
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' },
|
||||
],
|
||||
phone: [
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
const passwordRules = {
|
||||
password: [
|
||||
{ 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: any, value: string) => {
|
||||
if (value !== passwordForm.value.newPassword) {
|
||||
return new Error('两次输入密码不一致')
|
||||
}
|
||||
return true
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { getSelectOptions, getDictLabel } = useDict(['sys_user_sex', 'sys_switch_status'])
|
||||
|
||||
const genderOptions = computed(() => getSelectOptions('sys_user_sex'))
|
||||
|
||||
function getGenderText(sex: string) {
|
||||
return getDictLabel('sys_user_sex', sex, '未知')
|
||||
}
|
||||
|
||||
// 获取角色名称列表
|
||||
function getRoleNames(): string[] {
|
||||
if (personalData.value.roleNames && personalData.value.roleNames.length > 0) {
|
||||
return personalData.value.roleNames
|
||||
}
|
||||
if (personalData.value.roleName) {
|
||||
return [personalData.value.roleName]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
// 获取个人资料
|
||||
async function fetchPersonalData() {
|
||||
try {
|
||||
loading.value = true
|
||||
const result = await getPersonalData()
|
||||
if (result.isSuccess) {
|
||||
personalData.value = result.data
|
||||
// 同步到编辑表单
|
||||
basicForm.value = {
|
||||
userName: result.data.userName,
|
||||
email: result.data.email,
|
||||
phone: result.data.phone,
|
||||
sex: result.data.sex,
|
||||
avatar: result.data.avatar,
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
coiMsgError('获取个人资料失败')
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 头像上传处理
|
||||
function handleAvatarClick() {
|
||||
avatarUploadRef.value?.click()
|
||||
}
|
||||
|
||||
// 头像上传文件选择
|
||||
async function handleAvatarChange(event: Event) {
|
||||
const target = event.target as HTMLInputElement
|
||||
if (target.files && target.files[0]) {
|
||||
const file = target.files[0]
|
||||
|
||||
// 验证文件类型
|
||||
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
coiMsgError('只支持JPG、PNG、GIF格式的图片')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件大小(2MB)
|
||||
const fileSizeMB = file.size / 1024 / 1024
|
||||
if (fileSizeMB > 2) {
|
||||
coiMsgError(`头像文件大小超出限制!当前文件:${fileSizeMB.toFixed(2)}MB,最大允许:2MB`)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
uploading.value = true
|
||||
const result = await uploadAvatar(file, 2)
|
||||
if (result.isSuccess) {
|
||||
// 智能处理头像访问URL
|
||||
let avatarUrl = result.data.fileUploadPath
|
||||
|
||||
// 如果返回的不是完整URL(如本地存储返回相对路径),则添加服务地址前缀
|
||||
if (!avatarUrl.startsWith('http://') && !avatarUrl.startsWith('https://')) {
|
||||
const baseUrl = serviceConfig[import.meta.env.MODE].url
|
||||
avatarUrl = `${baseUrl}${avatarUrl}`
|
||||
}
|
||||
// 如果返回的是完整URL(如MinIO、OSS),直接使用
|
||||
|
||||
basicForm.value.avatar = avatarUrl
|
||||
personalData.value.avatar = avatarUrl
|
||||
coiMsgSuccess('头像上传成功')
|
||||
// 单独保存头像
|
||||
await handleAvatarSave()
|
||||
}
|
||||
else {
|
||||
coiMsgError('头像上传失败')
|
||||
}
|
||||
}
|
||||
catch (error: any) {
|
||||
// 解析后端返回的错误信息
|
||||
let errorMessage = '头像上传失败'
|
||||
|
||||
// 检查是否是服务返回的结构化错误
|
||||
if (error?.isSuccess === false) {
|
||||
// alova 返回的结构化错误
|
||||
if (error.message) {
|
||||
errorMessage = error.message
|
||||
}
|
||||
else if (error.msg) {
|
||||
errorMessage = error.msg
|
||||
}
|
||||
}
|
||||
else if (error?.response?.data?.msg) {
|
||||
// 原始HTTP响应错误
|
||||
errorMessage = error.response.data.msg
|
||||
}
|
||||
else if (error?.message) {
|
||||
// 网络错误或其他错误
|
||||
if (error.message.includes('413')) {
|
||||
errorMessage = '头像文件大小超出限制,请选择较小的文件'
|
||||
}
|
||||
else if (error.message.includes('400')) {
|
||||
errorMessage = '头像文件格式不支持或文件无效'
|
||||
}
|
||||
else {
|
||||
errorMessage = error.message
|
||||
}
|
||||
}
|
||||
|
||||
coiMsgError(errorMessage)
|
||||
}
|
||||
finally {
|
||||
uploading.value = false
|
||||
// 清空文件选择
|
||||
target.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 单独保存头像
|
||||
async function handleAvatarSave() {
|
||||
try {
|
||||
const result = await updateBasicData({ avatar: basicForm.value.avatar })
|
||||
if (result.isSuccess) {
|
||||
coiMsgSuccess('头像保存成功')
|
||||
// 更新全局用户状态中的头像
|
||||
authStore.updateUserInfo({ avatar: basicForm.value.avatar })
|
||||
await fetchPersonalData()
|
||||
}
|
||||
else {
|
||||
coiMsgError('头像保存失败')
|
||||
}
|
||||
}
|
||||
catch {
|
||||
coiMsgError('头像保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存基本信息
|
||||
async function handleBasicSave() {
|
||||
if (!basicFormRef.value)
|
||||
return
|
||||
|
||||
try {
|
||||
// 先进行表单验证
|
||||
await basicFormRef.value.validate()
|
||||
}
|
||||
catch {
|
||||
// 表单验证失败,提示用户检查填写内容
|
||||
coiMsgWarning('验证失败,请检查填写内容')
|
||||
return
|
||||
}
|
||||
|
||||
// 表单验证通过,执行API调用
|
||||
try {
|
||||
loading.value = true
|
||||
const result = await updateBasicData(basicForm.value)
|
||||
if (result.isSuccess) {
|
||||
coiMsgSuccess('保存成功')
|
||||
// 更新全局用户状态
|
||||
authStore.updateUserInfo({
|
||||
userName: basicForm.value.userName,
|
||||
avatar: basicForm.value.avatar,
|
||||
})
|
||||
await fetchPersonalData()
|
||||
}
|
||||
else {
|
||||
coiMsgError('保存失败')
|
||||
}
|
||||
}
|
||||
catch {
|
||||
coiMsgError('保存失败')
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置基本信息表单
|
||||
function handleBasicReset() {
|
||||
basicForm.value = {
|
||||
userName: personalData.value.userName,
|
||||
email: personalData.value.email,
|
||||
phone: personalData.value.phone,
|
||||
sex: personalData.value.sex,
|
||||
avatar: personalData.value.avatar,
|
||||
}
|
||||
}
|
||||
|
||||
// 重置密码表单
|
||||
function handlePasswordReset() {
|
||||
passwordForm.value = {
|
||||
password: '',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
}
|
||||
}
|
||||
|
||||
// 提交密码修改
|
||||
async function handlePasswordSubmit() {
|
||||
if (!passwordFormRef.value)
|
||||
return
|
||||
|
||||
try {
|
||||
// 先进行表单验证
|
||||
await passwordFormRef.value.validate()
|
||||
}
|
||||
catch {
|
||||
// 表单验证失败,提示用户检查填写内容
|
||||
coiMsgWarning('验证失败,请检查填写内容')
|
||||
return
|
||||
}
|
||||
|
||||
// 表单验证通过,执行API调用
|
||||
try {
|
||||
loading.value = true
|
||||
const result = await updatePassword(passwordForm.value)
|
||||
if (result.isSuccess) {
|
||||
coiMsgSuccess('密码修改成功')
|
||||
// 重置表单
|
||||
handlePasswordReset()
|
||||
}
|
||||
else {
|
||||
coiMsgError('密码修改失败')
|
||||
}
|
||||
}
|
||||
catch {
|
||||
coiMsgError('密码修改失败')
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(dateStr: string) {
|
||||
if (!dateStr)
|
||||
return '-'
|
||||
|
||||
const date = new Date(dateStr)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
fetchPersonalData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dark .bg-gray-50 {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.dark .bg-white {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
.dark .text-gray-900 {
|
||||
color: #f9fafb;
|
||||
}
|
||||
|
||||
.dark .text-gray-600 {
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
.dark .text-gray-700 {
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.dark .border-gray-100 {
|
||||
border-color: #4b5563;
|
||||
}
|
||||
|
||||
.dark .shadow-sm {
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dark .bg-yellow-50 {
|
||||
background-color: rgba(245, 158, 11, 0.1);
|
||||
}
|
||||
|
||||
.dark .border-yellow-200 {
|
||||
border-color: rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
|
||||
.dark .text-yellow-800 {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
.dark .text-yellow-700 {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.dark .text-yellow-600 {
|
||||
color: #d97706;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user