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
|
liberapay: # Replace with a single Liberapay username
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
otechie: # Replace with a single Otechie 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">
|
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||||
</a>
|
</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)
|
> 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)
|
[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)
|
||||||
|
|
||||||
## 赞助商
|
## 赞助商
|
||||||
|
@ -25,7 +26,7 @@
|
||||||
|
|
||||||
请作者喝杯咖啡吧!(微信号:linxin_20)
|
请作者喝杯咖啡吧!(微信号:linxin_20)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 前言
|
## 前言
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,6 @@
|
||||||
outline: 0 !important;
|
outline: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
|
||||||
body,
|
|
||||||
#app,
|
|
||||||
.wrapper {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
|
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<!-- 折叠按钮 -->
|
<!-- 折叠按钮 -->
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<img class="logo" src="../assets/img/logo.svg" alt="">
|
<img class="logo" src="../assets/img/logo.svg" alt="" />
|
||||||
<div class="web-title">
|
<div class="web-title">后台管理系统</div>
|
||||||
后台管理系统
|
|
||||||
</div>
|
|
||||||
<div class="collapse-btn" @click="collapseChage">
|
<div class="collapse-btn" @click="collapseChage">
|
||||||
<el-icon v-if="sidebar.collapse">
|
<el-icon v-if="sidebar.collapse">
|
||||||
<Expand />
|
<Expand />
|
||||||
|
@ -23,7 +21,11 @@
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-icon" @click="router.push('/ucenter')">
|
<div class="btn-icon" @click="router.push('/ucenter')">
|
||||||
<el-tooltip effect="dark" :content="message ? `有${message}条未读消息` : `消息中心`" placement="bottom">
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="message ? `有${message}条未读消息` : `消息中心`"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
<i class="el-icon-lx-notice"></i>
|
<i class="el-icon-lx-notice"></i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span class="btn-bell-badge" v-if="message"></span>
|
<span class="btn-bell-badge" v-if="message"></span>
|
||||||
|
@ -48,6 +50,9 @@
|
||||||
<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
|
<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
|
||||||
<el-dropdown-item>项目仓库</el-dropdown-item>
|
<el-dropdown-item>项目仓库</el-dropdown-item>
|
||||||
</a>
|
</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 command="user">个人中心</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
|
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
|
@ -63,7 +68,7 @@ import { useSidebarStore } from '../store/sidebar';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import imgurl from '../assets/img/img.jpg';
|
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 message: number = 2;
|
||||||
|
|
||||||
const sidebar = useSidebarStore();
|
const sidebar = useSidebarStore();
|
||||||
|
@ -82,7 +87,7 @@ onMounted(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const handleCommand = (command: string) => {
|
const handleCommand = (command: string) => {
|
||||||
if (command == 'loginout') {
|
if (command == 'loginout') {
|
||||||
localStorage.removeItem('ms_username');
|
localStorage.removeItem('vuems_name');
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
} else if (command == 'user') {
|
} else if (command == 'user') {
|
||||||
router.push('/ucenter');
|
router.push('/ucenter');
|
||||||
|
@ -95,7 +100,7 @@ const setFullScreen = () => {
|
||||||
} else {
|
} else {
|
||||||
document.body.requestFullscreen.call(document.body);
|
document.body.requestFullscreen.call(document.body);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.header {
|
.header {
|
||||||
|
@ -182,7 +187,6 @@ const setFullScreen = () => {
|
||||||
color: var(--header-text-color);
|
color: var(--header-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.user-avator {
|
.user-avator {
|
||||||
margin: 0 10px 0 20px;
|
margin: 0 10px 0 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="tabs-container">
|
<div class="tabs-container">
|
||||||
<el-tabs v-model="activePath" class="tabs" type="card" closable @tab-click="clickTabls" @tab-remove="closeTabs">
|
<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"
|
<el-tab-pane
|
||||||
@click="setTags(item)"></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>
|
</el-tabs>
|
||||||
<div class="Tabs-close-box">
|
<div class="Tabs-close-box">
|
||||||
<el-dropdown @command="handleTags">
|
<el-dropdown @command="handleTags">
|
||||||
|
@ -31,23 +36,23 @@ import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const activePath = ref(route.fullPath)
|
const activePath = ref(route.fullPath);
|
||||||
const tabs = useTabsStore();
|
const tabs = useTabsStore();
|
||||||
// 设置标签
|
// 设置标签
|
||||||
const setTags = (route: any) => {
|
const setTags = (route: any) => {
|
||||||
const isExist = tabs.list.some(item => {
|
const isExist = tabs.list.some((item) => {
|
||||||
return item.path === route.fullPath;
|
return item.path === route.fullPath;
|
||||||
});
|
});
|
||||||
if (!isExist) {
|
if (!isExist) {
|
||||||
tabs.setTabsItem({
|
tabs.setTabsItem({
|
||||||
name: route.name,
|
name: route.name,
|
||||||
title: route.meta.title,
|
title: route.meta.title,
|
||||||
path: route.fullPath
|
path: route.fullPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
setTags(route);
|
setTags(route);
|
||||||
onBeforeRouteUpdate(to => {
|
onBeforeRouteUpdate((to) => {
|
||||||
setTags(to);
|
setTags(to);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,7 +63,7 @@ const closeAll = () => {
|
||||||
};
|
};
|
||||||
// 关闭其他标签
|
// 关闭其他标签
|
||||||
const closeOther = () => {
|
const closeOther = () => {
|
||||||
const curItem = tabs.list.filter(item => {
|
const curItem = tabs.list.filter((item) => {
|
||||||
return item.path === route.fullPath;
|
return item.path === route.fullPath;
|
||||||
});
|
});
|
||||||
tabs.closeTabsOther(curItem);
|
tabs.closeTabsOther(curItem);
|
||||||
|
@ -69,7 +74,7 @@ const handleTags = (command: string) => {
|
||||||
// 关闭当前页面的标签页
|
// 关闭当前页面的标签页
|
||||||
tabs.closeCurrentTag({
|
tabs.closeCurrentTag({
|
||||||
$router: router,
|
$router: router,
|
||||||
$route: route
|
$route: route,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'all':
|
case 'all':
|
||||||
|
@ -84,23 +89,20 @@ const handleTags = (command: string) => {
|
||||||
|
|
||||||
const clickTabls = (item: any) => {
|
const clickTabls = (item: any) => {
|
||||||
router.push(item.props.name);
|
router.push(item.props.name);
|
||||||
}
|
};
|
||||||
const closeTabs = (path: any) => {
|
const closeTabs = (path: string) => {
|
||||||
console.log(path);
|
|
||||||
const index = tabs.list.findIndex((item) => item.path === path);
|
const index = tabs.list.findIndex((item) => item.path === path);
|
||||||
tabs.delTabsItem(index);
|
tabs.delTabsItem(index);
|
||||||
const item = tabs.list[index] ? tabs.list[index] : tabs.list[index - 1];
|
const item = tabs.list[index] || tabs.list[index - 1];
|
||||||
if (item) {
|
router.push(item ? item.path : '/');
|
||||||
path === route.fullPath && router.push(item.path);
|
};
|
||||||
} else {
|
|
||||||
router.push('/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => route.fullPath, (newVal, oldVal) => {
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
(newVal, oldVal) => {
|
||||||
activePath.value = newVal;
|
activePath.value = newVal;
|
||||||
})
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scss>
|
<style scss>
|
||||||
|
@ -112,7 +114,6 @@ watch(() => route.fullPath, (newVal, oldVal) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
|
|
||||||
.el-tabs__header {
|
.el-tabs__header {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -129,8 +130,6 @@ watch(() => route.fullPath, (newVal, oldVal) => {
|
||||||
&.el-tabs {
|
&.el-tabs {
|
||||||
--el-tabs-header-height: 28px;
|
--el-tabs-header-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Tabs-close-box {
|
.Tabs-close-box {
|
||||||
|
|
|
@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: 'dashboard',
|
name: 'dashboard',
|
||||||
meta: {
|
meta: {
|
||||||
title: '系统首页',
|
title: '系统首页',
|
||||||
permiss: '0',
|
noAuth: true,
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "dashboard" */ '../views/dashboard.vue'),
|
component: () => import(/* webpackChunkName: "dashboard" */ '../views/dashboard.vue'),
|
||||||
},
|
},
|
||||||
|
@ -273,12 +273,12 @@ const router = createRouter({
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
const role = localStorage.getItem('ms_username');
|
const role = localStorage.getItem('vuems_name');
|
||||||
const permiss = usePermissStore();
|
const permiss = usePermissStore();
|
||||||
|
|
||||||
if (!role && to.meta.noAuth !== true) {
|
if (!role && to.meta.noAuth !== true) {
|
||||||
next('/login');
|
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
|
// 如果没有权限,则进入403
|
||||||
next('/403');
|
next('/403');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,10 +6,7 @@ interface ObjectList {
|
||||||
|
|
||||||
export const usePermissStore = defineStore('permiss', {
|
export const usePermissStore = defineStore('permiss', {
|
||||||
state: () => {
|
state: () => {
|
||||||
const keys = localStorage.getItem('ms_keys');
|
const defaultList: ObjectList = {
|
||||||
return {
|
|
||||||
key: keys ? JSON.parse(keys) : <string[]>[],
|
|
||||||
defaultList: <ObjectList>{
|
|
||||||
admin: [
|
admin: [
|
||||||
'0',
|
'0',
|
||||||
'1',
|
'1',
|
||||||
|
@ -47,7 +44,12 @@ export const usePermissStore = defineStore('permiss', {
|
||||||
'66',
|
'66',
|
||||||
],
|
],
|
||||||
user: ['0', '1', '11', '12', '13'],
|
user: ['0', '1', '11', '12', '13'],
|
||||||
},
|
};
|
||||||
|
const username = localStorage.getItem('vuems_name');
|
||||||
|
console.log(username);
|
||||||
|
return {
|
||||||
|
key: (username == 'admin' ? defaultList.admin : defaultList.user) as string[],
|
||||||
|
defaultList,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
|
<div class="wrapper">
|
||||||
<v-header />
|
<v-header />
|
||||||
<v-sidebar />
|
<v-sidebar />
|
||||||
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
|
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
</router-view>
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSidebarStore } from '@/store/sidebar';
|
import { useSidebarStore } from '@/store/sidebar';
|
||||||
|
@ -26,6 +28,10 @@ const tabs = useTabsStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
.content-box {
|
.content-box {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 250px;
|
left: 250px;
|
||||||
|
@ -36,6 +42,7 @@ const tabs = useTabsStore();
|
||||||
-webkit-transition: left 0.3s ease-in-out;
|
-webkit-transition: left 0.3s ease-in-out;
|
||||||
transition: left 0.3s ease-in-out;
|
transition: left 0.3s ease-in-out;
|
||||||
background: #eef0fc;
|
background: #eef0fc;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|
|
@ -29,7 +29,7 @@ const goBack = () => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background: #eef0fc;
|
background: #eef0fc;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -47,13 +47,13 @@ const goBack = () => {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
text-align: center
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-desc {
|
.error-desc {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #777;
|
color: #777;
|
||||||
text-align: center
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-handle {
|
.error-handle {
|
||||||
|
|
|
@ -29,7 +29,7 @@ const goBack = () => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background: #eef0fc;
|
background: #eef0fc;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -47,13 +47,13 @@ const goBack = () => {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
text-align: center
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-desc {
|
.error-desc {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #777;
|
color: #777;
|
||||||
text-align: center
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-handle {
|
.error-handle {
|
||||||
|
|
|
@ -83,10 +83,9 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||||
formEl.validate((valid: boolean) => {
|
formEl.validate((valid: boolean) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
ElMessage.success('登录成功');
|
ElMessage.success('登录成功');
|
||||||
localStorage.setItem('ms_username', param.username);
|
localStorage.setItem('vuems_name', param.username);
|
||||||
const keys = permiss.defaultList[param.username == 'admin' ? 'admin' : 'user'];
|
const keys = permiss.defaultList[param.username == 'admin' ? 'admin' : 'user'];
|
||||||
permiss.handleSet(keys);
|
permiss.handleSet(keys);
|
||||||
localStorage.setItem('ms_keys', JSON.stringify(keys));
|
|
||||||
router.push('/');
|
router.push('/');
|
||||||
if (checked.value) {
|
if (checked.value) {
|
||||||
localStorage.setItem('login-param', JSON.stringify(param));
|
localStorage.setItem('login-param', JSON.stringify(param));
|
||||||
|
@ -110,7 +109,7 @@ tabs.clearTabs();
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
<div class="login-bg">
|
<div class="login-bg">
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<div class="login-header">
|
<div class="login-header">
|
||||||
<img class="logo mr10" src="../../assets/img/logo.svg" alt="">
|
<img class="logo mr10" src="../../assets/img/logo.svg" alt="" />
|
||||||
<div class="login-title">
|
<div class="login-title">后台管理系统</div>
|
||||||
后台管理系统
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<el-form :model="param" :rules="rules" ref="register" size="large">
|
<el-form :model="param" :rules="rules" ref="register" size="large">
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
|
@ -27,8 +25,12 @@
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<el-input type="password" placeholder="密码" v-model="param.password"
|
<el-input
|
||||||
@keyup.enter="submitForm(register)">
|
type="password"
|
||||||
|
placeholder="密码"
|
||||||
|
v-model="param.password"
|
||||||
|
@keyup.enter="submitForm(register)"
|
||||||
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<Lock />
|
<Lock />
|
||||||
|
@ -37,7 +39,9 @@
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</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>
|
<p class="login-text">
|
||||||
|
已有账号,<el-link type="primary" @click="$router.push('/login')">立即登录</el-link>
|
||||||
|
</p>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,7 +83,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -88,7 +91,7 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</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>
|
<p class="login-text"><el-link type="primary" @click="$router.push('/login')">返回登录</el-link></p>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +33,11 @@ const param = ref({
|
||||||
const rules: FormRules = {
|
const rules: FormRules = {
|
||||||
email: [
|
email: [
|
||||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
{ 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>();
|
const register = ref<FormInstance>();
|
||||||
|
@ -45,7 +51,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -54,7 +59,7 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
|
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 avatar from '@/assets/img/img.jpg';
|
||||||
import TabsComp from '../element/tabs.vue';
|
import TabsComp from '../element/tabs.vue';
|
||||||
|
|
||||||
const name = localStorage.getItem('ms_username');
|
const name = localStorage.getItem('vuems_name');
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
new1: '',
|
new1: '',
|
||||||
new: '',
|
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