mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-09-18 12:19:58 +08:00
feat: 完善个人中心
This commit is contained in:
parent
c168e92135
commit
41d4e9a0d5
@ -48,3 +48,14 @@ export function updateUser(id: number, data: Partial<Entity.User>) {
|
|||||||
export function deleteUser(id: number) {
|
export function deleteUser(id: number) {
|
||||||
return request.Delete<Api.Response<boolean>>(`/user/${id}`)
|
return request.Delete<Api.Response<boolean>>(`/user/${id}`)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 更新用户密码
|
||||||
|
* Patch /user/password
|
||||||
|
*/
|
||||||
|
export interface UserPasswordParams {
|
||||||
|
oldPassword: string
|
||||||
|
newPassword: string
|
||||||
|
}
|
||||||
|
export function updateUserPassword(data: UserPasswordParams) {
|
||||||
|
return request.Patch<Api.Response<boolean>>(`/user/password`, data)
|
||||||
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// 通知设置
|
||||||
|
const settings = ref({
|
||||||
|
systemUpdateEmail: true,
|
||||||
|
securityAlertEmail: true,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- 邮件通知 -->
|
||||||
|
<n-card title="邮件通知" size="small">
|
||||||
|
<n-list>
|
||||||
|
<n-list-item>
|
||||||
|
<n-thing>
|
||||||
|
<template #header>
|
||||||
|
系统更新通知
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
系统有重要更新时发送邮件通知
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
<template #suffix>
|
||||||
|
<n-switch v-model:value="settings.systemUpdateEmail" />
|
||||||
|
</template>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item>
|
||||||
|
<n-thing>
|
||||||
|
<template #header>
|
||||||
|
安全警报
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
检测到安全威胁时立即发送邮件
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
<template #suffix>
|
||||||
|
<n-switch v-model:value="settings.securityAlertEmail" />
|
||||||
|
</template>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -61,7 +61,6 @@ async function updateUserInfo() {
|
|||||||
:model="formData"
|
:model="formData"
|
||||||
label-placement="left"
|
label-placement="left"
|
||||||
label-width="80px"
|
label-width="80px"
|
||||||
class="max-w-400px"
|
|
||||||
>
|
>
|
||||||
<n-grid :cols="1">
|
<n-grid :cols="1">
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// 偏好设置
|
||||||
|
const preferences = ref({
|
||||||
|
aiAssistant: false,
|
||||||
|
predictiveLoading: false,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- 实验性功能 -->
|
||||||
|
<n-card title="实验性功能" size="small">
|
||||||
|
<n-alert type="warning" style="margin-bottom: 16px;">
|
||||||
|
以下功能仍在测试阶段,可能会影响系统稳定性
|
||||||
|
</n-alert>
|
||||||
|
|
||||||
|
<n-list>
|
||||||
|
<n-list-item>
|
||||||
|
<n-thing>
|
||||||
|
<template #header>
|
||||||
|
AI 助手
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
启用智能助手功能,提供操作建议
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
<template #suffix>
|
||||||
|
<n-switch v-model:value="preferences.aiAssistant" />
|
||||||
|
</template>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item>
|
||||||
|
<n-thing>
|
||||||
|
<template #header>
|
||||||
|
预测性加载
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
根据使用习惯预加载可能需要的内容
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
<template #suffix>
|
||||||
|
<n-switch v-model:value="preferences.predictiveLoading" />
|
||||||
|
</template>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
162
src/views/build-in/user-center/components/SecuritySettings.vue
Normal file
162
src/views/build-in/user-center/components/SecuritySettings.vue
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { FormInst, FormRules } from 'naive-ui'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { updateUserPassword } from '@/api/system/user'
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
const formRef = ref<FormInst | null>(null)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 密码表单
|
||||||
|
const passwordForm = ref({
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 安全设置
|
||||||
|
const loginNotificationEnabled = ref(true)
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
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, value) => {
|
||||||
|
return value === passwordForm.value.newPassword
|
||||||
|
},
|
||||||
|
message: '两次输入的密码不一致',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
async function handleUpdatePassword() {
|
||||||
|
try {
|
||||||
|
await formRef.value?.validate()
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
await updateUserPassword({
|
||||||
|
oldPassword: passwordForm.value.oldPassword,
|
||||||
|
newPassword: passwordForm.value.newPassword,
|
||||||
|
})
|
||||||
|
|
||||||
|
message.success('密码更新成功')
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
function resetForm() {
|
||||||
|
passwordForm.value = {
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
}
|
||||||
|
formRef.value?.restoreValidation()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- 密码更新 -->
|
||||||
|
<n-card title="密码更新" size="small">
|
||||||
|
<n-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="passwordForm"
|
||||||
|
:rules="passwordRules"
|
||||||
|
label-placement="left"
|
||||||
|
label-width="120px"
|
||||||
|
require-mark-placement="right-hanging"
|
||||||
|
>
|
||||||
|
<n-form-item label="当前密码" path="oldPassword">
|
||||||
|
<n-input
|
||||||
|
v-model:value="passwordForm.oldPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入当前密码"
|
||||||
|
show-password-on="click"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="新密码" path="newPassword">
|
||||||
|
<n-input
|
||||||
|
v-model:value="passwordForm.newPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入新密码"
|
||||||
|
show-password-on="click"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="确认新密码" path="confirmPassword">
|
||||||
|
<n-input
|
||||||
|
v-model:value="passwordForm.confirmPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请再次输入新密码"
|
||||||
|
show-password-on="click"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label=" ">
|
||||||
|
<n-space>
|
||||||
|
<n-button
|
||||||
|
type="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleUpdatePassword"
|
||||||
|
>
|
||||||
|
更新密码
|
||||||
|
</n-button>
|
||||||
|
<n-button @click="resetForm">
|
||||||
|
重置
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<!-- 账户安全 -->
|
||||||
|
<n-card title="账户安全" size="small">
|
||||||
|
<n-list>
|
||||||
|
<n-list-item>
|
||||||
|
<n-thing>
|
||||||
|
<template #header>
|
||||||
|
登录设备管理
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
管理已登录的设备,可以远程注销可疑设备
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
<template #suffix>
|
||||||
|
<n-button size="small" secondary>
|
||||||
|
查看设备
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item>
|
||||||
|
<n-thing>
|
||||||
|
<template #header>
|
||||||
|
登录通知
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
新设备登录时发送邮件通知
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
<template #suffix>
|
||||||
|
<n-switch v-model:value="loginNotificationEnabled" />
|
||||||
|
</template>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -1,13 +1,65 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { computed, h, onMounted, ref } from 'vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
import { useAuthStore } from '@/store'
|
import { useAuthStore } from '@/store'
|
||||||
import PersonalInfo from './components/PersonalInfo.vue'
|
import PersonalInfo from './components/PersonalInfo.vue'
|
||||||
|
import SecuritySettings from './components/SecuritySettings.vue'
|
||||||
|
import NotificationSettings from './components/NotificationSettings.vue'
|
||||||
|
import PreferenceSettings from './components/PreferenceSettings.vue'
|
||||||
|
import IconUser from '~icons/icon-park-outline/user'
|
||||||
|
import IconLock from '~icons/icon-park-outline/lock'
|
||||||
|
import IconRemind from '~icons/icon-park-outline/remind'
|
||||||
|
import IconSetting from '~icons/icon-park-outline/setting-one'
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const { userInfo } = storeToRefs(authStore)
|
const { userInfo } = storeToRefs(authStore)
|
||||||
|
|
||||||
// 当前选中的标签页
|
// 当前选中的菜单项
|
||||||
const activeTab = ref('profile')
|
const activeMenu = ref('profile')
|
||||||
|
|
||||||
|
// 页面配置 - 统一管理菜单项、标题和组件
|
||||||
|
const pageConfig = [
|
||||||
|
{
|
||||||
|
key: 'profile',
|
||||||
|
label: '个人信息',
|
||||||
|
title: '个人信息',
|
||||||
|
icon: IconUser,
|
||||||
|
component: PersonalInfo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'security',
|
||||||
|
label: '安全设置',
|
||||||
|
title: '安全设置',
|
||||||
|
icon: IconLock,
|
||||||
|
component: SecuritySettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'notification',
|
||||||
|
label: '通知设置',
|
||||||
|
title: '通知设置',
|
||||||
|
icon: IconRemind,
|
||||||
|
component: NotificationSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'preference',
|
||||||
|
label: '偏好设置',
|
||||||
|
title: '偏好设置',
|
||||||
|
icon: IconSetting,
|
||||||
|
component: PreferenceSettings,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 菜单选项 - 从配置中提取
|
||||||
|
const menuOptions = pageConfig.map(({ key, label, icon }) => ({
|
||||||
|
key,
|
||||||
|
label,
|
||||||
|
icon: () => h(icon),
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 当前页面配置 - 根据激活的菜单项获取
|
||||||
|
const currentPageConfig = computed(() => {
|
||||||
|
return pageConfig.find(config => config.key === activeMenu.value)
|
||||||
|
})
|
||||||
|
|
||||||
// 获取问候语
|
// 获取问候语
|
||||||
function getGreeting() {
|
function getGreeting() {
|
||||||
@ -34,83 +86,46 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-space vertical>
|
|
||||||
<!-- 欢迎标题 -->
|
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-flex align="center" justify="space-between">
|
<n-flex :wrap="false" style="height: 100%;">
|
||||||
<div>
|
<!-- 左侧区域 -->
|
||||||
<n-h2>
|
<div class="w-[220px] border-r border-[var(--n-border-color)] flex flex-col">
|
||||||
{{ getGreeting() }},{{ userInfo?.nickName || userInfo.username }}
|
<!-- 左上:头像和招呼 -->
|
||||||
</n-h2>
|
<n-flex
|
||||||
<n-p class="text-sm opacity-90 m-0">
|
align="center" justify="center" vertical
|
||||||
欢迎使用个人中心
|
class="p-3 border-b border-[var(--n-border-color)]"
|
||||||
</n-p>
|
:size="8"
|
||||||
</div>
|
>
|
||||||
<n-avatar
|
<n-avatar
|
||||||
round
|
round
|
||||||
:size="64"
|
:size="80"
|
||||||
:src="userInfo?.avatar || `https://api.dicebear.com/9.x/adventurer-neutral/svg?seed=${userInfo!.username}`"
|
:src="userInfo?.avatar || `https://api.dicebear.com/9.x/adventurer-neutral/svg?seed=${userInfo!.username}`"
|
||||||
/>
|
/>
|
||||||
|
<n-h3 class="m-0">
|
||||||
|
{{ getGreeting() }},{{ userInfo?.nickName || userInfo.username }}
|
||||||
|
</n-h3>
|
||||||
|
<n-text>
|
||||||
|
欢迎使用个人中心
|
||||||
|
</n-text>
|
||||||
|
</n-flex>
|
||||||
|
|
||||||
|
<!-- 左下:菜单 -->
|
||||||
|
<n-menu
|
||||||
|
v-model:value="activeMenu"
|
||||||
|
class="flex-1"
|
||||||
|
:options="menuOptions"
|
||||||
|
:icon-size="16"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧功能区域 -->
|
||||||
|
<div class="flex-1 p-2">
|
||||||
|
<n-h4 class="flex items-center gap-2">
|
||||||
|
<component :is="currentPageConfig!.icon" /> {{ currentPageConfig!.title }}
|
||||||
|
</n-h4>
|
||||||
|
<n-divider />
|
||||||
|
<component :is="currentPageConfig!.component" />
|
||||||
|
</div>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-card>
|
</n-card>
|
||||||
|
|
||||||
<!-- 主要内容区域 -->
|
|
||||||
<n-card>
|
|
||||||
<n-tabs
|
|
||||||
v-model:value="activeTab"
|
|
||||||
type="line"
|
|
||||||
placement="left"
|
|
||||||
tab-style="min-height: 30px;"
|
|
||||||
pane-style="padding-left: 1rem;"
|
|
||||||
>
|
|
||||||
<n-tab-pane name="profile">
|
|
||||||
<template #tab>
|
|
||||||
<icon-park-outline-user class="mr-2" />
|
|
||||||
个人信息
|
|
||||||
</template>
|
|
||||||
<n-h5>
|
|
||||||
个人信息
|
|
||||||
</n-h5>
|
|
||||||
<n-divider />
|
|
||||||
<PersonalInfo />
|
|
||||||
</n-tab-pane>
|
|
||||||
|
|
||||||
<n-tab-pane name="security">
|
|
||||||
<template #tab>
|
|
||||||
<icon-park-outline-lock class="mr-2" />
|
|
||||||
安全设置
|
|
||||||
</template>
|
|
||||||
<n-h5>
|
|
||||||
安全设置
|
|
||||||
</n-h5>
|
|
||||||
<n-divider />
|
|
||||||
<n-empty description="功能开发中..." />
|
|
||||||
</n-tab-pane>
|
|
||||||
|
|
||||||
<n-tab-pane name="notification">
|
|
||||||
<template #tab>
|
|
||||||
<icon-park-outline-remind class="mr-2" />
|
|
||||||
通知设置
|
|
||||||
</template>
|
|
||||||
<n-h5>
|
|
||||||
通知设置
|
|
||||||
</n-h5>
|
|
||||||
<n-divider />
|
|
||||||
<n-empty description="功能开发中..." />
|
|
||||||
</n-tab-pane>
|
|
||||||
|
|
||||||
<n-tab-pane name="preference">
|
|
||||||
<template #tab>
|
|
||||||
<icon-park-outline-setting-one class="mr-2" />
|
|
||||||
偏好设置
|
|
||||||
</template>
|
|
||||||
<n-h5>
|
|
||||||
偏好设置
|
|
||||||
</n-h5>
|
|
||||||
<n-divider />
|
|
||||||
<n-empty description="功能开发中..." />
|
|
||||||
</n-tab-pane>
|
|
||||||
</n-tabs>
|
|
||||||
</n-card>
|
|
||||||
</n-space>
|
|
||||||
</template>
|
</template>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user