Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
6a7019ec1a | |
![]() |
7375986a8e | |
![]() |
251936dcdc | |
![]() |
921c38cea2 | |
![]() |
718f255817 | |
![]() |
433b60e720 |
|
@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
|
|||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://lin-xin.gitee.io/images/weixin.jpg
|
||||
custom: https://lin-xin.github.io/images/weixin.jpg
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
|
||||
基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上地址](https://lin-xin.gitee.io/example/work/)
|
||||
基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上演示](https://lin-xin.github.io/example/vue-manage-system/)
|
||||
|
||||
> Vue2 版本请看 [tag-V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0),带后台功能请看 [tsrpc-manage-system](https://github.com/lin-xin/tsrpc-manage-system)
|
||||
|
||||
[文档地址](https://lin-xin.github.io/example/vuems-doc/)
|
||||
[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)
|
||||
|
||||
## 赞助商
|
||||
|
@ -25,7 +26,7 @@
|
|||
|
||||
请作者喝杯咖啡吧!(微信号:linxin_20)
|
||||
|
||||

|
||||

|
||||
|
||||
## 前言
|
||||
|
||||
|
|
|
@ -4,14 +4,6 @@
|
|||
outline: 0 !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app,
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
|
||||
|
|
|
@ -1,61 +1,66 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<!-- 折叠按钮 -->
|
||||
<div class="header-left">
|
||||
<img class="logo" src="../assets/img/logo.svg" alt="">
|
||||
<div class="web-title">
|
||||
后台管理系统
|
||||
</div>
|
||||
<div class="collapse-btn" @click="collapseChage">
|
||||
<el-icon v-if="sidebar.collapse">
|
||||
<Expand />
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<Fold />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
<div class="btn-icon" @click="router.push('/theme')">
|
||||
<el-tooltip effect="dark" content="设置主题" placement="bottom">
|
||||
<i class="el-icon-lx-skin"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="btn-icon" @click="router.push('/ucenter')">
|
||||
<el-tooltip effect="dark" :content="message ? `有${message}条未读消息` : `消息中心`" placement="bottom">
|
||||
<i class="el-icon-lx-notice"></i>
|
||||
</el-tooltip>
|
||||
<span class="btn-bell-badge" v-if="message"></span>
|
||||
</div>
|
||||
<div class="btn-icon" @click="setFullScreen">
|
||||
<el-tooltip effect="dark" content="全屏" placement="bottom">
|
||||
<i class="el-icon-lx-full"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 用户头像 -->
|
||||
<el-avatar class="user-avator" :size="30" :src="imgurl" />
|
||||
<!-- 用户名下拉菜单 -->
|
||||
<el-dropdown class="user-name" trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
{{ username }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
|
||||
<el-dropdown-item>项目仓库</el-dropdown-item>
|
||||
</a>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<!-- 折叠按钮 -->
|
||||
<div class="header-left">
|
||||
<img class="logo" src="../assets/img/logo.svg" alt="" />
|
||||
<div class="web-title">后台管理系统</div>
|
||||
<div class="collapse-btn" @click="collapseChage">
|
||||
<el-icon v-if="sidebar.collapse">
|
||||
<Expand />
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<Fold />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
<div class="btn-icon" @click="router.push('/theme')">
|
||||
<el-tooltip effect="dark" content="设置主题" placement="bottom">
|
||||
<i class="el-icon-lx-skin"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="btn-icon" @click="router.push('/ucenter')">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="message ? `有${message}条未读消息` : `消息中心`"
|
||||
placement="bottom"
|
||||
>
|
||||
<i class="el-icon-lx-notice"></i>
|
||||
</el-tooltip>
|
||||
<span class="btn-bell-badge" v-if="message"></span>
|
||||
</div>
|
||||
<div class="btn-icon" @click="setFullScreen">
|
||||
<el-tooltip effect="dark" content="全屏" placement="bottom">
|
||||
<i class="el-icon-lx-full"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 用户头像 -->
|
||||
<el-avatar class="user-avator" :size="30" :src="imgurl" />
|
||||
<!-- 用户名下拉菜单 -->
|
||||
<el-dropdown class="user-name" trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
{{ username }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
|
||||
<el-dropdown-item>项目仓库</el-dropdown-item>
|
||||
</a>
|
||||
<a href="https://lin-xin.gitee.io/example/vuems-doc/" target="_blank">
|
||||
<el-dropdown-item>官方文档</el-dropdown-item>
|
||||
</a>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
|
@ -63,138 +68,137 @@ import { useSidebarStore } from '../store/sidebar';
|
|||
import { useRouter } from 'vue-router';
|
||||
import imgurl from '../assets/img/img.jpg';
|
||||
|
||||
const username: string | null = localStorage.getItem('ms_username');
|
||||
const username: string | null = localStorage.getItem('vuems_name');
|
||||
const message: number = 2;
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
// 侧边栏折叠
|
||||
const collapseChage = () => {
|
||||
sidebar.handleCollapse();
|
||||
sidebar.handleCollapse();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (document.body.clientWidth < 1500) {
|
||||
collapseChage();
|
||||
}
|
||||
if (document.body.clientWidth < 1500) {
|
||||
collapseChage();
|
||||
}
|
||||
});
|
||||
|
||||
// 用户名下拉菜单选择事件
|
||||
const router = useRouter();
|
||||
const handleCommand = (command: string) => {
|
||||
if (command == 'loginout') {
|
||||
localStorage.removeItem('ms_username');
|
||||
router.push('/login');
|
||||
} else if (command == 'user') {
|
||||
router.push('/ucenter');
|
||||
}
|
||||
if (command == 'loginout') {
|
||||
localStorage.removeItem('vuems_name');
|
||||
router.push('/login');
|
||||
} else if (command == 'user') {
|
||||
router.push('/ucenter');
|
||||
}
|
||||
};
|
||||
|
||||
const setFullScreen = () => {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
document.body.requestFullscreen.call(document.body);
|
||||
}
|
||||
}
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
document.body.requestFullscreen.call(document.body);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
color: var(--header-text-color);
|
||||
background-color: var(--header-bg-color);
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
color: var(--header-text-color);
|
||||
background-color: var(--header-bg-color);
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 35px;
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
.web-title {
|
||||
margin: 0 40px 0 10px;
|
||||
font-size: 22px;
|
||||
margin: 0 40px 0 10px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.collapse-btn:hover {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
float: right;
|
||||
padding-right: 50px;
|
||||
float: right;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.header-user-con {
|
||||
display: flex;
|
||||
height: 70px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 70px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-fullscreen {
|
||||
transform: rotate(45deg);
|
||||
margin-right: 5px;
|
||||
font-size: 24px;
|
||||
transform: rotate(45deg);
|
||||
margin-right: 5px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--header-text-color);
|
||||
margin: 0 5px;
|
||||
font-size: 20px;
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--header-text-color);
|
||||
margin: 0 5px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.btn-bell-badge {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 0px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background: #f56c6c;
|
||||
color: var(--header-text-color);
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 0px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background: #f56c6c;
|
||||
color: var(--header-text-color);
|
||||
}
|
||||
|
||||
|
||||
.user-avator {
|
||||
margin: 0 10px 0 20px;
|
||||
margin: 0 10px 0 20px;
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
color: var(--header-text-color);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--header-text-color);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,27 +1,32 @@
|
|||
<template>
|
||||
<div class="tabs-container">
|
||||
<el-tabs v-model="activePath" class="tabs" type="card" closable @tab-click="clickTabls" @tab-remove="closeTabs">
|
||||
<el-tab-pane v-for="item in tabs.list" :key="item.path" :label="item.title" :name="item.path"
|
||||
@click="setTags(item)"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="Tabs-close-box">
|
||||
<el-dropdown @command="handleTags">
|
||||
<el-button size="small" type="primary" plain>
|
||||
标签选项
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu size="small">
|
||||
<el-dropdown-item command="other">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item command="current">关闭当前</el-dropdown-item>
|
||||
<el-dropdown-item command="all">关闭所有</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-container">
|
||||
<el-tabs v-model="activePath" class="tabs" type="card" closable @tab-click="clickTabls" @tab-remove="closeTabs">
|
||||
<el-tab-pane
|
||||
v-for="item in tabs.list"
|
||||
:key="item.path"
|
||||
:label="item.title"
|
||||
:name="item.path"
|
||||
@click="setTags(item)"
|
||||
></el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="Tabs-close-box">
|
||||
<el-dropdown @command="handleTags">
|
||||
<el-button size="small" type="primary" plain>
|
||||
标签选项
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu size="small">
|
||||
<el-dropdown-item command="other">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item command="current">关闭当前</el-dropdown-item>
|
||||
<el-dropdown-item command="all">关闭所有</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -31,119 +36,113 @@ import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
|
|||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const activePath = ref(route.fullPath)
|
||||
const activePath = ref(route.fullPath);
|
||||
const tabs = useTabsStore();
|
||||
// 设置标签
|
||||
const setTags = (route: any) => {
|
||||
const isExist = tabs.list.some(item => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
if (!isExist) {
|
||||
tabs.setTabsItem({
|
||||
name: route.name,
|
||||
title: route.meta.title,
|
||||
path: route.fullPath
|
||||
});
|
||||
}
|
||||
const isExist = tabs.list.some((item) => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
if (!isExist) {
|
||||
tabs.setTabsItem({
|
||||
name: route.name,
|
||||
title: route.meta.title,
|
||||
path: route.fullPath,
|
||||
});
|
||||
}
|
||||
};
|
||||
setTags(route);
|
||||
onBeforeRouteUpdate(to => {
|
||||
setTags(to);
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setTags(to);
|
||||
});
|
||||
|
||||
// 关闭全部标签
|
||||
const closeAll = () => {
|
||||
tabs.clearTabs();
|
||||
router.push('/');
|
||||
tabs.clearTabs();
|
||||
router.push('/');
|
||||
};
|
||||
// 关闭其他标签
|
||||
const closeOther = () => {
|
||||
const curItem = tabs.list.filter(item => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
tabs.closeTabsOther(curItem);
|
||||
const curItem = tabs.list.filter((item) => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
tabs.closeTabsOther(curItem);
|
||||
};
|
||||
const handleTags = (command: string) => {
|
||||
switch (command) {
|
||||
case 'current':
|
||||
// 关闭当前页面的标签页
|
||||
tabs.closeCurrentTag({
|
||||
$router: router,
|
||||
$route: route
|
||||
});
|
||||
break;
|
||||
case 'all':
|
||||
closeAll();
|
||||
break;
|
||||
switch (command) {
|
||||
case 'current':
|
||||
// 关闭当前页面的标签页
|
||||
tabs.closeCurrentTag({
|
||||
$router: router,
|
||||
$route: route,
|
||||
});
|
||||
break;
|
||||
case 'all':
|
||||
closeAll();
|
||||
break;
|
||||
|
||||
case 'other':
|
||||
closeOther();
|
||||
break;
|
||||
}
|
||||
case 'other':
|
||||
closeOther();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const clickTabls = (item: any) => {
|
||||
router.push(item.props.name);
|
||||
}
|
||||
const closeTabs = (path: any) => {
|
||||
console.log(path);
|
||||
const index = tabs.list.findIndex((item) => item.path === path);
|
||||
tabs.delTabsItem(index);
|
||||
const item = tabs.list[index] ? tabs.list[index] : tabs.list[index - 1];
|
||||
if (item) {
|
||||
path === route.fullPath && router.push(item.path);
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => route.fullPath, (newVal, oldVal) => {
|
||||
activePath.value = newVal;
|
||||
})
|
||||
router.push(item.props.name);
|
||||
};
|
||||
const closeTabs = (path: string) => {
|
||||
const index = tabs.list.findIndex((item) => item.path === path);
|
||||
tabs.delTabsItem(index);
|
||||
const item = tabs.list[index] || tabs.list[index - 1];
|
||||
router.push(item ? item.path : '/');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
(newVal, oldVal) => {
|
||||
activePath.value = newVal;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scss>
|
||||
.tabs-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
padding: 2px 120px 0 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
padding: 2px 120px 0 0;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
.el-tabs__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.el-tabs__nav {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.el-tabs__nav-next,
|
||||
.el-tabs__nav-prev {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&.el-tabs {
|
||||
--el-tabs-header-height: 28px;
|
||||
}
|
||||
.el-tabs__nav {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.el-tabs__nav-next,
|
||||
.el-tabs__nav-prev {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&.el-tabs {
|
||||
--el-tabs-header-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.Tabs-close-box {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
box-sizing: border-box;
|
||||
padding-top: 1px;
|
||||
text-align: center;
|
||||
width: 110px;
|
||||
height: 30px;
|
||||
background: #fff;
|
||||
box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
box-sizing: border-box;
|
||||
padding-top: 1px;
|
||||
text-align: center;
|
||||
width: 110px;
|
||||
height: 30px;
|
||||
background: #fff;
|
||||
box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
|
|||
name: 'dashboard',
|
||||
meta: {
|
||||
title: '系统首页',
|
||||
permiss: '0',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "dashboard" */ '../views/dashboard.vue'),
|
||||
},
|
||||
|
@ -273,12 +273,12 @@ const router = createRouter({
|
|||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start();
|
||||
const role = localStorage.getItem('ms_username');
|
||||
const role = localStorage.getItem('vuems_name');
|
||||
const permiss = usePermissStore();
|
||||
|
||||
if (!role && to.meta.noAuth !== true) {
|
||||
next('/login');
|
||||
} else if (to.meta.permiss && !permiss.key.includes(to.meta.permiss)) {
|
||||
} else if (typeof to.meta.permiss == 'string' && !permiss.key.includes(to.meta.permiss)) {
|
||||
// 如果没有权限,则进入403
|
||||
next('/403');
|
||||
} else {
|
||||
|
|
|
@ -6,48 +6,50 @@ interface ObjectList {
|
|||
|
||||
export const usePermissStore = defineStore('permiss', {
|
||||
state: () => {
|
||||
const keys = localStorage.getItem('ms_keys');
|
||||
const defaultList: ObjectList = {
|
||||
admin: [
|
||||
'0',
|
||||
'1',
|
||||
'11',
|
||||
'12',
|
||||
'13',
|
||||
'2',
|
||||
'21',
|
||||
'22',
|
||||
'23',
|
||||
'24',
|
||||
'25',
|
||||
'26',
|
||||
'27',
|
||||
'28',
|
||||
'29',
|
||||
'291',
|
||||
'292',
|
||||
'3',
|
||||
'31',
|
||||
'32',
|
||||
'33',
|
||||
'34',
|
||||
'4',
|
||||
'41',
|
||||
'42',
|
||||
'5',
|
||||
'7',
|
||||
'6',
|
||||
'61',
|
||||
'62',
|
||||
'63',
|
||||
'64',
|
||||
'65',
|
||||
'66',
|
||||
],
|
||||
user: ['0', '1', '11', '12', '13'],
|
||||
};
|
||||
const username = localStorage.getItem('vuems_name');
|
||||
console.log(username);
|
||||
return {
|
||||
key: keys ? JSON.parse(keys) : <string[]>[],
|
||||
defaultList: <ObjectList>{
|
||||
admin: [
|
||||
'0',
|
||||
'1',
|
||||
'11',
|
||||
'12',
|
||||
'13',
|
||||
'2',
|
||||
'21',
|
||||
'22',
|
||||
'23',
|
||||
'24',
|
||||
'25',
|
||||
'26',
|
||||
'27',
|
||||
'28',
|
||||
'29',
|
||||
'291',
|
||||
'292',
|
||||
'3',
|
||||
'31',
|
||||
'32',
|
||||
'33',
|
||||
'34',
|
||||
'4',
|
||||
'41',
|
||||
'42',
|
||||
'5',
|
||||
'7',
|
||||
'6',
|
||||
'61',
|
||||
'62',
|
||||
'63',
|
||||
'64',
|
||||
'65',
|
||||
'66',
|
||||
],
|
||||
user: ['0', '1', '11', '12', '13'],
|
||||
},
|
||||
key: (username == 'admin' ? defaultList.admin : defaultList.user) as string[],
|
||||
defaultList,
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
<template>
|
||||
<v-header />
|
||||
<v-sidebar />
|
||||
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
|
||||
<v-tabs></v-tabs>
|
||||
<div class="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<keep-alive :include="tabs.nameList">
|
||||
<component :is="Component"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<v-header />
|
||||
<v-sidebar />
|
||||
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
|
||||
<v-tabs></v-tabs>
|
||||
<div class="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<keep-alive :include="tabs.nameList">
|
||||
<component :is="Component"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useSidebarStore } from '@/store/sidebar';
|
||||
|
@ -26,31 +28,36 @@ const tabs = useTabsStore();
|
|||
</script>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content-box {
|
||||
position: absolute;
|
||||
left: 250px;
|
||||
right: 0;
|
||||
top: 70px;
|
||||
bottom: 0;
|
||||
padding-bottom: 30px;
|
||||
-webkit-transition: left 0.3s ease-in-out;
|
||||
transition: left 0.3s ease-in-out;
|
||||
background: #eef0fc;
|
||||
position: absolute;
|
||||
left: 250px;
|
||||
right: 0;
|
||||
top: 70px;
|
||||
bottom: 0;
|
||||
padding-bottom: 30px;
|
||||
-webkit-transition: left 0.3s ease-in-out;
|
||||
transition: left 0.3s ease-in-out;
|
||||
background: #eef0fc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content::-webkit-scrollbar {
|
||||
width: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.content-collapse {
|
||||
left: 65px;
|
||||
left: 65px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-code">403</div>
|
||||
<div class="error-desc">啊哦~ 你没有权限访问该页面哦</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-code">403</div>
|
||||
<div class="error-desc">啊哦~ 你没有权限访问该页面哦</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="403">
|
||||
|
@ -18,50 +18,50 @@ import { useRouter } from 'vue-router';
|
|||
|
||||
const router = useRouter();
|
||||
const goBack = () => {
|
||||
router.go(-2);
|
||||
router.go(-2);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #eef0fc;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #eef0fc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.error-box {
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
padding: 80px 50px;
|
||||
border-radius: 5px;
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
padding: 80px 50px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
line-height: 1;
|
||||
font-size: 100px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center
|
||||
line-height: 1;
|
||||
font-size: 100px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
font-size: 20px;
|
||||
color: #777;
|
||||
text-align: center
|
||||
font-size: 20px;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-handle {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-btn {
|
||||
margin-left: 100px;
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-code">404</div>
|
||||
<div class="error-desc">啊哦~ 你所访问的页面不存在</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-code">404</div>
|
||||
<div class="error-desc">啊哦~ 你所访问的页面不存在</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="404">
|
||||
|
@ -18,50 +18,50 @@ import { useRouter } from 'vue-router';
|
|||
|
||||
const router = useRouter();
|
||||
const goBack = () => {
|
||||
router.go(-1);
|
||||
router.go(-1);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #eef0fc;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #eef0fc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.error-box {
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
padding: 80px 50px;
|
||||
border-radius: 5px;
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
padding: 80px 50px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
line-height: 1;
|
||||
font-size: 100px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center
|
||||
line-height: 1;
|
||||
font-size: 100px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
font-size: 20px;
|
||||
color: #777;
|
||||
text-align: center
|
||||
font-size: 20px;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-handle {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-btn {
|
||||
margin-left: 100px;
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -83,10 +83,9 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
|||
formEl.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
ElMessage.success('登录成功');
|
||||
localStorage.setItem('ms_username', param.username);
|
||||
localStorage.setItem('vuems_name', param.username);
|
||||
const keys = permiss.defaultList[param.username == 'admin' ? 'admin' : 'user'];
|
||||
permiss.handleSet(keys);
|
||||
localStorage.setItem('ms_keys', JSON.stringify(keys));
|
||||
router.push('/');
|
||||
if (checked.value) {
|
||||
localStorage.setItem('login-param', JSON.stringify(param));
|
||||
|
@ -110,7 +109,7 @@ tabs.clearTabs();
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
<div class="login-bg">
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<img class="logo mr10" src="../../assets/img/logo.svg" alt="">
|
||||
<div class="login-title">
|
||||
后台管理系统
|
||||
</div>
|
||||
<img class="logo mr10" src="../../assets/img/logo.svg" alt="" />
|
||||
<div class="login-title">后台管理系统</div>
|
||||
</div>
|
||||
<el-form :model="param" :rules="rules" ref="register" size="large">
|
||||
<el-form-item prop="username">
|
||||
|
@ -27,8 +25,12 @@
|
|||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input type="password" placeholder="密码" v-model="param.password"
|
||||
@keyup.enter="submitForm(register)">
|
||||
<el-input
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
v-model="param.password"
|
||||
@keyup.enter="submitForm(register)"
|
||||
>
|
||||
<template #prepend>
|
||||
<el-icon>
|
||||
<Lock />
|
||||
|
@ -37,7 +39,9 @@
|
|||
</el-input>
|
||||
</el-form-item>
|
||||
<el-button class="login-btn" type="primary" size="large" @click="submitForm(register)">注册</el-button>
|
||||
<p class="login-text">已有账号,<el-link type="primary" @click="$router.push('/login')">立即登录</el-link></p>
|
||||
<p class="login-text">
|
||||
已有账号,<el-link type="primary" @click="$router.push('/login')">立即登录</el-link>
|
||||
</p>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -79,7 +83,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -88,7 +91,7 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-button class="login-btn" type="primary" size="large" @click="submitForm(register)">发送邮件</el-button>
|
||||
<el-button class="login-btn" type="primary" size="large" @click="submitForm(register)"
|
||||
>发送邮件</el-button
|
||||
>
|
||||
<p class="login-text"><el-link type="primary" @click="$router.push('/login')">返回登录</el-link></p>
|
||||
</el-form>
|
||||
</div>
|
||||
|
@ -31,7 +33,11 @@ const param = ref({
|
|||
const rules: FormRules = {
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||
message: '请输入正确的邮箱格式',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
};
|
||||
const register = ref<FormInstance>();
|
||||
|
@ -45,7 +51,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -54,7 +59,7 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ import 'vue-cropper/dist/index.css';
|
|||
import avatar from '@/assets/img/img.jpg';
|
||||
import TabsComp from '../element/tabs.vue';
|
||||
|
||||
const name = localStorage.getItem('ms_username');
|
||||
const name = localStorage.getItem('vuems_name');
|
||||
const form = reactive({
|
||||
new1: '',
|
||||
new: '',
|
||||
|
|
|
@ -1,253 +0,0 @@
|
|||
<template>
|
||||
<div class="user-container">
|
||||
<el-card class="user-profile mgb20" shadow="hover" :body-style="{ padding: '0px' }">
|
||||
<div class="user-profile-bg"></div>
|
||||
<div class="user-avatar-wrap">
|
||||
<el-avatar class="user-avatar" :size="120" :src="avatarImg" />
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="info-name">{{ name }}</div>
|
||||
<div class="info-desc">
|
||||
<!-- <span>{{ name }}</span>
|
||||
<el-divider direction="vertical" /> -->
|
||||
<span>FE Developer</span>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link href="https://lin-xin.gitee.io" target="_blank">lin-xin.gitee.io</el-link>
|
||||
</div>
|
||||
<!-- <div class="info-icon">
|
||||
<a href="https://github.com/lin-xin" target="_blank"> <i class="el-icon-lx-github-fill"></i></a>
|
||||
<i class="el-icon-lx-qq-fill"></i>
|
||||
<i class="el-icon-lx-facebook-fill"></i>
|
||||
<i class="el-icon-lx-twitter-fill"></i>
|
||||
</div> -->
|
||||
</div>
|
||||
<!-- <div class="user-footer">
|
||||
<div class="user-footer-item">
|
||||
<el-statistic title="Follower" value="18K" />
|
||||
</div>
|
||||
<div class="user-footer-item">
|
||||
<el-statistic title="Following" :value="666" />
|
||||
</div>
|
||||
<div class="user-footer-item">
|
||||
<el-statistic title="Total Post" :value="888" />
|
||||
</div>
|
||||
</div> -->
|
||||
</el-card>
|
||||
<el-card class="user-content" shadow="hover"
|
||||
:body-style="{ padding: '20px 50px', height: '100%', boxSizing: 'border-box' }">
|
||||
<el-tabs v-model="activeName">
|
||||
|
||||
<el-tab-pane name="label1" label="消息通知" class="user-tabpane">
|
||||
<TabsComp />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="label2" label="我的头像" class="user-tabpane">
|
||||
<div class="crop-wrap" v-if="activeName === 'label2'">
|
||||
<vueCropper ref="cropper" :img="imgSrc" :autoCrop="true" :centerBox="true" :full="true"
|
||||
mode="contain">
|
||||
</vueCropper>
|
||||
</div>
|
||||
<el-button class="crop-demo-btn" type="primary">选择图片
|
||||
<input class="crop-input" type="file" name="image" accept="image/*" @change="setImage" />
|
||||
</el-button>
|
||||
<el-button type="success" @click="saveAvatar">上传并保存</el-button>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="label3" label="修改密码" class="user-tabpane">
|
||||
<el-form class="w500" label-position="top">
|
||||
<el-form-item label="旧密码:">
|
||||
<el-input type="password" v-model="form.old"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码:">
|
||||
<el-input type="password" v-model="form.new"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认新密码:">
|
||||
<el-input type="password" v-model="form.new1"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="label4" label="赞赏作者" class="user-tabpane">
|
||||
<div class="plugins-tips">
|
||||
如果该框架 <el-link href="https://github.com/lin-xin/vue-manage-system"
|
||||
target="_blank">vue-manage-system</el-link> 对你有帮助,那就请作者喝杯饮料吧!<el-icon>
|
||||
<ColdDrink />
|
||||
</el-icon> 加微信号 linxin_20 探讨问题。
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://lin-xin.gitee.io/images/weixin.jpg" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ucenter">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { VueCropper } from "vue-cropper"
|
||||
import 'vue-cropper/dist/index.css'
|
||||
import avatar from '@/assets/img/img.jpg';
|
||||
import TabsComp from '../element/tabs.vue';
|
||||
|
||||
const name = localStorage.getItem('ms_username');
|
||||
const form = reactive({
|
||||
new1: '',
|
||||
new: '',
|
||||
old: ''
|
||||
});
|
||||
const onSubmit = () => { };
|
||||
|
||||
const activeName = ref('label1');
|
||||
|
||||
const avatarImg = ref(avatar);
|
||||
const imgSrc = ref(avatar);
|
||||
const cropImg = ref('');
|
||||
const cropper: any = ref();
|
||||
|
||||
const setImage = (e: any) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file.type.includes('image/')) {
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event: any) => {
|
||||
imgSrc.value = event.target.result;
|
||||
cropper.value && cropper.value.replace(event.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const cropImage = () => {
|
||||
cropImg.value = cropper.value?.getCroppedCanvas().toDataURL();
|
||||
};
|
||||
|
||||
const saveAvatar = () => {
|
||||
avatarImg.value = cropImg.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* .user-container {
|
||||
display: flex;
|
||||
} */
|
||||
|
||||
.user-profile-bg {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background-image: url('../../assets/img/bahnhofsidylle.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
position: relative;
|
||||
/* width: 500px; */
|
||||
/* margin-right: 20px; */
|
||||
/* flex: 0 0 auto;
|
||||
align-self: flex-start;
|
||||
} */
|
||||
}
|
||||
|
||||
.user-avatar-wrap {
|
||||
position: absolute;
|
||||
top: 90px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
border: 5px solid #fff;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 7px 12px 0 rgba(62, 57, 107, .16)
|
||||
}
|
||||
|
||||
.user-info {
|
||||
text-align: center;
|
||||
padding: 70px 0 20px;
|
||||
}
|
||||
|
||||
.info-name {
|
||||
margin: 0 0 10px;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
color: #373a3c;
|
||||
}
|
||||
|
||||
.info-desc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.info-desc,
|
||||
.info-desc a {
|
||||
font-size: 18px;
|
||||
color: #55595c;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.info-icon i {
|
||||
font-size: 30px;
|
||||
margin: 0 10px;
|
||||
color: #343434;
|
||||
}
|
||||
|
||||
.user-content {
|
||||
flex: 1
|
||||
}
|
||||
|
||||
.user-tabpane {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.crop-wrap {
|
||||
width: 600px;
|
||||
height: 350px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.crop-demo-btn {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.crop-input {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.w500 {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.user-footer {
|
||||
display: flex;
|
||||
border-top: 1px solid rgba(83, 70, 134, 0.1);
|
||||
}
|
||||
|
||||
.user-footer-item {
|
||||
padding: 20px 0;
|
||||
width: 33.3333333333%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-footer>div+div {
|
||||
border-left: 1px solid rgba(83, 70, 134, 0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.el-tabs.el-tabs--left {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue