mirror of
https://github.com/pure-admin/pure-admin-thin.git
synced 2025-04-24 23:47:17 +08:00
perf: 系统管理
This commit is contained in:
parent
9bac6333fb
commit
0838e69d23
@ -1,5 +1,6 @@
|
|||||||
buttons:
|
buttons:
|
||||||
hsLoginOut: LoginOut
|
hsLoginOut: LoginOut
|
||||||
|
userInfo: user info
|
||||||
hsfullscreen: FullScreen
|
hsfullscreen: FullScreen
|
||||||
hsexitfullscreen: ExitFullscreen
|
hsexitfullscreen: ExitFullscreen
|
||||||
hsrefreshRoute: RefreshRoute
|
hsrefreshRoute: RefreshRoute
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
buttons:
|
buttons:
|
||||||
hsLoginOut: 退出系统
|
hsLoginOut: 退出系统
|
||||||
|
userInfo: 个人信息
|
||||||
hsfullscreen: 全屏
|
hsfullscreen: 全屏
|
||||||
hsexitfullscreen: 退出全屏
|
hsexitfullscreen: 退出全屏
|
||||||
hsrefreshRoute: 刷新路由
|
hsrefreshRoute: 刷新路由
|
||||||
|
@ -7,9 +7,11 @@ import Breadcrumb from "./sidebar/breadCrumb.vue";
|
|||||||
import topCollapse from "./sidebar/topCollapse.vue";
|
import topCollapse from "./sidebar/topCollapse.vue";
|
||||||
import { useTranslationLang } from "../hooks/useTranslationLang";
|
import { useTranslationLang } from "../hooks/useTranslationLang";
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from "@/assets/svg/globalization.svg?component";
|
||||||
|
import { useDetail } from "@/router/utils";
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from "@iconify-icons/ep/check";
|
||||||
|
import Avatar from "@iconify-icons/ep/avatar";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
layout,
|
layout,
|
||||||
@ -24,6 +26,7 @@ const {
|
|||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
const { router } = useDetail();
|
||||||
|
|
||||||
const { t, locale, translationCh, translationEn } = useTranslationLang();
|
const { t, locale, translationCh, translationEn } = useTranslationLang();
|
||||||
</script>
|
</script>
|
||||||
@ -89,6 +92,16 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
|
<el-dropdown-item
|
||||||
|
@click="
|
||||||
|
router.push({
|
||||||
|
name: 'UserInfo'
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<IconifyIconOffline :icon="Avatar" style="margin: 5px" />
|
||||||
|
个人中心
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="logout">
|
<el-dropdown-item @click="logout">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
|
26
src/router/modules/userInfo.ts
Normal file
26
src/router/modules/userInfo.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { $t } from "@/plugins/i18n";
|
||||||
|
import { home } from "@/router/enums";
|
||||||
|
const Layout = () => import("@/layout/index.vue");
|
||||||
|
|
||||||
|
export default {
|
||||||
|
path: "/system/user/info",
|
||||||
|
name: "UserInfo",
|
||||||
|
component: Layout,
|
||||||
|
redirect: "/system/user/info",
|
||||||
|
meta: {
|
||||||
|
icon: "homeFilled",
|
||||||
|
title: $t("buttons.userInfo"),
|
||||||
|
rank: home
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/system/user/info",
|
||||||
|
name: "UserInfo",
|
||||||
|
component: () => import("@/views/system/user/info.vue"),
|
||||||
|
meta: {
|
||||||
|
title: $t("buttons.userInfo"),
|
||||||
|
showLink: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} satisfies RouteConfigsTable;
|
@ -3,12 +3,17 @@ import {
|
|||||||
type RouteRecordRaw,
|
type RouteRecordRaw,
|
||||||
type RouteComponent,
|
type RouteComponent,
|
||||||
createWebHistory,
|
createWebHistory,
|
||||||
createWebHashHistory
|
createWebHashHistory,
|
||||||
|
useRouter,
|
||||||
|
useRoute,
|
||||||
|
type LocationQueryRaw,
|
||||||
|
type RouteParamsRaw
|
||||||
} from "vue-router";
|
} from "vue-router";
|
||||||
import { router } from "./index";
|
import { router } from "./index";
|
||||||
import { isProxy, toRaw } from "vue";
|
import { isProxy, toRaw } from "vue";
|
||||||
import { useTimeoutFn } from "@vueuse/core";
|
import { useTimeoutFn } from "@vueuse/core";
|
||||||
import {
|
import {
|
||||||
|
isEmpty,
|
||||||
isString,
|
isString,
|
||||||
cloneDeep,
|
cloneDeep,
|
||||||
isAllEmpty,
|
isAllEmpty,
|
||||||
@ -366,6 +371,67 @@ function getTopMenu(tag = false): menuType {
|
|||||||
return topMenu;
|
return topMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useDetail() {
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const getParameter = isEmpty(route.params) ? route.query : route.params;
|
||||||
|
|
||||||
|
function toDetail(
|
||||||
|
parameter: LocationQueryRaw | RouteParamsRaw,
|
||||||
|
model: "query" | "params"
|
||||||
|
) {
|
||||||
|
// ⚠️ 这里要特别注意下,因为vue-router在解析路由参数的时候会自动转化成字符串类型,比如在使用useRoute().route.query或useRoute().route.params时,得到的参数都是字符串类型
|
||||||
|
// 所以在传参的时候,如果参数是数字类型,就需要在此处 toString() 一下,保证传参跟路由参数类型一致都是字符串,这是必不可少的环节!!!
|
||||||
|
Object.keys(parameter).forEach(param => {
|
||||||
|
if (!isString(parameter[param])) {
|
||||||
|
parameter[param] = parameter[param].toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (model === "query") {
|
||||||
|
// 保存信息到标签页
|
||||||
|
useMultiTagsStoreHook().handleTags("push", {
|
||||||
|
path: `/tabs/query-detail`,
|
||||||
|
name: "TabQueryDetail",
|
||||||
|
query: parameter,
|
||||||
|
meta: {
|
||||||
|
title: {
|
||||||
|
zh: `No.${parameter.id} - 详情信息`,
|
||||||
|
en: `No.${parameter.id} - DetailInfo`
|
||||||
|
},
|
||||||
|
// 如果使用的是非国际化精简版title可以像下面这么写
|
||||||
|
// title: `No.${index} - 详情信息`,
|
||||||
|
// 最大打开标签数
|
||||||
|
dynamicLevel: 3
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 路由跳转
|
||||||
|
router.push({ name: "TabQueryDetail", query: parameter });
|
||||||
|
} else if (model === "params") {
|
||||||
|
useMultiTagsStoreHook().handleTags("push", {
|
||||||
|
path: `/tabs/params-detail/:id`,
|
||||||
|
name: "TabParamsDetail",
|
||||||
|
params: parameter,
|
||||||
|
meta: {
|
||||||
|
title: {
|
||||||
|
zh: `No.${parameter.id} - 详情信息`,
|
||||||
|
en: `No.${parameter.id} - DetailInfo`
|
||||||
|
}
|
||||||
|
// 如果使用的是非国际化精简版title可以像下面这么写
|
||||||
|
// title: `No.${index} - 详情信息`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
router.push({ name: "TabParamsDetail", params: parameter });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于页面刷新,重新获取浏览器地址栏参数并保存到标签页
|
||||||
|
const initToDetail = (model: "query" | "params") => {
|
||||||
|
if (getParameter) toDetail(getParameter, model);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { toDetail, initToDetail, getParameter, router };
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
hasAuth,
|
hasAuth,
|
||||||
getAuths,
|
getAuths,
|
||||||
@ -382,5 +448,6 @@ export {
|
|||||||
handleAliveRoute,
|
handleAliveRoute,
|
||||||
formatTwoStageRoutes,
|
formatTwoStageRoutes,
|
||||||
formatFlatteningRoutes,
|
formatFlatteningRoutes,
|
||||||
filterNoPermissionTree
|
filterNoPermissionTree,
|
||||||
|
useDetail
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,8 @@ export function setToken(data: DataInfo<Date>) {
|
|||||||
function setUserKey(
|
function setUserKey(
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
username: string,
|
username: string,
|
||||||
roles: Array<string>
|
roles: Array<string>,
|
||||||
|
user?: any
|
||||||
) {
|
) {
|
||||||
useUserStoreHook().SET_USERNAME(username);
|
useUserStoreHook().SET_USERNAME(username);
|
||||||
useUserStoreHook().SET_ROLES(roles);
|
useUserStoreHook().SET_ROLES(roles);
|
||||||
@ -73,13 +74,14 @@ export function setToken(data: DataInfo<Date>) {
|
|||||||
accessToken,
|
accessToken,
|
||||||
expires,
|
expires,
|
||||||
username,
|
username,
|
||||||
roles
|
roles,
|
||||||
|
user
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.username && data.roles) {
|
if (data.username && data.roles) {
|
||||||
const { username, roles } = data;
|
const { username, roles, user } = data;
|
||||||
setUserKey(accessToken, username, roles);
|
setUserKey(accessToken, username, roles, user);
|
||||||
} else {
|
} else {
|
||||||
const username =
|
const username =
|
||||||
storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "";
|
storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "";
|
||||||
|
@ -30,7 +30,7 @@ const {
|
|||||||
submitEditUser
|
submitEditUser
|
||||||
} = useUser(tableRef, treeRef);
|
} = useUser(tableRef, treeRef);
|
||||||
|
|
||||||
const user = reactive(userInfo.value.user);
|
const user = reactive(userInfo.value);
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UserInfo"
|
name: "UserInfo"
|
||||||
});
|
});
|
||||||
@ -51,9 +51,9 @@ defineOptions({
|
|||||||
<el-avatar
|
<el-avatar
|
||||||
:size="80"
|
:size="80"
|
||||||
src="https://empty"
|
src="https://empty"
|
||||||
@click="handleUpload(userInfo.user)"
|
@click="handleUpload(userInfo)"
|
||||||
>
|
>
|
||||||
<img :src="'/avatar/' + userInfo.user.avatarName" />
|
<img :src="'/avatar/' + userInfo.avatarName" />
|
||||||
</el-avatar>
|
</el-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -62,28 +62,28 @@ defineOptions({
|
|||||||
<div style="height: 100%">
|
<div style="height: 100%">
|
||||||
<IconifyIconOffline class="check-zh" :icon="SignIn" />
|
<IconifyIconOffline class="check-zh" :icon="SignIn" />
|
||||||
登录账号
|
登录账号
|
||||||
<div class="user-right">{{ userInfo.user.username }}</div>
|
<div class="user-right">{{ userInfo.username }}</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconifyIconOffline class="check-zh" :icon="Check" />
|
<IconifyIconOffline class="check-zh" :icon="Check" />
|
||||||
用户昵称
|
用户昵称
|
||||||
<div class="user-right">{{ userInfo.user.nickName }}</div>
|
<div class="user-right">{{ userInfo.nickName }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconifyIconOffline class="check-zh" :icon="NodeTree" />
|
<IconifyIconOffline class="check-zh" :icon="NodeTree" />
|
||||||
所属部门
|
所属部门
|
||||||
<div class="user-right">{{ userInfo.user.dept.name }}</div>
|
<div class="user-right">{{ userInfo.dept.name }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconifyIconOffline class="check-zh" :icon="Phone" />
|
<IconifyIconOffline class="check-zh" :icon="Phone" />
|
||||||
手机号码
|
手机号码
|
||||||
<div class="user-right">{{ userInfo.user.phone }}</div>
|
<div class="user-right">{{ userInfo.phone }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconifyIconOffline class="check-zh" :icon="Mail" />
|
<IconifyIconOffline class="check-zh" :icon="Mail" />
|
||||||
用户邮箱
|
用户邮箱
|
||||||
<div class="user-right">{{ userInfo.user.email }}</div>
|
<div class="user-right">{{ userInfo.email }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconifyIconOffline class="check-zh" :icon="Secure" />
|
<IconifyIconOffline class="check-zh" :icon="Secure" />
|
||||||
@ -135,7 +135,7 @@ defineOptions({
|
|||||||
clearable
|
clearable
|
||||||
style="width: 35%"
|
style="width: 35%"
|
||||||
/>
|
/>
|
||||||
<span style=" margin-left: 10px;color: #e6a23c"
|
<span style="margin-left: 10px; color: #e6a23c"
|
||||||
>⚠️用户昵称不作为登录使用</span
|
>⚠️用户昵称不作为登录使用</span
|
||||||
>
|
>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -164,7 +164,7 @@ defineOptions({
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<el-input v-model="user.phone" clearable style="width: 35%" />
|
<el-input v-model="user.phone" clearable style="width: 35%" />
|
||||||
<span style=" margin-left: 10px;color: #e6a23c"
|
<span style="margin-left: 10px; color: #e6a23c"
|
||||||
>⚠️手机号码不能重复</span
|
>⚠️手机号码不能重复</span
|
||||||
>
|
>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -4,7 +4,7 @@ import { baseUrlAvatar } from "@/api/utils";
|
|||||||
import { zxcvbn } from "@zxcvbn-ts/core";
|
import { zxcvbn } from "@zxcvbn-ts/core";
|
||||||
import { isAllEmpty, isNull, isEmail } from "@pureadmin/utils";
|
import { isAllEmpty, isNull, isEmail } from "@pureadmin/utils";
|
||||||
import { addDialog } from "@/components/ReDialog";
|
import { addDialog } from "@/components/ReDialog";
|
||||||
import Cookies from "js-cookie";
|
import { storageLocal } from "@pureadmin/utils";
|
||||||
import * as User from "@/api/system/user";
|
import * as User from "@/api/system/user";
|
||||||
import {
|
import {
|
||||||
ElForm,
|
ElForm,
|
||||||
@ -55,7 +55,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
});
|
});
|
||||||
/** 用户信息 */
|
/** 用户信息 */
|
||||||
const userInfo = computed(() => {
|
const userInfo = computed(() => {
|
||||||
return JSON.parse(Cookies.get("userInfo"));
|
return storageLocal().getItem("user-info").user;
|
||||||
});
|
});
|
||||||
/** 获取邮箱验证码 */
|
/** 获取邮箱验证码 */
|
||||||
const getEmailCode = email => {
|
const getEmailCode = email => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user