commit:微服务工程目录

This commit is contained in:
Jerry
2021-09-28 16:28:10 +08:00
parent 13ba3386dc
commit a6ae0d1a79
1012 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<el-breadcrumb-item :to="{name: 'welcome'}" :replace="true">
<i class="el-icon-s-home" style="margin-right: 5px;" />主页
</el-breadcrumb-item>
<el-breadcrumb-item v-for="item in menuPathList" :key="item.menuId">
{{item.menuName}}
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex';
export default {
created () {
this.getBreadcrumb();
},
data () {
return {
menuPathList: null
};
},
watch: {
$route (newValue) {
if (newValue.name === 'welcome') this.setCurrentMenuId(null);
this.getBreadcrumb();
}
},
methods: {
getBreadcrumb () {
this.menuPathList = this.getMultiColumn ? null : this.getCurrentMenuPath;
},
...mapMutations(['setCurrentMenuId'])
},
computed: {
...mapGetters(['getCurrentMenuPath', 'getMultiColumn'])
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 60px;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<div class="form-single-fragment" style="position: relative;">
<el-form ref="formModifyPassword" :model="formData" class="full-width-input" :rules="rules" style="width: 100%;"
label-width="120px" size="mini" label-position="right" @submit.native.prevent>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="旧密码" prop="oldPassword">
<el-input class="input-item" v-model.trim="formData.oldPassword"
type="password" show-password
:clearable="true" placeholder="旧密码" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="新密码" prop="password">
<el-input class="input-item" v-model.trim="formData.password"
type="password" show-password
:clearable="true" placeholder="新密码" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="新密码确认" prop="repeatPassword">
<el-input class="input-item" v-model.trim="formData.repeatPassword"
type="password" show-password
:clearable="true" placeholder="新密码确认" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-row class="no-scroll flex-box" type="flex" justify="end">
<el-button type="primary" size="mini" :plain="true"
@click="onCancel(false)">
取消
</el-button>
<el-button type="primary" size="mini" @click="onSave()">
保存
</el-button>
</el-row>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
import { SystemController } from '@/api';
import { encrypt } from '@/utils';
export default {
data () {
return {
formData: {
oldPassword: undefined,
password: undefined,
repeatPassword: undefined
},
rules: {
'oldPassword': [
{required: true, message: '请输入旧密码', trigger: 'blur'}
],
'password': [
{required: true, message: '请输入新密码', trigger: 'blur'}
],
'repeatPassword': [
{required: true, message: '请输入新密码', trigger: 'blur'}
]
}
}
},
methods: {
onCancel (isSuccess) {
if (this.observer != null) {
this.observer.cancel(isSuccess);
}
},
onSave () {
this.$refs.formModifyPassword.validate((valid) => {
if (!valid) return;
if (this.formData.password !== this.formData.repeatPassword) {
this.$message.error('两次密码输入不一致,请核对!');
return;
}
let params = {
oldPass: encrypt(this.formData.oldPassword),
newPass: encrypt(this.formData.password)
};
SystemController.changePassword(this, params).then(res => {
this.$message.success('密码修改成功');
this.onCancel(true);
}).catch(e => {});
});
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,59 @@
<template>
<div class="menu-wrapper">
<el-menu-item ref="item" :index="menu.menuId + ''" :key="menu.menuId" v-if="menu.children == null || menu.children.length <= 0">
<template slot="title">
<i v-if="menu.icon" :class="menu.icon" style="margin-right: 5px; font-size: 18px;" :style="getIconStyle(menu.icon)"></i>
<span slot="title" :style="getTextStyle(!menu.icon)">{{menu.menuName}}</span>
</template>
</el-menu-item>
<el-submenu v-else :index="menu.menuId + ''" :key="menu.menuId">
<template slot="title">
<i v-if="menu.icon" :class="menu.icon" style="margin-right: 5px; font-size: 18px;" :style="getIconStyle(menu.icon)"></i>
<span slot="title" :style="getTextStyle(!menu.icon)" v-show="!getCollapse">{{menu.menuName}}</span>
</template>
<template v-for="child in menu.children">
<menu-item class="nest-menu" :menu="child" :isChild="true" :key="child.menuId" />
</template>
</el-submenu>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
name: 'menuItem',
props: {
menu: {
type: Object,
required: true,
default: undefined
},
isChild: {
type: Boolean,
default: false
}
},
methods: {
getIconStyle (isShow) {
if (isShow && this.isChild) {
return [
{ 'margin-left': '13px' }
]
}
},
getTextStyle (isShow) {
if (isShow && this.isChild) {
return [
{ 'padding-left': '13px' }
]
}
}
},
computed: {
showText () {
return !this.getCollapse;
},
...mapGetters(['getCollapse'])
}
}
</script>

View File

@@ -0,0 +1,82 @@
<template>
<div style="height: 100%; position: relative;" class="sidebar-bg">
<div class="sidebar-title">
<img :src="logoImage" style="width: 32px; height: 32px; border-radius: 50%;" />
<p class="sidebar-title-text">{{getProjectName()}}</p>
</div>
<div class="left-menu" style="height: 100%; padding-bottom: 60px;">
<el-scrollbar wrap-class="scrollbar_dropdown__wrap" style="height: 100%;">
<el-menu ref="menu" mode="vertical" :default-active="getCurrentMenuId" :unique-opened="true" @select="selectMenu"
:active-text-color="activeTextColor" :collapse="getCollapse" >
<template v-for="menu in getMenuList">
<menu-item :menu="menu" :key="menu.menuId" />
</template>
</el-menu>
</el-scrollbar>
</div>
</div>
</template>
<script>
import menuItem from './menu-item.vue';
import { mapGetters, mapMutations } from 'vuex';
import projectConfig from '@/core/config';
export default {
data () {
return {
isCollapse: false,
collapseLeft: '200px',
showCollapseBtn: true,
logoImage: require('../../../../assets/img/logo.png')
}
},
components: {
'menu-item': menuItem
},
computed: {
activeTextColor () {
return undefined;
},
getCollapseStyle () {
return [{
left: this.collapseLeft
}]
},
...mapGetters(['getMultiTags', 'getMenuList', 'getCollapse', 'getCurrentMenuPath', 'getCurrentMenuId', 'getMultiColumn'])
},
methods: {
onCollapseChange () {
this.showCollapseBtn = false;
setTimeout(() => {
this.setCollapse(!this.getCollapse);
this.collapseLeft = this.getCollapse ? '65px' : '200px';
this.showCollapseBtn = true;
}, 100);
},
getProjectName () {
if (this.getCollapse) {
return projectConfig.projectName.substr(0, 1);
} else {
return projectConfig.projectName;
}
},
selectMenu (index, path) {
if (this.getCurrentMenuId === index) return;
// 单页面清空所有tags和cachePage
if (!this.getMultiTags) {
this.clearAllTags();
}
this.setCurrentMenuId(index);
},
...mapMutations(['setCollapse', 'clearAllTags', 'setCurrentMenuId'])
}
};
</script>
<style scoped>
.sidebar-title-text {
font-size: 18px;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<div class="tags-item">
<span class="title">{{title}}</span>
<i :class="{'el-icon-close close': !enterClose, 'el-icon-error close hover-close': enterClose}"
v-if="supportClose" @click.stop="onClose"
@mouseenter="() => enterClose = true" @mouseleave="() => enterClose = false" />
</div>
</template>
<script>
export default {
props: {
title: String,
supportClose: {
type: Boolean,
default: true
}
},
data () {
return {
enterClose: false
}
},
methods: {
onClose () {
this.$emit('close');
}
}
}
</script>
<style lang="scss" scoped>
@import '@/assets/style/element-variables.scss';
.tags-item {
height: 30px;
line-height: 30px;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 3px;
box-sizing: border-box;
display: inline-block;
cursor: pointer;
background: white;
padding: 0px 20px;
color: $--color-text-primary;
}
.tags-item .title {
font-size: 13px;
}
.close {
height: 28px;
line-height: 28px;
display: none;
}
.tags-item.active .close {
margin-left: 10px;
display: inline-block;
}
.close.hover-close {
color: $--color-text-secondary;
}
.tags-item:hover {
color: $--color-primary;
}
.tags-item.active {
color: $--color-primary;
border-color: $--color-primary-light-5;
background-color: $--color-primary-light-9;
}
.tags-item + .tags-item {
margin-left: 5px;
}
</style>

View File

@@ -0,0 +1,213 @@
<template>
<div class="tags-panel">
<i class="el-icon-arrow-left arrow left" @click="beginPos > 0 && beginPos--" />
<i class="el-icon-arrow-right arrow right" @click="getEndTagPos >= panelWidth && beginPos++" />
<div class="main-panel">
<div class="scroll-box">
<TagItem class="item" title="主页" :class="{active: getCurrentMenuId == null}" v-show="0 >= beginPos" :supportClose="false"
@click.native="onTagItemClick(null)" @contextmenu.prevent.native="openMenu(null, $event)" />
<TagItem class="item" v-for="(item, index) in tagList" :key="item.menuId" :title="item.menuName"
:class="{active: item.menuId === getCurrentMenuId}" v-show="(index + 1) >= beginPos"
@close="onTagItemClose(item)" @click.native="onTagItemClick(item)"
@contextmenu.prevent.native="openMenu(item, $event)" />
</div>
</div>
<div v-show="visible" @click.stop="onMenuMaskClick" @contextmenu="openMaskMenu"
style="z-index: 99999; position: fixed; background: rgba(0, 0, 0, 0.01); width: 100vw; height: 100vh; top: 0px; left: 0px">
<ul class="contextmenu" style="z-index: 99999; background: white;" :style="{left: left + 'px', top: top + 'px'}">
<li @click="closeSelectTag">关闭</li>
<li @click="closeOthersTags">关闭其他</li>
</ul>
</div>
</div>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex';
import TagItem from './tagItem.vue';
export default {
props: {
tagList: Array
},
components: {
TagItem
},
data () {
return {
panelWidth: 0,
beginPos: 0,
visible: false,
top: 0,
left: 0,
selectedItem: undefined
}
},
methods: {
openMenu (item, e) {
this.visible = true;
this.selectedItem = item;
this.left = e.clientX;
this.top = e.clientY;
},
openMaskMenu (e) {
e.preventDefault();
},
onMenuMaskClick () {
this.visible = false;
},
closeSelectTag () {
if (this.selectedItem != null) this.onTagItemClose(this.selectedItem);
this.visible = false;
},
closeOthersTags () {
this.selectedItem ? this.closeOtherTags(this.selectedItem.menuId) : this.clearAllTags();
this.visible = false;
},
onTagItemClose (item) {
this.removeTag(item.menuId);
},
onTagItemClick (item) {
this.setCurrentMenuId(item ? item.menuId : undefined);
},
setCurrentTag (id) {
if (id == null) {
this.beginPos = 0;
return;
}
let curPos = -1;
for (let i = 0; i < this.tagList.length; i++) {
if (this.tagList[i].menuId === id) {
curPos = (i + 1);
break;
}
}
if (curPos > 0) {
if (curPos > this.getEndPos()) {
let timer = null;
timer = setInterval(() => {
let endPos = this.getEndPos();
if (endPos >= curPos) {
clearInterval(timer);
} else {
this.beginPos++;
}
}, 10);
}
if (curPos < this.beginPos) this.beginPos = curPos;
}
},
getEndPos () {
let width = 0;
let childList = this.$children;
let endPos = 0;
for (let i = this.beginPos; i < childList.length; i++) {
width += childList[i].$el.offsetWidth;
if (width >= this.panelWidth) break;
endPos = i;
}
return endPos;
},
onSizeChange () {
this.$nextTick(() => {
this.panelWidth = this.$el.offsetWidth - 60;
});
},
...mapMutations(['removeTag', 'setCurrentMenuId', 'closeOtherTags', 'clearAllTags'])
},
computed: {
getEndTagPos () {
let width = 0;
let childList = this.$children;
for (let i = this.beginPos; i < childList.length; i++) {
width += childList[i].$el.offsetWidth;
// 间隔距离
width += 5;
if (width > this.panelWidth) {
break;
}
}
return width;
},
...mapGetters(['getCurrentMenuId'])
},
mounted () {
this.panelWidth = this.$el.offsetWidth - 60;
window.addEventListener('resize', this.onSizeChange);
// this.getLastPosition();
},
destroyed () {
window.removeEventListener('resize', this.onSizeChange);
},
watch: {
getCurrentMenuId: {
handler (newValue) {
this.setCurrentTag(newValue);
},
immediate: true
}
}
}
</script>
<style lang="scss" scoped>
@import '@/assets/style/element-variables.scss';
.tags-panel {
}
.main-panel {
margin: 0px 30px;
}
.scroll-box {
overflow: hidden;
white-space: nowrap;
display: flex;
flex-wrap: nowrap;
align-items: center;
width: 100%;
height: 50px;
}
.arrow {
height: 50px;
line-height: 50px;
width: 30px;
text-align: center;
font-size: 14px;
cursor: pointer;
z-index: 100;
box-sizing: border-box;
}
.arrow.left {
float: left;
}
.arrow.right {
float: right;
}
.contextmenu {
margin: 0px;
z-index: 2;
position: fixed;
list-style-type: none;
padding: 5px 0px;
border-radius: 5px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
}
.contextmenu li {
margin: 0px;
padding: 7px 16px;
cursor: pointer;
}
.contextmenu li:hover {
background: #eee;
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<el-container :style="getMainStyle">
<el-aside width='250px' class="sidebar">
<side-bar style="overflow: hidden"></side-bar>
</el-aside>
<el-container style="background-color: #F5F8F9">
<el-header class="header" style="box-shadow: 0px 2px 4px 0px rgba(206, 206, 206, 0.5);">
<breadcrumb class="breadcrumb-container" />
<div class="menu-column" v-if="getMultiColumn" style="margin-left: 20px;">
<el-menu mode="horizontal" :default-active="getCurrentColumnId" @select="onColumnChange">
<el-menu-item v-for="column in getColumnList" :key="column.columnId" :index="column.columnId">{{column.columnName}}</el-menu-item>
</el-menu>
</div>
<div class="header-menu" style="flex-grow: 1;">
<el-dropdown class="user-dropdown" trigger="click" @command="handleCommand">
<span class="el-dropdown-link">{{(getUserInfo || {}).showName}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item class="user-dropdown-item" command="modifyPassword">修改密码</el-dropdown-item>
<el-dropdown-item class="user-dropdown-item" command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<img :src="header" class="header-img" />
</div>
</el-header>
<el-main :style="{'padding-bottom': '15px', 'padding-top': (getMultiTags ? '0px' : '15px')}">
<tag-panel v-if="getMultiTags" :tagList="getTagList" style="margin: 0px 20px;" />
<el-scrollbar :style="getContextStyle" wrap-class="scrollbar_dropdown__wrap">
<transition name="fade" mode="out-in">
<keep-alive :include="getCachePages">
<router-view style="margin: 0px 15px; background-color: white; overflow: hidden; padding: 20px;" :style="getRouterViewStyle" />
</keep-alive>
</transition>
</el-scrollbar>
</el-main>
</el-container>
</el-container>
</template>
<script>
import SideBar from './components/sidebar/sidebar.vue';
import { mapGetters, mapMutations } from 'vuex';
import Breadcrumb from './components/breadcrumb';
import TagPanel from './components/tags/tagPanel.vue';
import formModifyPassword from './components/formModifyPassword/index.vue';
import { SystemController } from '@/api';
import { getToken, setToken } from '@/utils';
export default {
data () {
return {
header: require('../../assets/img/default-header.jpg')
};
},
components: {
'side-bar': SideBar,
'breadcrumb': Breadcrumb,
'tag-panel': TagPanel
},
methods: {
toggleSideBar () {
this.setCollapse(!this.getCollapse);
},
onColumnChange (columnId) {
this.setCurrentColumnId(columnId);
this.clearCachePage();
this.$router.replace({
name: 'welcome'
});
},
resetDocumentClientHeight () {
let timerID;
let _this = this;
return function () {
clearTimeout(timerID);
timerID = setTimeout(() => {
var h = document.documentElement['clientHeight'];
var w = document.documentElement['clientWidth'];
_this.setClientHeight(h);
_this.setClientWidth(w);
}, 50);
}
},
handleCommand (command) {
if (command === 'logout') {
this.$confirm('是否退出登录?', '', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let options = {
headers: {
Authorization: getToken()
},
showMask: false
}
SystemController.logout(this, {}, options).catch(e => {});
this.clearAllTags();
setToken();
window.sessionStorage.removeItem('isUaaLogin');
this.$router.replace({name: 'login'});
}).catch(e => {});
} else if (command === 'modifyPassword') {
this.$dialog.show('修改密码', formModifyPassword, {
area: ['500px']
}, {}).catch(e => {});
}
},
...mapMutations([
'setClientHeight',
'setClientWidth',
'setCurrentColumnId',
'clearCachePage',
'clearAllTags',
'setUserInfo',
'setMenuList'
])
},
computed: {
getMainStyle () {
return [
{'height': this.getClientHeight + 'px'}
]
},
getContextStyle () {
return [
{'height': this.getMainContextHeight + 'px'}
]
},
getRouterViewStyle () {
return [
{'min-height': this.getMainContextHeight + 'px'}
]
},
...mapGetters([
'getMultiTags',
'getClientHeight',
'getUserInfo',
'getCollapse',
'getCachePages',
'getTagList',
'getMultiColumn',
'getCurrentColumnId',
'getColumnList',
'getMenuItem',
'getMainContextHeight'
])
},
mounted () {
let resetHeight = this.resetDocumentClientHeight();
resetHeight();
window.onresize = () => {
resetHeight();
}
// 重新获取登录信息
if (getToken() != null && getToken() !== '' && this.getUserInfo == null) {
SystemController.getLoginInfo(this, {}).then(data => {
this.setMenuList(data.data.menuList);
delete data.data.menuList;
this.setUserInfo(data.data);
}).catch(e => {});
}
},
watch: {
getMenuItem: {
handler (newValue) {
if (newValue == null) {
if (this.$route.name !== 'welcome') {
this.$router.replace({
name: 'welcome'
});
}
} else {
this.$router.replace({
name: newValue.formRouterName
});
}
},
immediate: true
}
}
}
</script>
<style lang="scss" scoped>
</style>