diff --git a/Web/src/assets/style/main.less b/Web/src/assets/style/main.less
index 3853d6f..7d7dbd6 100644
--- a/Web/src/assets/style/main.less
+++ b/Web/src/assets/style/main.less
@@ -320,6 +320,99 @@
}
}
}
+ .yo-layout-sider {
+ height: 100%;
+
+ background-color: @nav-background;
+ .ant-layout-sider-children {
+ display: flex;
+ flex-direction: column;
+ }
+ .logo {
+ font-size: @font-size-lg * 1.5;
+ font-weight: 500;
+ line-height: @layout-header-height + 10px;
+
+ z-index: 11;
+
+ display: flex;
+ overflow: hidden;
+ align-items: center;
+ flex: 0 0 @layout-header-height + 10px;
+
+ height: @layout-header-height + 10px;
+ padding: 0 @padding-md 0 @padding-lg;
+
+ color: @logo-color;
+ box-shadow: @logo-box-shadow;
+ img {
+ max-height: 100%;
+ }
+ span {
+ margin-left: @padding-sm;
+
+ transition: @animation-duration-slow;
+ transition-property: opacity;
+ }
+ }
+ &.ant-layout-sider-collapsed {
+ .logo {
+ span {
+ opacity: 0;
+ }
+ }
+ }
+ .yo-sider-nav {
+ position: relative;
+ z-index: 10;
+
+ flex: 1 1 100%;
+
+ box-shadow: 2px 0 8px @nav-box-shadow-color;
+ &--app {
+ font-size: @font-size-sm;
+
+ margin-top: @padding-sm;
+ padding: 0 @padding-md;
+
+ color: @nav-app-color;
+ }
+ }
+ .swiper-container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+
+ width: 100%;
+ .swiper-scrollbar {
+ transition: @animation-duration-slow;
+ transition-property: opacity;
+
+ opacity: 0;
+ }
+ .swiper-scrollbar-drag {
+ background-color: @nav-scrollbar-background;
+ }
+ &:hover {
+ .swiper-scrollbar {
+ opacity: 1;
+ }
+ }
+ }
+ .swiper-slide {
+ height: auto;
+ min-height: 100%;
+ >.ant-spin-nested-loading {
+ height: 100%;
+ .ant-spin-blur {
+ &::after {
+ opacity: 0;
+ }
+ }
+ }
+ }
+ }
.yo-layout--left-menu,
.yo-layout--right-menu {
position: absolute;
@@ -366,99 +459,11 @@
}
}
}
+ .layout-sider {
+ }
>section {
>.ant-layout-sider {
- height: 100%;
-
- background-color: @nav-background;
- .ant-layout-sider-children {
- display: flex;
- flex-direction: column;
- }
- .logo {
- font-size: @font-size-lg * 1.5;
- font-weight: 500;
- line-height: @layout-header-height + 10px;
-
- z-index: 11;
-
- display: flex;
- overflow: hidden;
- align-items: center;
- flex: 0 0 @layout-header-height + 10px;
-
- height: @layout-header-height + 10px;
- padding: 0 @padding-md 0 @padding-lg;
-
- color: @logo-color;
- box-shadow: @logo-box-shadow;
- img {
- max-height: 100%;
- }
- span {
- margin-left: @padding-sm;
-
- transition: @animation-duration-slow;
- transition-property: opacity;
- }
- }
- &.ant-layout-sider-collapsed {
- .logo {
- span {
- opacity: 0;
- }
- }
- }
- .yo-sider-nav {
- position: relative;
- z-index: 10;
-
- flex: 1 1 100%;
-
- box-shadow: 2px 0 8px @nav-box-shadow-color;
- &--app {
- font-size: @font-size-sm;
-
- margin-top: @padding-sm;
- padding: 0 @padding-md;
-
- color: @nav-app-color;
- }
- }
- .swiper-container {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
-
- width: 100%;
- .swiper-scrollbar {
- transition: @animation-duration-slow;
- transition-property: opacity;
-
- opacity: 0;
- }
- .swiper-scrollbar-drag {
- background-color: @nav-scrollbar-background;
- }
- &:hover {
- .swiper-scrollbar {
- opacity: 1;
- }
- }
- }
- .swiper-slide {
- height: auto;
- min-height: 100%;
- >.ant-spin-nested-loading {
- height: 100%;
- .ant-spin-blur {
- &::after {
- opacity: 0;
- }
- }
- }
- }
+ .yo-layout-sider();
}
}
}
@@ -473,9 +478,16 @@
width: 100%;
min-width: @container-width;
height: 100%;
+
+ @layout-header-height: 54px;
.ant-layout-header {
+ line-height: @layout-header-height;
+
+ z-index: 11;
+
flex: 0 0 @layout-header-height;
+ height: @layout-header-height;
padding: 0;
background-color: @nav-background;
@@ -515,19 +527,19 @@
}
}
.user-container {
- margin: 12px 0;
+ margin: (@layout-header-height - 40px) / 2 0;
}
.logo {
font-size: @font-size-lg * 1.5;
font-weight: 500;
- line-height: @layout-header-height;
+ line-height: @layout-header-height - 10px;
display: flex;
overflow: hidden;
align-items: center;
- height: @layout-header-height;
- margin-right: @padding-lg;
+ height: @layout-header-height 10px;
+ margin: 5px @padding-lg 5px 0;
color: @logo-color;
img {
diff --git a/Web/src/main.js b/Web/src/main.js
index ab56557..1e13ac4 100644
--- a/Web/src/main.js
+++ b/Web/src/main.js
@@ -100,6 +100,11 @@ import './assets/style/app.less'
import { SETTING_KEY } from './common/storage'
const settings = JSON.parse(window.localStorage.getItem(SETTING_KEY))
+Object.assign(settings, {
+ layout: 'top-nav',
+ container: 'container-fluid',
+ navTheme: 'dark'
+})
const app = new Vue({
data: {
diff --git a/Web/src/views/main-dynamic/_layout/content.vue b/Web/src/views/main-dynamic/_layout/content.vue
new file mode 100644
index 0000000..f049914
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/content.vue
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+ pane.closable && $emit('close', pane.key)">
+
+ {{ pane.title }}
+
+ {{`- ${pane.subTitle}`}}
+
+
+
+
+
+ 复制组件地址
+ dev
+
+
+
+ 重新加载
+
+ pane.closable && $emit('close', pane.key)"
+ key="1"
+ >关闭
+ 关闭其他标签页
+ 关闭右侧标签页
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Web/src/views/main-dynamic/_layout/header/index.vue b/Web/src/views/main-dynamic/_layout/header/index.vue
new file mode 100644
index 0000000..06fd340
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/header/index.vue
@@ -0,0 +1,100 @@
+
+
+
+
\ No newline at end of file
diff --git a/Web/src/views/main-dynamic/_layout/header/search.js b/Web/src/views/main-dynamic/_layout/header/search.js
new file mode 100644
index 0000000..ea452f0
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/header/search.js
@@ -0,0 +1,149 @@
+export default {
+ props: {
+ menus: {
+ type: Array,
+ require: true
+ }
+ },
+
+ data() {
+ return {
+ searchText: '',
+ searchResult: [],
+
+ timer: null
+ };
+ },
+
+ methods: {
+
+ renderSelect(menu) {
+ return menu.map((p) => {
+ return p.children ? this.renderSelectGroup(p) : this.renderSelectOption(p)
+ })
+ },
+
+ renderSelectGroup(menu) {
+ return (
+
+
+ {menu.parents}
+
+ {this.renderSelect(menu.children)}
+
+ )
+ },
+
+ renderSelectOption(menu) {
+ return (
+ {menu.meta.icon && }
+ {menu.meta.title}
+ {menu.component}
+ )
+ },
+
+
+ onSearch(value) {
+ clearTimeout(this.timer)
+
+ this.timer = setTimeout(() => {
+ this.doSearch(value)
+ }, 300)
+ },
+
+ doSearch(value) {
+ this.searchText = value
+
+ const menus = this.$_.concat.apply(this, this.$_.cloneDeep(this.menus.map(p => p.menu)))
+
+ const search = (m) => {
+ if (!value) return []
+ return m.filter((p) => {
+ if (p.children) {
+ p.children = search(p.children)
+ } else {
+ return p.meta.title.indexOf(value) > -1 || (p.component || '').toLowerCase().indexOf(value.toLowerCase()) > -1
+ }
+ return p.children.length
+ })
+ }
+
+ /**
+ * 拆分层级,只留下 [父级-父级-...] [子级]
+ * *******在更为复杂的目录下会出现父级联动错误的问题
+ */
+ const unzip = (m) => {
+ const getSeed = (parent, seed) => {
+ if (parent.children) {
+ seed.parents.push(parent.meta.title)
+ seed.children = parent.children
+ parent.children.forEach(p => {
+ getSeed(p, seed)
+ })
+ }
+ return seed
+ }
+ const result = []
+ m.forEach(p => {
+ const r = { parents: [], children: [] }
+ result.push(getSeed(p, r))
+ })
+ return result
+ }
+
+ const result = unzip(search(menus)).filter(p => p.parents.length).map(p => {
+ return {
+ parents: p.parents.join('-'),
+ children: p.children
+ }
+ })
+
+ this.searchResult = result
+ },
+
+ onSearchSelect(value, node) {
+ this.searchText = '';
+ this.onSearch(this.searchText);
+
+ const menu = JSON.parse(node.componentOptions.propsData.value)
+
+ this.openContentWindow({
+ key: menu.id,
+ title: menu.meta.title,
+ icon: menu.meta.icon,
+ path: menu.component,
+ });
+ },
+
+ },
+
+ render() {
+
+ const props = {
+ dropdownMatchSelectWidth: false,
+ dropdownStyle: { width: '300px' },
+ optionLabelProp: 'value',
+ placeholder: '请输入检索关键字',
+ value: this.searchText
+ }
+
+ const on = {
+ search: this.onSearch,
+ select: this.onSearchSelect
+ }
+
+ return (
+
+
+ {this.renderSelect(this.searchResult)}
+
+
+
+
+
+ )
+
+ }
+}
\ No newline at end of file
diff --git a/Web/src/views/main-dynamic/_layout/header/user.js b/Web/src/views/main-dynamic/_layout/header/user.js
new file mode 100644
index 0000000..fe89071
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/header/user.js
@@ -0,0 +1,91 @@
+let userOpenTimer, userCloseTimer
+
+let initDropdownHeight
+
+import { PERVIEW_URL } from '@/util/global';
+
+import { doLogout } from '@/common/login'
+
+export default {
+ data() {
+ return {
+ dropdownHeight: 0
+ }
+ },
+ mounted() {
+ initDropdownHeight = this.$refs.dropdown.scrollHeight
+ },
+ methods: {
+ onOpen(e) {
+ clearTimeout(userCloseTimer)
+ e.target.classList.add('open')
+ userOpenTimer = setTimeout(() => {
+ e.target.classList.add('drop')
+ this.dropdownHeight = initDropdownHeight
+ }, 300)
+ },
+
+ onClose(e) {
+ clearTimeout(userOpenTimer)
+ e.target.classList.remove('drop')
+ this.dropdownHeight = 0
+ userCloseTimer = setTimeout(() => {
+ e.target.classList.remove('open')
+ }, 300)
+ },
+
+ onAccountSetting() {
+ this.openContentWindow({
+ key: 'account-home',
+ title: '个人中心',
+ icon: 'user',
+ path: '/system/account'
+ })
+ },
+
+ onLogout() {
+ this.$confirm({
+ title: '提示',
+ content: '是否确定退出登录',
+ onOk: async () => {
+ await doLogout()
+ },
+ onCancel() {
+ }
+ })
+ }
+ },
+
+ render() {
+ return (
+
+
+
+ {
+ this.$root.global.info &&
+ }
+ {
+ this.$root.global.info &&
+ {this.$root.global.info.nickName || this.$root.global.info.name}
+ }
+
+
+
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/Web/src/views/main-dynamic/_layout/logo.vue b/Web/src/views/main-dynamic/_layout/logo.vue
new file mode 100644
index 0000000..309fc67
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/logo.vue
@@ -0,0 +1,10 @@
+
+
+
![]()
+
![]()
+
+
\ No newline at end of file
diff --git a/Web/src/views/main/_layout/nav/index.js b/Web/src/views/main-dynamic/_layout/nav/index.js
similarity index 100%
rename from Web/src/views/main/_layout/nav/index.js
rename to Web/src/views/main-dynamic/_layout/nav/index.js
diff --git a/Web/src/views/main-dynamic/_layout/sider/index.vue b/Web/src/views/main-dynamic/_layout/sider/index.vue
new file mode 100644
index 0000000..57b9069
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/sider/index.vue
@@ -0,0 +1,116 @@
+
+
+
+
\ No newline at end of file
diff --git a/Web/src/views/main-dynamic/_layout/sider/menu.js b/Web/src/views/main-dynamic/_layout/sider/menu.js
new file mode 100644
index 0000000..a4cccc5
--- /dev/null
+++ b/Web/src/views/main-dynamic/_layout/sider/menu.js
@@ -0,0 +1,92 @@
+import { HmacMD5 } from "crypto-js"
+
+export default {
+ props: {
+ nav: {
+ default() {
+ return {
+ content: []
+ }
+ },
+ type: Object,
+ },
+ menuStyle: {
+ default() {
+ return {}
+ },
+ type: Object || String,
+ },
+ mode: {
+ default: 'inline',
+ type: String,
+ },
+ },
+
+ methods: {
+ renderMenu(menu) {
+ return menu.map((p) => {
+ return p.children ? this.renderSubMenu(p) : this.renderMenuItem(p)
+ })
+ },
+
+ renderSubMenu(menu) {
+ return (
+
+
+ {menu.meta.icon && }
+ {menu.meta.title}
+
+ {this.renderMenu(menu.children)}
+
+ )
+ },
+
+ renderMenuItem(menu) {
+ return (
+ this.onOpenContentWindow(menu)}>
+ {menu.meta.icon && }
+ {menu.meta.title}
+
+ )
+ },
+
+ onMenuOpenChange() {
+ this.$emit('openChange')
+ },
+
+ onOpenContentWindow(menu) {
+ this.openContentWindow({
+ key: menu.id,
+ title: menu.meta.title,
+ icon: menu.meta.icon,
+ path: menu.component,
+ })
+ },
+ },
+
+ render() {
+ const props = {
+ mode: this.mode,
+ selectable: false,
+ style: this.menuStyle,
+ theme: this.$root.global.settings.navTheme,
+ }
+
+ const on = {
+ openChange: this.onMenuOpenChange,
+ }
+
+ return (
+ {
+ this.nav.content.map(item => {
+ return (
+
+ {item.app.name}
+ {this.renderMenu(item.menu)}
+
+ )
+ })
+ }
+ )
+ },
+}
\ No newline at end of file
diff --git a/Web/src/views/main-dynamic/index.vue b/Web/src/views/main-dynamic/index.vue
new file mode 100644
index 0000000..b841dde
--- /dev/null
+++ b/Web/src/views/main-dynamic/index.vue
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Web/src/views/main/setting.vue b/Web/src/views/main-dynamic/setting.vue
similarity index 100%
rename from Web/src/views/main/setting.vue
rename to Web/src/views/main-dynamic/setting.vue
diff --git a/Web/src/views/main/_layout/header/index.vue b/Web/src/views/main/_layout/header/index.vue
index 06fd340..62d708b 100644
--- a/Web/src/views/main/_layout/header/index.vue
+++ b/Web/src/views/main/_layout/header/index.vue
@@ -1,35 +1,11 @@
\ No newline at end of file
diff --git a/Web/src/views/main/_layout/sider/index.vue b/Web/src/views/main/_layout/sider/index.vue
index 57b9069..5eeede6 100644
--- a/Web/src/views/main/_layout/sider/index.vue
+++ b/Web/src/views/main/_layout/sider/index.vue
@@ -1,32 +1,29 @@
-