wip(Layout): Layout developing

This commit is contained in:
陈凯龙 2022-01-13 17:26:06 +08:00
parent 66e8d0e41c
commit 2fe9543b84
27 changed files with 282 additions and 35 deletions

View File

@ -41,6 +41,7 @@ specifiers:
pretty-quick: ^3.1.3 pretty-quick: ^3.1.3
qs: ^6.10.3 qs: ^6.10.3
rimraf: ^3.0.2 rimraf: ^3.0.2
screenfull: ^6.0.0
stylelint: ^14.2.0 stylelint: ^14.2.0
stylelint-config-html: ^1.0.0 stylelint-config-html: ^1.0.0
stylelint-config-prettier: ^9.0.3 stylelint-config-prettier: ^9.0.3
@ -75,6 +76,7 @@ dependencies:
nprogress: registry.npmmirror.com/nprogress/0.2.0 nprogress: registry.npmmirror.com/nprogress/0.2.0
pinia: registry.npmmirror.com/pinia/2.0.9_typescript@4.5.4+vue@3.2.26 pinia: registry.npmmirror.com/pinia/2.0.9_typescript@4.5.4+vue@3.2.26
qs: registry.npmmirror.com/qs/6.10.3 qs: registry.npmmirror.com/qs/6.10.3
screenfull: registry.npmmirror.com/screenfull/6.0.0
vue: registry.npmmirror.com/vue/3.2.26 vue: registry.npmmirror.com/vue/3.2.26
vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26 vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26
vue-router: registry.npmmirror.com/vue-router/4.0.12_vue@3.2.26 vue-router: registry.npmmirror.com/vue-router/4.0.12_vue@3.2.26
@ -7064,8 +7066,8 @@ packages:
vue-i18n: vue-i18n:
optional: true optional: true
dependencies: dependencies:
'@intlify/message-compiler': registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.26 '@intlify/message-compiler': registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.27
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.26 '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.27
jsonc-eslint-parser: registry.npmmirror.com/jsonc-eslint-parser/1.4.1 jsonc-eslint-parser: registry.npmmirror.com/jsonc-eslint-parser/1.4.1
source-map: registry.nlark.com/source-map/0.6.1 source-map: registry.nlark.com/source-map/0.6.1
vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26 vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26
@ -7121,18 +7123,18 @@ packages:
source-map: registry.nlark.com/source-map/0.6.1 source-map: registry.nlark.com/source-map/0.6.1
dev: false dev: false
registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.26: registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.27:
resolution: resolution:
{ {
integrity: sha512-qtDgHCMqrXNTekKXGzm0Dm6r3+/X7/jFXP+E07hx+PJbPMv7DzK1iU8h5LlAMQ1/jr2UIRBgXvR5wh35OKoGrA==, integrity: sha512-T3mBTm0559VX6l+lh8p5gDJ9/IS1XbVXeeMNJ2zTzxrf4lXg8OuotNjaxG3ZsuauQ5OqqlArkMYryXGyZnHolA==,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.26.tgz tarball: https://registry.npmmirror.com/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.27.tgz
} }
name: '@intlify/message-compiler' name: '@intlify/message-compiler'
version: 9.2.0-beta.26 version: 9.2.0-beta.27
engines: { node: '>= 12' } engines: { node: '>= 12' }
dependencies: dependencies:
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.26 '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.27
source-map: registry.nlark.com/source-map/0.6.1 source-map: registry.nlark.com/source-map/0.6.1
dev: true dev: true
@ -7176,15 +7178,15 @@ packages:
engines: { node: '>= 10' } engines: { node: '>= 10' }
dev: false dev: false
registry.npmmirror.com/@intlify/shared/9.2.0-beta.26: registry.npmmirror.com/@intlify/shared/9.2.0-beta.27:
resolution: resolution:
{ {
integrity: sha512-MjUlkjNThqkqy8yXUcFKBiW/hIfqAn5cP3Vd0b4wdOHS8rPCEbvSbAnF08uiZDkVv8gTcsLyymX21GaU6oYyyQ==, integrity: sha512-+Av77mIHy0qFkAq96mMAQGYcColMGN7e5+rUsyn3XxBw6oC3AGqYn/cQ6U/T3qOrzcHgcA+etAaLN3IxFqkJDw==,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/@intlify/shared/download/@intlify/shared-9.2.0-beta.26.tgz tarball: https://registry.npmmirror.com/@intlify/shared/download/@intlify/shared-9.2.0-beta.27.tgz
} }
name: '@intlify/shared' name: '@intlify/shared'
version: 9.2.0-beta.26 version: 9.2.0-beta.27
engines: { node: '>= 12' } engines: { node: '>= 12' }
dev: true dev: true
@ -7210,7 +7212,7 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@intlify/bundle-utils': registry.npmmirror.com/@intlify/bundle-utils/2.2.0_vue-i18n@9.1.9 '@intlify/bundle-utils': registry.npmmirror.com/@intlify/bundle-utils/2.2.0_vue-i18n@9.1.9
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.26 '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.27
'@rollup/pluginutils': registry.npmmirror.com/@rollup/pluginutils/4.1.2 '@rollup/pluginutils': registry.npmmirror.com/@rollup/pluginutils/4.1.2
debug: registry.npmmirror.com/debug/4.3.3 debug: registry.npmmirror.com/debug/4.3.3
fast-glob: registry.nlark.com/fast-glob/3.2.7 fast-glob: registry.nlark.com/fast-glob/3.2.7
@ -11607,6 +11609,18 @@ packages:
tslib: registry.npmmirror.com/tslib/2.3.1 tslib: registry.npmmirror.com/tslib/2.3.1
dev: true dev: true
registry.npmmirror.com/screenfull/6.0.0:
resolution:
{
integrity: sha512-LGY0nhNQkC4FX4DT4pZdJ5cZH5EOz9Gfh9KcVMl779pS677k4IV1Wv7sY/CwC9VKFT21fYgCh7zkTVVefi5XKA==,
registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/screenfull/download/screenfull-6.0.0.tgz
}
name: screenfull
version: 6.0.0
engines: { node: ^14.13.1 || >=16.0.0 }
dev: false
registry.npmmirror.com/shebang-regex/3.0.0: registry.npmmirror.com/shebang-regex/3.0.0:
resolution: resolution:
{ {

View File

@ -32,6 +32,7 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
overflow: hidden;
.size; .size;
#app { #app {

BIN
src/assets/imgs/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,3 @@
import Breadcrumb from './src/Breadcrumb.vue'
export { Breadcrumb }

View File

@ -0,0 +1,3 @@
import Collapse from './src/Collapse.vue'
export { Collapse }

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import { computed, unref } from 'vue'
import { Icon } from '@/components/Icon'
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
const collapse = computed(() => appStore.getCollapse)
function toggleCollapse() {
const collapsed = unref(collapse)
appStore.setCollapse(!collapsed)
}
</script>
<template>
<div>
<Icon
:size="18"
:icon="collapse ? 'ant-design:menu-unfold-outlined' : 'ant-design:menu-fold-outlined'"
class="cursor-pointer"
@click="toggleCollapse"
/>
</div>
</template>

View File

@ -25,6 +25,7 @@ function setLang(lang: LocaleType) {
<template> <template>
<ElDropdown trigger="click" @command="setLang"> <ElDropdown trigger="click" @command="setLang">
<Icon <Icon
:size="18"
icon="ion:language-sharp" icon="ion:language-sharp"
color="var(--el-text-color-primary)" color="var(--el-text-color-primary)"
class="cursor-pointer" class="cursor-pointer"

View File

@ -22,8 +22,8 @@ export default defineComponent({
const preFixCls = getPrefixCls('menu') const preFixCls = getPrefixCls('menu')
const menuMode = computed(() => { const menuMode = computed((): 'vertical' | 'horizontal' => {
// //
const vertical: LayoutType[] = ['classic'] const vertical: LayoutType[] = ['classic']
if (vertical.includes(appStore.getLayout)) { if (vertical.includes(appStore.getLayout)) {
@ -77,7 +77,7 @@ export default defineComponent({
> >
{{ {{
default: () => { default: () => {
const { renderMenuItem } = useRenderMenuItem(routers.value) const { renderMenuItem } = useRenderMenuItem(routers.value, menuMode.value)
return renderMenuItem() return renderMenuItem()
} }
}} }}
@ -93,7 +93,10 @@ export default defineComponent({
@prefix-cls: ~'@{namespace}-menu'; @prefix-cls: ~'@{namespace}-menu';
.@{prefix-cls} { .@{prefix-cls} {
transition: width var(--transition-time-02);
:deep(.el-menu) { :deep(.el-menu) {
width: 100% !important;
border-right: none; border-right: none;
// //
@ -132,8 +135,55 @@ export default defineComponent({
} }
} }
//
:deep(.el-menu--collapse) { :deep(.el-menu--collapse) {
width: var(--left-menu-min-width); width: var(--left-menu-min-width);
& > .is-active,
& > .is-active > .el-sub-menu__title {
background-color: var(--left-menu-collapse-bg-active-color) !important;
}
}
//
:deep(.horizontal-collapse-transition) {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out !important;
.@{prefix-cls}__title {
display: none;
}
}
}
</style>
<style lang="less">
@prefix-cls: ~'@{namespace}-menu-popper';
.@{prefix-cls} {
&--vertical {
//
.is-active {
& > .el-sub-menu__title {
color: var(--left-menu-text-active-color) !important;
}
}
//
.el-sub-menu__title,
.el-menu-item {
&:hover {
color: var(--left-menu-text-active-color) !important;
background-color: var(--left-menu-bg-color) !important;
}
}
//
.el-menu-item.is-active {
background-color: var(--left-menu-bg-active-color) !important;
&:hover {
background-color: var(--left-menu-bg-active-color) !important;
}
}
} }
} }
</style> </style>

View File

@ -3,8 +3,13 @@ import type { RouteMeta } from 'vue-router'
import { getAllParentPath, hasOneShowingChild } from '../helper' import { getAllParentPath, hasOneShowingChild } from '../helper'
import { isUrl } from '@/utils/is' import { isUrl } from '@/utils/is'
import { useRenderMenuTitle } from './useRenderMenuTitle' import { useRenderMenuTitle } from './useRenderMenuTitle'
import { useDesign } from '@/hooks/web/useDesign'
import { pathResolve } from '@/utils/routerHelper'
export function useRenderMenuItem(allRouters: AppRouteRecordRaw[] = []) { export function useRenderMenuItem(
allRouters: AppRouteRecordRaw[] = [],
menuMode: 'vertical' | 'horizontal'
) {
function renderMenuItem(routers?: AppRouteRecordRaw[]) { function renderMenuItem(routers?: AppRouteRecordRaw[]) {
return (routers || allRouters).map((v) => { return (routers || allRouters).map((v) => {
const meta = (v.meta ?? {}) as RouteMeta const meta = (v.meta ?? {}) as RouteMeta
@ -23,15 +28,23 @@ export function useRenderMenuItem(allRouters: AppRouteRecordRaw[] = []) {
!meta?.alwaysShow !meta?.alwaysShow
) { ) {
return ( return (
<ElMenuItem index={fullPath}> <ElMenuItem index={onlyOneChild ? pathResolve(fullPath, onlyOneChild.path) : fullPath}>
{{ {{
default: () => renderMenuTitle(meta) default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta)
}} }}
</ElMenuItem> </ElMenuItem>
) )
} else { } else {
const { getPrefixCls } = useDesign()
const preFixCls = getPrefixCls('menu-popper')
return ( return (
<ElSubMenu index={fullPath}> <ElSubMenu
index={fullPath}
popperClass={
menuMode === 'vertical' ? `${preFixCls}--vertical` : `${preFixCls}--horizontal`
}
>
{{ {{
title: () => renderMenuTitle(meta), title: () => renderMenuTitle(meta),
default: () => renderMenuItem(v.children) default: () => renderMenuItem(v.children)

View File

@ -10,10 +10,10 @@ export function useRenderMenuTitle() {
return icon ? ( return icon ? (
<> <>
<Icon icon={meta.icon}></Icon> <Icon icon={meta.icon}></Icon>
{t(title as string)} <span>{t(title as string)}</span>
</> </>
) : ( ) : (
t(title as string) <span>{t(title as string)}</span>
) )
} }

View File

@ -0,0 +1,3 @@
import Screenfull from './src/Screenfull.vue'
export { Screenfull }

View File

@ -0,0 +1,16 @@
<script setup lang="ts">
import { Icon } from '@/components/Icon'
import { useFullscreen } from '@vueuse/core'
const { toggle, isFullscreen } = useFullscreen()
function toggleFullscreen() {
toggle()
}
</script>
<template>
<div @click="toggleFullscreen">
<Icon :size="18" :icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'" />
</div>
</template>

View File

@ -16,7 +16,12 @@ function setSize(size: ElememtPlusSzie) {
<template> <template>
<ElDropdown trigger="click" @command="setSize"> <ElDropdown trigger="click" @command="setSize">
<Icon icon="mdi:format-size" color="var(--el-text-color-primary)" class="cursor-pointer" /> <Icon
:size="18"
icon="mdi:format-size"
color="var(--el-text-color-primary)"
class="cursor-pointer"
/>
<template #dropdown> <template #dropdown>
<ElDropdownMenu> <ElDropdownMenu>
<ElDropdownItem v-for="item in sizeMap" :key="item" :command="item"> <ElDropdownItem v-for="item in sizeMap" :key="item" :command="item">

View File

@ -0,0 +1,3 @@
import UserInfo from './src/UserInfo.vue'
export { UserInfo }

View File

@ -0,0 +1,46 @@
<script setup lang="ts">
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache'
import { resetRouter } from '@/router'
import { useRouter } from 'vue-router'
const { t } = useI18n()
const { wsCache } = useCache()
const { replace } = useRouter()
function loginOut() {
ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), {
confirmButtonText: t('common.ok'),
cancelButtonText: t('common.cancel'),
type: 'warning'
})
.then(() => {
wsCache.clear()
resetRouter() //
replace('/login')
})
.catch(() => {})
}
</script>
<template>
<ElDropdown trigger="click">
<div class="flex items-center">
<img src="@/assets/imgs/avatar.png" alt="" class="w-[calc(var(--tags-view-height)-10px)]" />
<span class="<lg:hidden text-14px pl-[5px] text-dark-50">Archer</span>
</div>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem>
<div>{{ t('common.document') }}</div>
</ElDropdownItem>
<ElDropdownItem divided>
<div @click="loginOut">{{ t('common.loginOut') }}</div>
</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</template>

View File

@ -4,6 +4,11 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { Menu } from '@/components/Menu' import { Menu } from '@/components/Menu'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { Collapse } from '@/components/Collapse'
import { LocaleDropdown } from '@/components/LocaleDropdown'
import { SizeDropdown } from '@/components/SizeDropdown'
import { UserInfo } from '@/components/UserInfo'
import { Screenfull } from '@/components/Screenfull'
// import { TagsView } from '@/components/TagsView' // import { TagsView } from '@/components/TagsView'
const tagsViewStore = useTagsViewStore() const tagsViewStore = useTagsViewStore()
@ -43,10 +48,18 @@ export default defineComponent({
<div <div
class={[ class={[
`${perFixCls}-right__tool`, `${perFixCls}-right__tool`,
'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center' 'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between'
]} ]}
> >
ssss <div class="h-full flex items-center">
<Collapse class="header__tigger"></Collapse>
</div>
<div class="h-full flex items-center">
<Screenfull class="header__tigger"></Screenfull>
<SizeDropdown class="header__tigger"></SizeDropdown>
<LocaleDropdown class="header__tigger"></LocaleDropdown>
<UserInfo class="header__tigger"></UserInfo>
</div>
</div> </div>
<router-view> <router-view>
{{ {{
@ -67,8 +80,23 @@ export default defineComponent({
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app'; @prefix-cls: ~'@{namespace}-app';
.header__tigger {
display: flex;
height: 100%;
padding: 1px 10px 0;
cursor: pointer;
align-items: center;
transition: background var(--transition-time-02);
&:hover {
background-color: #f6f6f6;
}
}
.@{prefix-cls} { .@{prefix-cls} {
&-right { &-right {
transition: left var(--transition-time-02);
&__tool { &__tool {
&::after { &::after {
position: absolute; position: absolute;

View File

@ -5,7 +5,13 @@ export default {
startTimeText: 'Start time', startTimeText: 'Start time',
endTimeText: 'End time', endTimeText: 'End time',
login: 'Login', login: 'Login',
required: 'This is required' required: 'This is required',
loginOut: 'Login out',
document: 'Document',
reminder: 'Reminder',
loginOutMessage: 'Exit the system',
ok: 'OK',
cancel: 'Cancel'
}, },
size: { size: {
default: 'Default', default: 'Default',

View File

@ -5,7 +5,13 @@ export default {
startTimeText: '开始时间', startTimeText: '开始时间',
endTimeText: '结束时间', endTimeText: '结束时间',
login: '登录', login: '登录',
required: '该项为必填项' required: '该项为必填项',
loginOut: '退出系统',
document: '项目文档',
reminder: '温馨提示',
loginOutMessage: '是否退出本系统?',
ok: '确定',
cancel: '取消'
}, },
size: { size: {
default: '默认', default: '默认',

View File

@ -94,6 +94,23 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
} }
} }
] ]
},
{
path: '/icon',
component: Layout,
name: 'IconsDemo',
meta: {},
children: [
{
path: 'index',
component: () => import('@/views/Level/Menu2.vue'),
name: 'Icons',
meta: {
title: '图标',
icon: 'carbon:skill-level-advanced'
}
}
]
} }
] ]

View File

@ -1,6 +1,7 @@
:root { :root {
--dark-bg-color: #293146; --dark-bg-color: #293146;
/* left menu start */
--left-menu-max-width: 200px; --left-menu-max-width: 200px;
--left-menu-min-width: 64px; --left-menu-min-width: 64px;
@ -15,11 +16,16 @@
--left-menu-text-active-color: #fff; --left-menu-text-active-color: #fff;
--left-menu-collapse-bg-active-color: var(--el-color-primary);
/* left menu end */
--top-tool-height: 40px; --top-tool-height: 40px;
--top-tool-p-x: 20px; --top-tool-p-x: 0;
--top-tool-border-color: #eee; --top-tool-border-color: #eee;
--tags-view-height: 40px; --tags-view-height: 40px;
--transition-time-02: 0.2s;
} }

View File

@ -149,7 +149,3 @@ function subtractLight(color: string, amount: number) {
const c = cc < 0 ? 0 : cc const c = cc < 0 ? 0 : cc
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}` return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
} }
export function setCssVar(prop: string, val: any, dom = document.documentElement) {
dom.style.setProperty(prop, val)
}

View File

@ -34,3 +34,7 @@ export function underlineToHump(str: string): string {
return letter.toUpperCase() return letter.toUpperCase()
}) })
} }
export function setCssVar(prop: string, val: any, dom = document.documentElement) {
dom.style.setProperty(prop, val)
}

View File

@ -122,7 +122,8 @@ export function generateRoutesFn2(routes: AppRouteRecordRaw[]): AppRouteRecordRa
} }
export function pathResolve(parentPath: string, path: string) { export function pathResolve(parentPath: string, path: string) {
return `${parentPath}/${path}` const childPath = path.startsWith('/') || !path ? path : `/${path}`
return `${parentPath}${childPath}`
} }
// 路由降级 // 路由降级

View File

@ -1,5 +1,5 @@
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<div>Menu111</div> <div>Menu111 <input type="text" /></div>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<div>Menu12</div> <div>Menu12 <input type="text" /></div>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<div>Menu2</div> <div>Menu2 <input type="text" /></div>
</template> </template>