mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-08 01:17:23 +08:00
chore: update
This commit is contained in:
parent
309e31fcd9
commit
02b2568427
@ -1,4 +1,5 @@
|
||||
buttons:
|
||||
pureAccountSettings: Account Settings
|
||||
pureLoginOut: LoginOut
|
||||
pureLogin: Login
|
||||
pureSystemSet: Open ProjectConfig
|
||||
@ -10,7 +11,6 @@ buttons:
|
||||
pureCloseAllTabs: Close AllTabs
|
||||
pureContentFullScreen: Content FullScreen
|
||||
pureContentExitFullScreen: Content ExitFullScreen
|
||||
pureUserSettings: User Settings
|
||||
menus:
|
||||
pureHome: Home
|
||||
pureLogin: Login
|
||||
|
@ -1,4 +1,5 @@
|
||||
buttons:
|
||||
pureAccountSettings: 账户设置
|
||||
pureLoginOut: 退出系统
|
||||
pureLogin: 登录
|
||||
pureSystemSet: 打开项目配置
|
||||
@ -10,7 +11,6 @@ buttons:
|
||||
pureCloseAllTabs: 关闭全部标签页
|
||||
pureContentFullScreen: 内容区全屏
|
||||
pureContentExitFullScreen: 内容区退出全屏
|
||||
pureUserSettings: 个人设置
|
||||
menus:
|
||||
pureHome: 首页
|
||||
pureLogin: 登录
|
||||
|
21
mock/mine.ts
Normal file
21
mock/mine.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
||||
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: "/mine",
|
||||
method: "get",
|
||||
response: () => {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||
username: "admin",
|
||||
nickname: "Coder",
|
||||
email: "pureadmin@163.com",
|
||||
phone: "15888886789",
|
||||
description: "一个热爱开源的前端工程师"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
]);
|
@ -1,52 +0,0 @@
|
||||
import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
||||
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: "/get-regions",
|
||||
method: "get",
|
||||
response: () => {
|
||||
return {
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
code: "001",
|
||||
name: "中国",
|
||||
children: [
|
||||
{
|
||||
code: "001001",
|
||||
name: "北京市",
|
||||
children: [
|
||||
{
|
||||
code: "001001001",
|
||||
name: "东城区"
|
||||
},
|
||||
{
|
||||
code: "001001002",
|
||||
name: "西城区"
|
||||
}
|
||||
// 其他区
|
||||
]
|
||||
},
|
||||
{
|
||||
code: "001002",
|
||||
name: "上海市",
|
||||
children: [
|
||||
{
|
||||
code: "001002001",
|
||||
name: "黄浦区"
|
||||
},
|
||||
{
|
||||
code: "001002002",
|
||||
name: "徐汇区"
|
||||
}
|
||||
// 其他区
|
||||
]
|
||||
}
|
||||
// 其他城市
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
]);
|
@ -1,21 +0,0 @@
|
||||
import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
||||
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: "/get-user-info",
|
||||
method: "get",
|
||||
response: () => {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
avatarUrl: "https://avatars.githubusercontent.com/u/44761321",
|
||||
nickName: "企丸丸",
|
||||
introduce: "我是幻兽帕鲁里的明星",
|
||||
regionCode: "001002001",
|
||||
address: "冰鸟密域祭坛地下城",
|
||||
userName: "admin"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
]);
|
@ -23,8 +23,3 @@ export const formUpload = data => {
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**所在区域数据*/
|
||||
export const getRegions = (params?: object) => {
|
||||
return http.request<Result>("get", "/get-regions", { params });
|
||||
};
|
||||
|
@ -29,12 +29,18 @@ export type RefreshTokenResult = {
|
||||
};
|
||||
|
||||
export type UserInfo = {
|
||||
avatarUrl: string;
|
||||
nickName: string;
|
||||
introduce: string;
|
||||
regionCode: string;
|
||||
address: string;
|
||||
userName: string;
|
||||
/** 头像 */
|
||||
avatar: string;
|
||||
/** 用户名 */
|
||||
username: string;
|
||||
/** 昵称 */
|
||||
nickname: string;
|
||||
/** 邮箱 */
|
||||
email: string;
|
||||
/** 联系电话 */
|
||||
phone: string;
|
||||
/** 简介 */
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type UserInfoResult = {
|
||||
@ -47,12 +53,12 @@ export const getLogin = (data?: object) => {
|
||||
return http.request<UserResult>("post", "/login", { data });
|
||||
};
|
||||
|
||||
/** 刷新token */
|
||||
/** 刷新`token` */
|
||||
export const refreshTokenApi = (data?: object) => {
|
||||
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
|
||||
};
|
||||
|
||||
/**获取个人信息 */
|
||||
export const getUserInfo = (data?: object) => {
|
||||
return http.request<UserInfoResult>("get", "/get-user-info", { data });
|
||||
/** 账户设置-个人信息 */
|
||||
export const getMine = (data?: object) => {
|
||||
return http.request<UserInfoResult>("get", "/mine", { data });
|
||||
};
|
||||
|
@ -8,22 +8,22 @@ import Breadcrumb from "./sidebar/breadCrumb.vue";
|
||||
import topCollapse from "./sidebar/topCollapse.vue";
|
||||
import { useTranslationLang } from "../hooks/useTranslationLang";
|
||||
import globalization from "@/assets/svg/globalization.svg?component";
|
||||
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
|
||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||
import Check from "@iconify-icons/ep/check";
|
||||
import UserSettingsLine from "@iconify-icons/ri/user-settings-line";
|
||||
|
||||
const {
|
||||
layout,
|
||||
device,
|
||||
logout,
|
||||
handleOpenUserSettings,
|
||||
onPanel,
|
||||
pureApp,
|
||||
username,
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
toggleSideBar,
|
||||
toAccountSettings,
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass
|
||||
} = useNav();
|
||||
@ -94,12 +94,12 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="handleOpenUserSettings">
|
||||
<el-dropdown-item @click="toAccountSettings">
|
||||
<IconifyIconOffline
|
||||
:icon="UserSettingsLine"
|
||||
:icon="AccountSettingsIcon"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
{{ t("buttons.pureUserSettings") }}
|
||||
{{ t("buttons.pureAccountSettings") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">
|
||||
<IconifyIconOffline
|
||||
@ -187,7 +187,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
||||
}
|
||||
|
||||
.logout {
|
||||
max-width: 160px;
|
||||
max-width: 120px;
|
||||
|
||||
::v-deep(.el-dropdown-menu__item) {
|
||||
display: inline-flex;
|
||||
|
@ -9,10 +9,10 @@ import { useNav } from "@/layout/hooks/useNav";
|
||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
import globalization from "@/assets/svg/globalization.svg?component";
|
||||
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
|
||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||
import Check from "@iconify-icons/ep/check";
|
||||
import UserSettingsLine from "@iconify-icons/ri/user-settings-line";
|
||||
|
||||
const menuRef = ref();
|
||||
|
||||
@ -27,9 +27,9 @@ const {
|
||||
username,
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
toAccountSettings,
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass,
|
||||
handleOpenUserSettings
|
||||
getDropdownItemClass
|
||||
} = useNav();
|
||||
|
||||
const defaultActive = computed(() =>
|
||||
@ -109,9 +109,12 @@ nextTick(() => {
|
||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-item @click="handleOpenUserSettings">
|
||||
<IconifyIconOffline :icon="UserSettingsLine" style="margin: 5px" />
|
||||
{{ t("buttons.pureUserSettings") }}
|
||||
<el-dropdown-item @click="toAccountSettings">
|
||||
<IconifyIconOffline
|
||||
:icon="AccountSettingsIcon"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
{{ t("buttons.pureAccountSettings") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="logout">
|
||||
|
@ -12,10 +12,10 @@ import { getParentPaths, findRouteByPath } from "@/router/utils";
|
||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
import globalization from "@/assets/svg/globalization.svg?component";
|
||||
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
|
||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||
import Check from "@iconify-icons/ep/check";
|
||||
import UserSettingsLine from "@iconify-icons/ri/user-settings-line";
|
||||
|
||||
const menuRef = ref();
|
||||
const defaultActive = ref(null);
|
||||
@ -31,9 +31,9 @@ const {
|
||||
userAvatar,
|
||||
getDivStyle,
|
||||
avatarsStyle,
|
||||
toAccountSettings,
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass,
|
||||
handleOpenUserSettings
|
||||
getDropdownItemClass
|
||||
} = useNav();
|
||||
|
||||
function getDefaultActive(routePath) {
|
||||
@ -142,9 +142,12 @@ watch(
|
||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-item @click="handleOpenUserSettings">
|
||||
<IconifyIconOffline :icon="UserSettingsLine" style="margin: 5px" />
|
||||
{{ t("buttons.pureUserSettings") }}
|
||||
<el-dropdown-item @click="toAccountSettings">
|
||||
<IconifyIconOffline
|
||||
:icon="AccountSettingsIcon"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
{{ t("buttons.pureAccountSettings") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="logout">
|
||||
|
@ -1,126 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import leftLine from "@iconify-icons/ri/arrow-left-s-line";
|
||||
import userLine from "@iconify-icons/ri/user-3-line";
|
||||
import profileLine from "@iconify-icons/ri/profile-line";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getUserInfo } from "@/api/user";
|
||||
|
||||
import UserInfoManage from "@/layout/components/userSettings/userInfoManage.vue";
|
||||
import SafeManage from "@/layout/components/userSettings/safeManage.vue";
|
||||
|
||||
const router = useRouter();
|
||||
const { getLogo, isCollapse } = useNav();
|
||||
const userInfo = ref({
|
||||
nickName: "",
|
||||
avatarUrl: "",
|
||||
userName: ""
|
||||
});
|
||||
getUserInfo().then(res => {
|
||||
userInfo.value = res.data;
|
||||
});
|
||||
const handleBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
|
||||
const witchPane = ref("userInfoManage");
|
||||
const panes = [
|
||||
{
|
||||
key: "userInfoManage",
|
||||
label: "基本设置",
|
||||
icon: userLine,
|
||||
component: UserInfoManage
|
||||
},
|
||||
{
|
||||
key: "safeManage",
|
||||
label: "安全设置",
|
||||
icon: profileLine,
|
||||
component: SafeManage
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container class="h-full">
|
||||
<el-aside class="settings-sidebar px-2" width="210px">
|
||||
<el-menu :default-active="witchPane" class="settings-menu">
|
||||
<el-menu-item @click="handleBack">
|
||||
<div class="flex items-center">
|
||||
<IconifyIconOffline :icon="leftLine" />
|
||||
<img :src="getLogo()" class="w-6 h-6" alt="logo" />
|
||||
<span class="ml-2">返回</span>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
<li class="flex items-center ml-8 mt-4">
|
||||
<div>
|
||||
<el-avatar :size="80" :src="userInfo.avatarUrl" />
|
||||
</div>
|
||||
<div class="pl-4 flex-col">
|
||||
<p>{{ userInfo.nickName }}</p>
|
||||
<p>
|
||||
<el-text class="mt-2" type="info">{{
|
||||
userInfo.userName
|
||||
}}</el-text>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<el-menu-item
|
||||
v-for="item in panes"
|
||||
:key="item.key"
|
||||
:index="item.key"
|
||||
@click="witchPane = item.key"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<el-icon><IconifyIconOffline :icon="item.icon" /></el-icon>
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<component :is="panes.find(item => item.key === witchPane).component" />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-sidebar {
|
||||
background: $menuBg;
|
||||
}
|
||||
|
||||
.settings-menu {
|
||||
color: $subMenuActiveText;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
|
||||
::v-deep(.el-menu-item) {
|
||||
position: relative;
|
||||
background-color: transparent !important;
|
||||
|
||||
div {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
span {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
i {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
inset: 0 8px;
|
||||
margin: 4px 0;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-color-primary) !important;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,56 +0,0 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>安全设置</template>
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>账户密码</p>
|
||||
<p class="wp-4">
|
||||
<el-text class="mx-1" type="info">当前密码强度:强</el-text>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-64">
|
||||
<el-link type="primary" :underline="false">修改</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>密保手机</p>
|
||||
<p class="wp-4">
|
||||
<el-text class="mx-1" type="info">已经绑定手机:138****1234</el-text>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-64">
|
||||
<el-link type="primary" :underline="false">修改</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>密保问题</p>
|
||||
<p class="wp-4">
|
||||
<el-text class="mx-1" type="info">
|
||||
未设置密保问题,密保问题可有效保护账户安全
|
||||
</el-text>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-64">
|
||||
<el-link type="primary" :underline="false">设置</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>备用邮箱</p>
|
||||
<p class="wp-4">
|
||||
<el-text class="mx-1" type="info">已绑定邮箱:ant***sign.com</el-text>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-64">
|
||||
<el-link type="primary" :underline="false">修改</el-link>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
@ -99,8 +99,8 @@ export function useNav() {
|
||||
emitter.emit("openPanel");
|
||||
}
|
||||
|
||||
function handleOpenUserSettings() {
|
||||
router.push({ name: "UserSettings" });
|
||||
function toAccountSettings() {
|
||||
router.push({ name: "AccountSettings" });
|
||||
}
|
||||
|
||||
function toggleSideBar() {
|
||||
@ -163,8 +163,8 @@ export function useNav() {
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
tooltipEffect,
|
||||
toAccountSettings,
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass,
|
||||
handleOpenUserSettings
|
||||
getDropdownItemClass
|
||||
};
|
||||
}
|
||||
|
@ -40,13 +40,13 @@ export default [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/user-settings",
|
||||
name: "UserSettings",
|
||||
component: () => import("@/layout/components/userSettings/index.vue"),
|
||||
path: "/account-settings",
|
||||
name: "AccountSettings",
|
||||
component: () => import("@/views/account-settings/index.vue"),
|
||||
meta: {
|
||||
title: $t("buttons.pureUserSettings"),
|
||||
title: $t("buttons.pureAccountSettings"),
|
||||
showLink: false,
|
||||
rank: 103
|
||||
rank: 104
|
||||
}
|
||||
}
|
||||
] satisfies Array<RouteConfigsTable>;
|
||||
|
48
src/views/account-settings/components/accountManagement.vue
Normal file
48
src/views/account-settings/components/accountManagement.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
// 仅提供示例,请根据具体业务自行实现
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
|
||||
<h3 class="my-8">账户管理</h3>
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>账户密码</p>
|
||||
<el-text class="mx-1" type="info">当前密码强度:强</el-text>
|
||||
</div>
|
||||
<el-button type="primary" text>修改</el-button>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>密保手机</p>
|
||||
<el-text class="mx-1" type="info">已经绑定手机:158****6789</el-text>
|
||||
</div>
|
||||
<el-button type="primary" text>修改</el-button>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>密保问题</p>
|
||||
<el-text class="mx-1" type="info">
|
||||
未设置密保问题,密保问题可有效保护账户安全
|
||||
</el-text>
|
||||
</div>
|
||||
<el-button type="primary" text>修改</el-button>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>备用邮箱</p>
|
||||
<el-text class="mx-1" type="info">已绑定邮箱:pure***@163.com</el-text>
|
||||
</div>
|
||||
<el-button type="primary" text>修改</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-divider--horizontal {
|
||||
border-top: 0.1px var(--el-border-color) var(--el-border-style);
|
||||
}
|
||||
</style>
|
58
src/views/account-settings/components/preferences.vue
Normal file
58
src/views/account-settings/components/preferences.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { message } from "@/utils/message";
|
||||
|
||||
const list = ref([
|
||||
{
|
||||
title: "账户密码",
|
||||
illustrate: "其他用户的消息将以站内信的形式通知",
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
title: "系统消息",
|
||||
illustrate: "系统消息将以站内信的形式通知",
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
title: "待办任务",
|
||||
illustrate: "待办任务将以站内信的形式通知",
|
||||
checked: false
|
||||
}
|
||||
]);
|
||||
|
||||
function onChange(val, item) {
|
||||
message(`${item.title}设置成功`, { type: "success" });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
|
||||
<h3 class="my-8">偏好设置</h3>
|
||||
<div v-for="(item, index) in list" :key="index">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<p>{{ item.title }}</p>
|
||||
<p class="wp-4">
|
||||
<el-text class="mx-1" type="info">
|
||||
{{ item.illustrate }}
|
||||
</el-text>
|
||||
</p>
|
||||
</div>
|
||||
<el-switch
|
||||
v-model="item.checked"
|
||||
inline-prompt
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="val => onChange(val, item)"
|
||||
/>
|
||||
</div>
|
||||
<el-divider />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-divider--horizontal {
|
||||
border-top: 0.1px var(--el-border-color) var(--el-border-style);
|
||||
}
|
||||
</style>
|
@ -8,8 +8,7 @@ import type { FormInstance, FormRules } from "element-plus";
|
||||
import { formUpload } from "@/api/mock";
|
||||
import { message } from "@/utils/message";
|
||||
import { createFormData } from "@pureadmin/utils";
|
||||
import { getRegions } from "@/api/mock";
|
||||
import { getUserInfo, UserInfo } from "@/api/user";
|
||||
import { getMine, UserInfo } from "@/api/user";
|
||||
const { userAvatar, getLogo, username } = useNav();
|
||||
const cropRef = ref();
|
||||
const upload = ref();
|
||||
@ -18,19 +17,19 @@ const userInfoFormRef = ref<FormInstance>();
|
||||
// 从服务器拉取用户信息,然后填充表单
|
||||
|
||||
const userInfoFormData = reactive({
|
||||
avatarUrl: "",
|
||||
nickName: "",
|
||||
introduce: "",
|
||||
regionCode: "",
|
||||
address: ""
|
||||
avatar: "",
|
||||
nickname: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
description: ""
|
||||
});
|
||||
|
||||
getUserInfo().then(res => {
|
||||
getMine().then(res => {
|
||||
Object.assign(userInfoFormData, res.data);
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<UserInfo>>({
|
||||
nickName: [
|
||||
nickname: [
|
||||
{ required: true, message: "昵称必填", trigger: "blur" },
|
||||
{ min: 3, max: 5, message: "长度最小3最大16", trigger: "blur" }
|
||||
]
|
||||
@ -81,13 +80,6 @@ const handleSubmitImage = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const options = ref(); // 级联选择器的选项数据
|
||||
|
||||
// 设置区域数据
|
||||
getRegions().then(res => {
|
||||
options.value = res.data;
|
||||
});
|
||||
|
||||
// 监听级联选择器的值变化
|
||||
const handleCascaderChange = value => {
|
||||
console.log(value);
|
||||
@ -104,11 +96,34 @@ const onSubmit = async (formEl: FormInstance) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function createFilter(queryString) {
|
||||
return item => {
|
||||
return item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
|
||||
};
|
||||
}
|
||||
|
||||
function querySearchEmail(queryString, callback) {
|
||||
const emailList = [
|
||||
{ value: "@qq.com" },
|
||||
{ value: "@126.com" },
|
||||
{ value: "@163.com" }
|
||||
];
|
||||
let results = [];
|
||||
let queryList = [];
|
||||
emailList.map(item =>
|
||||
queryList.push({ value: queryString.split("@")[0] + item.value })
|
||||
);
|
||||
results = queryString
|
||||
? queryList.filter(createFilter(queryString))
|
||||
: queryList;
|
||||
callback(results);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>基本信息</template>
|
||||
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
|
||||
<h3 class="my-8">个人信息</h3>
|
||||
<el-form
|
||||
ref="userInfoFormRef"
|
||||
label-position="top"
|
||||
@ -132,37 +147,42 @@ const onSubmit = async (formEl: FormInstance) => {
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" prop="nickName">
|
||||
<el-input v-model="userInfoFormData.nickName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介">
|
||||
<el-form-item label="昵称" prop="nickname">
|
||||
<el-input
|
||||
v-model="userInfoFormData.introduce"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
v-model="userInfoFormData.nickname"
|
||||
placeholder="请输入昵称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所在地区">
|
||||
<el-cascader
|
||||
v-model="userInfoFormData.regionCode"
|
||||
:options="options"
|
||||
:props="{ value: 'code', label: 'name', emitPath: false }"
|
||||
placeholder="请选择"
|
||||
@change="handleCascaderChange"
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-autocomplete
|
||||
v-model="userInfoFormData.email"
|
||||
:fetch-suggestions="querySearchEmail"
|
||||
:trigger-on-focus="false"
|
||||
placeholder="请输入邮箱"
|
||||
clearable
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="街道地址">
|
||||
<el-form-item label="联系电话">
|
||||
<el-input
|
||||
v-model="userInfoFormData.address"
|
||||
placeholder="请输入"
|
||||
v-model="userInfoFormData.phone"
|
||||
placeholder="请输入联系电话"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit(userInfoFormRef)"
|
||||
>更新基本信息</el-button
|
||||
>
|
||||
<el-form-item label="简介">
|
||||
<el-input
|
||||
v-model="userInfoFormData.description"
|
||||
placeholder="请输入简介"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 6, maxRows: 8 }"
|
||||
maxlength="56"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="onSubmit(userInfoFormRef)">
|
||||
更新信息
|
||||
</el-button>
|
||||
</el-form>
|
||||
<el-dialog
|
||||
v-model="isShow"
|
||||
@ -180,5 +200,5 @@ const onSubmit = async (formEl: FormInstance) => {
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
7
src/views/account-settings/components/securityLog.vue
Normal file
7
src/views/account-settings/components/securityLog.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div class="ml-[120px] min-w-[180px] max-w-[70%]">
|
||||
<h3 class="my-8">安全日志</h3>
|
||||
</div>
|
||||
</template>
|
145
src/views/account-settings/index.vue
Normal file
145
src/views/account-settings/index.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { getMine } from "@/api/user";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ReText } from "@/components/ReText";
|
||||
import Profile from "./components/profile.vue";
|
||||
import Preferences from "./components/preferences.vue";
|
||||
import SecurityLog from "./components/securityLog.vue";
|
||||
import AccountManagement from "./components/accountManagement.vue";
|
||||
|
||||
import leftLine from "@iconify-icons/ri/arrow-left-s-line";
|
||||
import ProfileIcon from "@iconify-icons/ri/user-3-line";
|
||||
import PreferencesIcon from "@iconify-icons/ri/settings-3-line";
|
||||
import SecurityLogIcon from "@iconify-icons/ri/window-line";
|
||||
import AccountManagementIcon from "@iconify-icons/ri/profile-line";
|
||||
|
||||
defineOptions({
|
||||
name: "AccountSettings"
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const userInfo = ref({
|
||||
avatar: "",
|
||||
username: "",
|
||||
nickname: ""
|
||||
});
|
||||
const panes = [
|
||||
{
|
||||
key: "profile",
|
||||
label: "个人信息",
|
||||
icon: ProfileIcon,
|
||||
component: Profile
|
||||
},
|
||||
{
|
||||
key: "preferences",
|
||||
label: "偏好设置",
|
||||
icon: PreferencesIcon,
|
||||
component: Preferences
|
||||
},
|
||||
{
|
||||
key: "securityLog",
|
||||
label: "安全日志",
|
||||
icon: SecurityLogIcon,
|
||||
component: SecurityLog
|
||||
},
|
||||
{
|
||||
key: "accountManagement",
|
||||
label: "账户管理",
|
||||
icon: AccountManagementIcon,
|
||||
component: AccountManagement
|
||||
}
|
||||
];
|
||||
const witchPane = ref("profile");
|
||||
|
||||
getMine().then(res => {
|
||||
userInfo.value = res.data;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container class="h-full">
|
||||
<el-aside class="settings-sidebar px-2" width="240px">
|
||||
<el-menu :default-active="witchPane" class="settings-menu">
|
||||
<el-menu-item
|
||||
class="hover:transition-all hover:duration-200 hover:text-base"
|
||||
@click="router.go(-1)"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<IconifyIconOffline :icon="leftLine" />
|
||||
<span class="ml-2">返回</span>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
<div class="flex items-center ml-8 mt-4 mb-4">
|
||||
<el-avatar :size="48" :src="userInfo.avatar" />
|
||||
<div class="ml-4 flex flex-col max-w-[130px]">
|
||||
<ReText class="font-bold !self-baseline">
|
||||
{{ userInfo.nickname }}
|
||||
</ReText>
|
||||
<ReText class="!self-baseline" type="info">
|
||||
{{ userInfo.username }}
|
||||
</ReText>
|
||||
</div>
|
||||
</div>
|
||||
<el-menu-item
|
||||
v-for="item in panes"
|
||||
:key="item.key"
|
||||
:index="item.key"
|
||||
@click="witchPane = item.key"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<el-icon><IconifyIconOffline :icon="item.icon" /></el-icon>
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<component :is="panes.find(item => item.key === witchPane).component" />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-sidebar {
|
||||
overflow: hidden;
|
||||
background: $menuBg;
|
||||
border-right: 1px solid var(--pure-border-color);
|
||||
}
|
||||
|
||||
.settings-menu {
|
||||
color: $subMenuActiveText;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
|
||||
::v-deep(.el-menu-item) {
|
||||
position: relative;
|
||||
background-color: transparent !important;
|
||||
|
||||
div {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
span {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
i {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
inset: 0 8px;
|
||||
margin: 4px 0;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-color-primary) !important;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user