提交 c774a6d3 编写于 作者: V vben

feat: support mobile layout adaptation

上级 683d1f52
......@@ -3,17 +3,21 @@
### ✨ Features
- 移除左侧菜单搜索,新增顶部菜单搜索功能
- layout 移动端适配。页面未适配
### ⚡ Performance Improvements
- 异步引入组件
- 优化整体结构
- 替换菜单默认滚动条为滚动组件
- 菜单性能优化
### 🎫 Chores
- 返回顶部样式调整,避免遮住其他元素
- 升级`ant-design-vue``2.0.0-rc.5`
- 刷新按钮布局调整
- `route.meta` 移除 `externalLink` 属性
### 🐛 Bug Fixes
......
<template>
<div :class="prefixCls" v-if="getShowSearch" @click="handleSearch">
<div :class="prefixCls" v-if="getShowSearch" @click.stop="handleSearch">
<Tooltip>
<template #title> {{ t('component.app.search') }} </template>
<SearchOutlined />
</Tooltip>
<transition name="zoom-fade" mode="out-in">
<AppSearchModal @close="handleClose" v-if="showModal" />
</transition>
<AppSearchModal @close="handleClose" :visible="showModal" />
</div>
</template>
<script lang="ts">
......
<template>
<div :class="prefixCls" @click.stop>
<ClickOutSide @clickOutside="handleClose">
<div :class="`${prefixCls}-content`">
<a-input
:class="`${prefixCls}-input`"
:placeholder="t('component.app.search')"
allow-clear
@change="handleSearch"
>
<template #prefix>
<SearchOutlined />
</template>
</a-input>
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
{{ t('component.app.searchNotData') }}
</div>
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
<li
:ref="setRefs(index)"
v-for="(item, index) in searchResult"
:key="item.path"
:data-index="index"
@mouseenter="handleMouseenter"
@click="handleEnter"
:class="[
`${prefixCls}-list__item`,
{
[`${prefixCls}-list__item--active`]: activeIndex === index,
},
]"
>
<div :class="`${prefixCls}-list__item-icon`">
<g-icon :icon="item.icon || 'mdi:form-select'" :size="20" />
<Teleport to="body">
<transition name="zoom-fade" mode="out-in">
<div :class="getClass" @click.stop v-if="visible">
<ClickOutSide @clickOutside="handleClose">
<div :class="`${prefixCls}-content`">
<div :class="`${prefixCls}-input__wrapper`">
<a-input
:class="`${prefixCls}-input`"
:placeholder="t('component.app.search')"
allow-clear
@change="handleSearch"
>
<template #prefix>
<SearchOutlined />
</template>
</a-input>
<span :class="`${prefixCls}-cancel`" @click="handleClose">{{
t('component.app.cancel')
}}</span>
</div>
<div :class="`${prefixCls}-list__item-text`">{{ item.name }}</div>
<div :class="`${prefixCls}-list__item-enter`">
<g-icon icon="ant-design:enter-outlined" :size="20" />
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
{{ t('component.app.searchNotData') }}
</div>
</li>
</ul>
<AppSearchFooter />
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
<li
:ref="setRefs(index)"
v-for="(item, index) in searchResult"
:key="item.path"
:data-index="index"
@mouseenter="handleMouseenter"
@click="handleEnter"
:class="[
`${prefixCls}-list__item`,
{
[`${prefixCls}-list__item--active`]: activeIndex === index,
},
]"
>
<div :class="`${prefixCls}-list__item-icon`">
<g-icon :icon="item.icon || 'mdi:form-select'" :size="20" />
</div>
<div :class="`${prefixCls}-list__item-text`">{{ item.name }}</div>
<div :class="`${prefixCls}-list__item-enter`">
<g-icon icon="ant-design:enter-outlined" :size="20" />
</div>
</li>
</ul>
<AppSearchFooter />
</div>
</ClickOutSide>
</div>
</ClickOutSide>
</div>
</transition>
</Teleport>
</template>
<script lang="ts">
import { defineComponent, computed, unref, ref } from 'vue';
......@@ -54,15 +64,20 @@
import AppSearchFooter from './AppSearchFooter.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { ClickOutSide } from '/@/components/ClickOutSide';
import { useAppInject } from '/@/hooks/web/useAppInject';
export default defineComponent({
name: 'AppSearchModal',
components: { SearchOutlined, ClickOutSide, AppSearchFooter },
emits: ['close'],
props: {
visible: Boolean,
},
setup(_, { emit }) {
const scrollWrap = ref<ElRef>(null);
const { prefixCls } = useDesign('app-search-modal');
const { t } = useI18n();
const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject();
const {
handleSearch,
......@@ -77,9 +92,19 @@
return !keyword || unref(searchResult).length === 0;
});
const getClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--mobile`]: unref(getIsMobile),
},
];
});
return {
t,
prefixCls,
getClass,
handleSearch,
searchResult,
activeIndex,
......@@ -98,12 +123,12 @@
<style lang="less" scoped>
@import (reference) '../../../../design/index.less';
@prefix-cls: ~'@{namespace}-app-search-modal';
@footer-prefix-cls: ~'@{namespace}-app-search-footer';
.@{prefix-cls} {
position: fixed;
top: 0;
left: 0;
z-index: 100;
z-index: 800;
display: flex;
width: 100%;
height: 100%;
......@@ -113,6 +138,43 @@
justify-content: center;
// backdrop-filter: blur(2px);
&--mobile {
padding: 0;
> div {
width: 100%;
}
.@{prefix-cls}-input {
width: calc(100% - 38px);
}
.@{prefix-cls}-cancel {
display: inline-block;
}
.@{prefix-cls}-content {
width: 100%;
height: 100%;
border-radius: 0;
}
.@{footer-prefix-cls} {
display: none;
}
.@{prefix-cls}-list {
height: calc(100% - 80px);
max-height: unset;
&__item {
&-enter {
opacity: 0 !important;
}
}
}
}
&-content {
position: relative;
width: 532px;
......@@ -124,10 +186,16 @@
flex-direction: column;
}
&-input__wrapper {
display: flex;
padding: 14px 14px 0 14px;
justify-content: space-between;
align-items: center;
}
&-input {
width: calc(100% - 28px);
width: 100%;
height: 56px;
margin: 14px 14px 0 14px;
font-size: 1.5em;
color: #1c1e21;
......@@ -136,6 +204,12 @@
}
}
&-cancel {
display: none;
font-size: 1em;
color: #666;
}
&-not-data {
display: flex;
width: 100%;
......
......@@ -151,7 +151,7 @@
if (props.mode !== MenuModeEnum.HORIZONTAL) {
setOpenKeys(path);
}
if (unref(getIsTopMenu)) {
if (props.isHorizontal && unref(getSplit)) {
const parentPath = await getCurrentParentPath(path);
menuState.selectedKeys = [parentPath];
} else {
......
......@@ -50,11 +50,7 @@ const getShowTopMenu = computed(() => {
});
const getShowHeaderTrigger = computed(() => {
if (
unref(getMenuType) === MenuTypeEnum.TOP_MENU ||
!unref(getShowMenu) ||
!unref(getMenuHidden)
) {
if (unref(getMenuType) === MenuTypeEnum.TOP_MENU || !unref(getShowMenu) || unref(getMenuHidden)) {
return false;
}
......@@ -79,7 +75,11 @@ const getMiniWidthNumber = computed(() => {
});
const getCalcContentWidth = computed(() => {
const width = unref(getIsTopMenu) || !unref(getShowMenu) ? 0 : unref(getRealWidth);
const width =
unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden))
? 0
: unref(getRealWidth);
return `calc(100% - ${unref(width)}px)`;
});
......
import './index.less';
import type { FunctionalComponent } from 'vue';
import type { Component } from '/@/components/types';
import { defineComponent, unref, computed } from 'vue';
import { Layout, Tooltip, Badge } from 'ant-design-vue';
import { AppLogo } from '/@/components/Application';
import LayoutMenu from '../menu';
import LockAction from './actions/LockAction';
import LayoutTrigger from '../trigger/index.vue';
import NoticeAction from './notice/NoticeActionItem.vue';
import { LockOutlined, BugOutlined } from '@ant-design/icons-vue';
import { AppSearch } from '/@/components/Application';
import { useModal } from '/@/components/Modal';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
import { useRouter } from 'vue-router';
import { errorStore } from '/@/store/modules/error';
import { PageEnum } from '/@/enums/pageEnum';
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { AppLocalePicker } from '/@/components/Application';
import { useI18n } from '/@/hooks/web/useI18n';
import { propTypes } from '/@/utils/propTypes';
import { UserDropDown, LayoutBreadcrumb, FullScreen } from './components';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useDesign } from '../../../hooks/web/useDesign';
interface TooltipItemProps {
title: string;
}
const TooltipItem: FunctionalComponent<TooltipItemProps> = (props, { slots }) => {
return (
<Tooltip>
{{
title: () => props.title,
default: () => slots.default?.(),
}}
</Tooltip>
);
};
export default defineComponent({
name: 'LayoutHeader',
props: {
fixed: propTypes.bool,
},
setup(props) {
const { t } = useI18n();
const { prefixCls } = useDesign('layout-header');
const { getShowTopMenu, getShowHeaderTrigger, getSplit } = useMenuSetting();
const { getShowLocale } = useLocaleSetting();
const { getUseErrorHandle } = useRootSetting();
const {
getHeaderTheme,
getUseLockPage,
getShowFullScreen,
getShowNotice,
getShowContent,
getShowBread,
getShowHeaderLogo,
} = useHeaderSetting();
const { push } = useRouter();
const [register, { openModal }] = useModal();
const { getIsMobile } = useAppInject();
const headerClass = computed(() => {
const theme = unref(getHeaderTheme);
return theme ? `${prefixCls}__header--${theme}` : '';
});
const getSplitType = computed(() => {
return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE;
});
const getMenuMode = computed(() => {
return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
});
function handleToErrorList() {
push(PageEnum.ERROR_LOG_PAGE).then(() => {
errorStore.commitErrorListCountState(0);
});
}
function handleLockPage() {
openModal(true);
}
function renderHeaderLeft() {
return (
<>
{unref(getShowContent) && (
<div class={`${prefixCls}__left`}>
{unref(getShowHeaderTrigger) && (
<LayoutTrigger theme={unref(getHeaderTheme)} sider={false} />
)}
{unref(getShowBread) && !unref(getIsMobile) && (
<LayoutBreadcrumb theme={unref(getHeaderTheme)} />
)}
</div>
)}
</>
);
}
function renderHeaderContent() {
return (
<div class={`${prefixCls}__content`}>
{unref(getShowTopMenu) && !unref(getIsMobile) && (
<div class={[`${prefixCls}__menu `]}>
{/* <div class={[`layout-header__menu `]}> */}
<LayoutMenu
isHorizontal={true}
// class={`justify-${unref(getTopMenuAlign)}`}
theme={unref(getHeaderTheme)}
splitType={unref(getSplitType)}
menuMode={unref(getMenuMode)}
/>
</div>
)}
</div>
);
}
function renderActionDefault(Comp: Component | any, event: Fn) {
return (
<div class={`${prefixCls}__action-item`} onClick={event}>
<Comp class={`${prefixCls}__action-icon`} />
</div>
);
}
function renderAction() {
return (
<div class={`${prefixCls}__action`}>
{!unref(getIsMobile) && <AppSearch class={`${prefixCls}__action-item`} />}
{unref(getUseErrorHandle) && !unref(getIsMobile) && (
<TooltipItem title={t('layout.header.tooltipErrorLog')}>
{() => (
<Badge
count={errorStore.getErrorListCountState}
offset={[0, 10]}
dot
overflowCount={99}
>
{() => renderActionDefault(BugOutlined, handleToErrorList)}
</Badge>
)}
</TooltipItem>
)}
{unref(getUseLockPage) && !unref(getIsMobile) && (
<TooltipItem title={t('layout.header.tooltipLock')}>
{() => renderActionDefault(LockOutlined, handleLockPage)}
</TooltipItem>
)}
{unref(getShowNotice) && !unref(getIsMobile) && (
<TooltipItem title={t('layout.header.tooltipNotify')}>
{() => <NoticeAction />}
</TooltipItem>
)}
{unref(getShowFullScreen) && !unref(getIsMobile) && <FullScreen />}
<UserDropDown theme={unref(getHeaderTheme)} />
{unref(getShowLocale) && (
<AppLocalePicker
reload={true}
showText={false}
class={`${prefixCls}__action-item locale`}
/>
)}
</div>
);
}
function renderHeaderDefault() {
return (
<>
{unref(getShowHeaderLogo) && (
<AppLogo class={`${prefixCls}__logo`} theme={unref(getHeaderTheme)} />
)}
{renderHeaderLeft()}
{renderHeaderContent()}
{renderAction()}
<LockAction onRegister={register} />
</>
);
}
return () => {
return (
<Layout.Header class={[prefixCls, unref(headerClass), { fixed: props.fixed }]}>
{() => renderHeaderDefault()}
</Layout.Header>
);
};
},
});
@import (reference) '../../../design/index.less';
.multiple-tab-header {
margin-left: 1px;
transition: width 0.2s;
flex: 0 0 auto;
&.dark {
margin-left: 0;
}
&.fixed {
position: fixed;
top: 0;
z-index: @multiple-tab-fixed-z-index;
width: 100%;
}
}
import './LayoutMultipleHeader.less';
import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue';
import LayoutHeader from './index.vue';
import MultipleTabs from '../tabs/index.vue';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useFullContent } from '/@/hooks/web/useFullContent';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
import { useLayoutContext } from '../useLayoutContext';
import { useAppInject } from '/@/hooks/web/useAppInject';
export default defineComponent({
name: 'LayoutMultipleHeader',
setup() {
const placeholderHeightRef = ref(0);
const fullHeaderHeightRef = ref(0);
const headerElRef = ref<ComponentRef>(null);
const tabElRef = ref<ComponentRef>(null);
const injectValue = useLayoutContext();
const { getCalcContentWidth, getSplit } = useMenuSetting();
const { getIsMobile } = useAppInject();
const {
getFixed,
getShowInsetHeaderRef,
getShowFullHeaderRef,
getShowHeader,
getUnFixedAndFull,
getHeaderTheme,
} = useHeaderSetting();
const { getFullContent } = useFullContent();
const { getShowMultipleTab } = useMultipleTabSetting();
const getShowTabs = computed(() => {
return unref(getShowMultipleTab) && !unref(getFullContent);
});
const getPlaceholderDomStyle = computed(
(): CSSProperties => {
return {
height: `${unref(placeholderHeightRef)}px`,
};
}
);
const getIsShowPlaceholderDom = computed(() => {
return unref(getFixed) || unref(getShowFullHeaderRef);
});
const getWrapStyle = computed(
(): CSSProperties => {
const style: CSSProperties = {};
if (unref(getFixed)) {
style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth);
}
if (unref(getShowFullHeaderRef)) {
style.top = `${unref(fullHeaderHeightRef)}px`;
}
return style;
}
);
const getIsFixed = computed(() => {
return unref(getFixed) || unref(getShowFullHeaderRef);
});
watch(
() => [
unref(getFixed),
unref(getShowFullHeaderRef),
unref(getShowHeader),
unref(getShowMultipleTab),
],
() => {
if (unref(getUnFixedAndFull)) return;
nextTick(() => {
const headerEl = unref(headerElRef)?.$el;
const tabEl = unref(tabElRef)?.$el;
const fullHeaderEl = unref(injectValue.fullHeader)?.$el;
let height = 0;
if (headerEl && !unref(getShowFullHeaderRef) && !unref(getSplit)) {
height += headerEl.offsetHeight;
}
if (tabEl) {
height += tabEl.offsetHeight;
}
if (fullHeaderEl && unref(getShowFullHeaderRef)) {
const fullHeaderHeight = fullHeaderEl.offsetHeight;
height += fullHeaderHeight;
fullHeaderHeightRef.value = fullHeaderHeight;
}
placeholderHeightRef.value = height;
});
},
{
immediate: true,
}
);
return () => {
return (
<>
{unref(getIsShowPlaceholderDom) && <div style={unref(getPlaceholderDomStyle)} />}
<div
style={unref(getWrapStyle)}
class={['multiple-tab-header', unref(getHeaderTheme), { fixed: unref(getIsFixed) }]}
>
{unref(getShowInsetHeaderRef) && <LayoutHeader ref={headerElRef} />}
{unref(getShowTabs) && <MultipleTabs ref={tabElRef} />}
</div>
</>
);
};
},
});
<template>
<div :style="getPlaceholderDomStyle" v-if="getIsShowPlaceholderDom" />
<div :style="getWrapStyle" :class="getClass">
<LayoutHeader v-if="getShowInsetHeaderRef" />
<MultipleTabs v-if="getShowTabs" />
</div>
</template>
<script lang="ts">
import { defineComponent, unref, computed, CSSProperties } from 'vue';
import LayoutHeader from './index.vue';
import MultipleTabs from '../tabs/index.vue';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useFullContent } from '/@/hooks/web/useFullContent';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useDesign } from '/@/hooks/web/useDesign';
const HEADER_HEIGHT = 48;
const TABS_HEIGHT = 32;
export default defineComponent({
name: 'LayoutMultipleHeader',
components: { LayoutHeader, MultipleTabs },
setup() {
const { prefixCls } = useDesign('layout-multiple-header');
const { getCalcContentWidth, getSplit } = useMenuSetting();
const { getIsMobile } = useAppInject();
const {
getFixed,
getShowInsetHeaderRef,
getShowFullHeaderRef,
getHeaderTheme,
} = useHeaderSetting();
const { getFullContent } = useFullContent();
const { getShowMultipleTab } = useMultipleTabSetting();
const getShowTabs = computed(() => {
return unref(getShowMultipleTab) && !unref(getFullContent);
});
const getIsShowPlaceholderDom = computed(() => {
return unref(getFixed) || unref(getShowFullHeaderRef);
});
const getWrapStyle = computed(
(): CSSProperties => {
const style: CSSProperties = {};
if (unref(getFixed)) {
style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth);
}
if (unref(getShowFullHeaderRef)) {
style.top = `${HEADER_HEIGHT}px`;
}
return style;
}
);
const getIsFixed = computed(() => {
return unref(getFixed) || unref(getShowFullHeaderRef);
});
const getPlaceholderDomStyle = computed(
(): CSSProperties => {
let height = 0;
if (unref(getShowFullHeaderRef) || !unref(getSplit)) {
height += HEADER_HEIGHT;
}
if (unref(getShowMultipleTab)) {
height += TABS_HEIGHT;
}
return {
height: `${height}px`,
};
}
);
const getClass = computed(() => {
return [
prefixCls,
`${prefixCls}--${unref(getHeaderTheme)}`,
{ [`${prefixCls}--fixed`]: unref(getIsFixed) },
];
});
return {
getClass,
prefixCls,
getPlaceholderDomStyle,
getIsFixed,
getWrapStyle,
getIsShowPlaceholderDom,
getShowTabs,
getShowInsetHeaderRef,
};
},
});
</script>
<style lang="less" scoped>
@import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-layout-multiple-header';
.@{prefix-cls} {
margin-left: 1px;
transition: width 0.2s;
flex: 0 0 auto;
&--dark {
margin-left: 0;
}
&--fixed {
position: fixed;
top: 0;
z-index: @multiple-tab-fixed-z-index;
width: 100%;
}
}
</style>
......@@ -2,6 +2,8 @@
@header-trigger-prefix-cls: ~'@{namespace}-layout-header-trigger';
@header-prefix-cls: ~'@{namespace}-layout-header';
@locale-prefix-cls: ~'@{namespace}-app-locale-picker';
@breadcrumb-prefix-cls: ~'@{namespace}-layout-breadcrumb';
@logo-prefix-cls: ~'@{namespace}-app-logo';
.@{header-prefix-cls} {
display: flex;
......@@ -14,6 +16,30 @@
align-items: center;
justify-content: space-between;
&--mobile {
.@{breadcrumb-prefix-cls},
.error-action,
.notify-item,
.fullscreen-item {
display: none;
}
.@{logo-prefix-cls} {
min-width: unset;
padding-right: 0;
&__title {
display: none;
}
}
.@{header-trigger-prefix-cls} {
padding: 0 4px 0 8px !important;
}
.@{header-prefix-cls}-action {
padding-right: 4px;
}
}
&--fixed {
position: fixed;
top: 0;
......@@ -78,7 +104,7 @@
&-action {
display: flex;
min-width: 200px;
min-width: 180px;
padding-right: 12px;
align-items: center;
......
......@@ -3,17 +3,17 @@
<!-- left start -->
<div :class="`${prefixCls}-left`">
<!-- logo -->
<AppLogo v-if="getShowHeaderLogo" :class="`${prefixCls}-logo`" :theme="getHeaderTheme" />
<LayoutTrigger
v-if="getShowContent && getShowHeaderTrigger"
<AppLogo
v-if="getShowHeaderLogo || getIsMobile"
:class="`${prefixCls}-logo`"
:theme="getHeaderTheme"
:sider="false"
/>
<LayoutBreadcrumb
v-if="getShowContent && getShowBread && !getIsMobile"
<LayoutTrigger
v-if="(getShowContent && getShowHeaderTrigger && !getSplit) || getIsMobile"
:theme="getHeaderTheme"
:sider="false"
/>
<LayoutBreadcrumb v-if="getShowContent && getShowBread" :theme="getHeaderTheme" />
</div>
<!-- left end -->
......@@ -30,15 +30,15 @@
<!-- action -->
<div :class="`${prefixCls}-action`">
<AppSearch v-if="!getIsMobile" :class="`${prefixCls}-action__item`" />
<AppSearch :class="`${prefixCls}-action__item `" />
<ErrorAction v-if="getUseErrorHandle && !getIsMobile" :class="`${prefixCls}-action__item`" />
<ErrorAction v-if="getUseErrorHandle" :class="`${prefixCls}-action__item error-action`" />
<LockItem v-if="getUseLockPage && !getIsMobile" :class="`${prefixCls}-action__item`" />
<LockItem v-if="getUseLockPage" :class="`${prefixCls}-action__item lock-item`" />
<Notify v-if="getShowNotice && !getIsMobile" :class="`${prefixCls}-action__item`" />
<Notify v-if="getShowNotice" :class="`${prefixCls}-action__item notify-item`" />
<FullScreen v-if="getShowFullScreen && !getIsMobile" :class="`${prefixCls}-action__item`" />
<FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" />
<UserDropDown :theme="getHeaderTheme" />
......@@ -123,7 +123,11 @@
const theme = unref(getHeaderTheme);
return [
prefixCls,
{ [`${prefixCls}--fixed`]: props.fixed, [`${prefixCls}--${theme}`]: theme },
{
[`${prefixCls}--fixed`]: props.fixed,
[`${prefixCls}--mobile`]: unref(getIsMobile),
[`${prefixCls}--${theme}`]: theme,
},
];
});
......@@ -145,6 +149,7 @@
getShowBread,
getShowContent,
getSplitType,
getSplit,
getMenuMode,
getShowTopMenu,
getShowLocale,
......
<template>
<Layout :class="prefixCls">
<LayoutFeatures />
<LayoutHeader fixed ref="headerRef" v-if="getShowFullHeaderRef" />
<LayoutHeader fixed v-if="getShowFullHeaderRef" />
<Layout>
<LayoutSideBar v-if="getShowSidebar" />
<LayoutSideBar v-if="getShowSidebar || getIsMobile" />
<Layout :class="`${prefixCls}__main`">
<LayoutMultipleHeader />
<LayoutContent />
......@@ -14,21 +14,21 @@
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { defineComponent } from 'vue';
import { Layout } from 'ant-design-vue';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import LayoutHeader from './header/index.vue';
import LayoutContent from './content/index.vue';
import LayoutSideBar from './sider';
import LayoutMultipleHeader from './header/LayoutMultipleHeader';
import LayoutSideBar from './sider/index.vue';
import LayoutMultipleHeader from './header/MultipleHeader.vue';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDesign } from '/@/hooks/web/useDesign';
import { createLayoutContext } from './useLayoutContext';
import { registerGlobComp } from '/@/components/registerGlobComp';
import { useAppInject } from '/@/hooks/web/useAppInject';
export default defineComponent({
name: 'DefaultLayout',
......@@ -47,11 +47,9 @@
// default layout It is loaded after login. So it won’t be packaged to the first screen
registerGlobComp();
const headerRef = ref<ComponentRef>(null);
const { prefixCls } = useDesign('default-layout');
createLayoutContext({ fullHeader: headerRef });
const { getIsMobile } = useAppInject();
const { getShowFullHeaderRef } = useHeaderSetting();
......@@ -60,8 +58,8 @@
return {
getShowFullHeaderRef,
getShowSidebar,
headerRef,
prefixCls,
getIsMobile,
};
},
});
......
@import (reference) '../../../design/index.less';
.layout-menu {
&__logo {
@prefix-cls: ~'@{namespace}-layout-menu';
@logo-prefix-cls: ~'@{namespace}-app-logo';
.@{prefix-cls} {
&-logo {
height: @header-height;
padding: 10px 4px 10px 10px;
......@@ -10,4 +13,12 @@
height: @logo-width;
}
}
&--mobile {
.@{logo-prefix-cls} {
&__title {
opacity: 1;
}
}
}
}
import './index.less';
import { PropType, toRef } from 'vue';
import type { PropType, CSSProperties } from 'vue';
import { computed, defineComponent, unref } from 'vue';
import { computed, defineComponent, unref, toRef } from 'vue';
import { BasicMenu } from '/@/components/Menu';
import { AppLogo } from '/@/components/Application';
......@@ -17,7 +17,8 @@ import { openWindow } from '/@/utils';
import { propTypes } from '/@/utils/propTypes';
import { isUrl } from '/@/utils/is';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { CSSProperties } from 'vue';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'LayoutMenu',
......@@ -50,9 +51,15 @@ export default defineComponent({
} = useMenuSetting();
const { getShowLogo } = useRootSetting();
const { prefixCls } = useDesign('layout-menu');
const { menusRef } = useSplitMenu(toRef(props, 'splitType'));
const getComputedMenuMode = computed(() => props.menuMode || unref(getMenuMode));
const { getIsMobile } = useAppInject();
const getComputedMenuMode = computed(() =>
unref(getIsMobile) ? MenuModeEnum.INLINE : props.menuMode || unref(getMenuMode)
);
const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme));
......@@ -69,6 +76,16 @@ export default defineComponent({
};
}
);
const getLogoClass = computed(() => {
return [
`${prefixCls}-logo`,
unref(getComputedMenuTheme),
{
[`${prefixCls}--mobile`]: unref(getIsMobile),
},
];
});
/**
* click menu
* @param menu
......@@ -91,12 +108,12 @@ export default defineComponent({
}
function renderHeader() {
if (!unref(getIsShowLogo)) return null;
if (!unref(getIsShowLogo) && !unref(getIsMobile)) return null;
return (
<AppLogo
showTitle={!unref(getCollapsed)}
class={[`layout-menu__logo`, unref(getComputedMenuTheme)]}
class={unref(getLogoClass)}
theme={unref(getComputedMenuTheme)}
/>
);
......@@ -128,7 +145,6 @@ export default defineComponent({
) : (
renderMenu()
)}
;
</>
);
};
......
......@@ -10,11 +10,13 @@ import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus';
import { permissionStore } from '/@/store/modules/permission';
import { useAppInject } from '/@/hooks/web/useAppInject';
export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
// Menu array
const menusRef = ref<Menu[]>([]);
const { currentRoute } = useRouter();
const { getIsMobile } = useAppInject();
const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
......@@ -36,7 +38,7 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
watch(
[() => unref(currentRoute).path, () => unref(splitType)],
async ([path]: [string, MenuSplitTyeEnum]) => {
if (unref(splitNotLeft)) return;
if (unref(splitNotLeft) || unref(getIsMobile)) return;
const parentPath = await getCurrentParentPath(path);
parentPath && throttleHandleSplitLeftMenu(parentPath);
......@@ -65,24 +67,24 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
// Handle left menu split
async function handleSplitLeftMenu(parentPath: string) {
if (unref(getSplitLeft)) return;
if (unref(getSplitLeft) || unref(getIsMobile)) return;
// spilt mode left
const children = await getChildrenMenus(parentPath);
if (!children) {
setMenuSetting({ hidden: false });
setMenuSetting({ hidden: true });
menusRef.value = [];
return;
}
setMenuSetting({ hidden: true });
setMenuSetting({ hidden: false });
menusRef.value = children;
}
// get menus
async function genMenus() {
// normal mode
if (unref(normalType)) {
if (unref(normalType) || unref(getIsMobile)) {
menusRef.value = await getMenus();
return;
}
......
<template>
<div :class="getClass" :style="getDragBarStyle" />
</template>
<script lang="ts">
import { defineComponent, computed, unref } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
export default defineComponent({
name: 'DargBar',
props: {
mobile: Boolean,
},
setup(props) {
const { getMiniWidthNumber, getCollapsed, getCanDrag } = useMenuSetting();
const { prefixCls } = useDesign('darg-bar');
const getDragBarStyle = computed(() => {
if (unref(getCollapsed)) {
return { left: `${unref(getMiniWidthNumber)}px` };
}
return {};
});
const getClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--hide`]: !unref(getCanDrag) || props.mobile,
},
];
});
return {
prefixCls,
getDragBarStyle,
getClass,
};
},
});
</script>
<style lang="less" scoped>
@import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-darg-bar';
.@{prefix-cls} {
position: absolute;
top: 0;
right: -2px;
z-index: @side-drag-z-index;
width: 2px;
height: 100%;
cursor: col-resize;
border-top: none;
border-bottom: none;
&--hide {
display: none;
}
&:hover {
background: @primary-color;
box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
}
}
</style>
......@@ -12,6 +12,7 @@ import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useDesign } from '/@/hooks/web/useDesign';
import DragBar from './DragBar.vue';
export default defineComponent({
name: 'LayoutSideBar',
setup() {
......@@ -31,11 +32,11 @@ export default defineComponent({
const { prefixCls } = useDesign('layout-sideBar');
const { getTriggerAttr, getTriggerSlot } = useTrigger();
const { getIsMobile } = useAppInject();
const { renderDragLine } = useDragLine(sideRef, dragBarRef);
const { getTriggerAttr, getTriggerSlot } = useTrigger(getIsMobile);
useDragLine(sideRef, dragBarRef);
const { getCollapsedWidth, onBreakpointChange, onCollapseChange } = useSiderEvent();
......@@ -48,7 +49,7 @@ export default defineComponent({
});
const showClassSideBarRef = computed(() => {
return unref(getSplit) ? unref(getMenuHidden) : true;
return unref(getSplit) ? !unref(getMenuHidden) : true;
});
const getSiderClass = computed(() => {
......@@ -57,7 +58,7 @@ export default defineComponent({
{
[`${prefixCls}--fixed`]: unref(getMenuFixed),
hidden: !unref(showClassSideBarRef),
[`${prefixCls}--mix`]: unref(getIsMixMode),
[`${prefixCls}--mix`]: unref(getIsMixMode) && !unref(getIsMobile),
},
];
});
......@@ -84,7 +85,7 @@ export default defineComponent({
menuMode={unref(getMode)}
splitType={unref(getSplitType)}
/>
{renderDragLine()}
<DragBar ref={dragBarRef} />
</>
);
}
......@@ -101,7 +102,7 @@ export default defineComponent({
collapsible
class={unref(getSiderClass)}
width={unref(getMenuWidth)}
collapsed={unref(getCollapsed)}
collapsed={unref(getIsMobile) ? false : unref(getCollapsed)}
collapsedWidth={unref(getCollapsedWidth)}
theme={unref(getMenuTheme)}
onCollapse={onCollapseChange}
......
......@@ -44,27 +44,6 @@
z-index: 10;
}
&__darg-bar {
position: absolute;
top: 0;
right: -2px;
z-index: @side-drag-z-index;
width: 2px;
height: 100%;
cursor: col-resize;
border-top: none;
border-bottom: none;
&.hide {
display: none;
}
&:hover {
background: @primary-color;
box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
}
}
& .ant-layout-sider-trigger {
height: 36px;
line-height: 36px;
......
<template>
<Drawer
v-if="getIsMobile"
placement="left"
:class="prefixCls"
:width="getMenuWidth"
:getContainer="null"
:visible="!getCollapsed"
@close="handleClose"
>
<Sider />
</Drawer>
<Sider v-else />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import Sider from './LayoutSider';
import { Drawer } from 'ant-design-vue';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'SiderWrapper',
components: { Sider, Drawer },
setup() {
const { prefixCls } = useDesign('layout-sider-wrapper');
const { getIsMobile } = useAppInject();
const { setMenuSetting, getCollapsed, getMenuWidth } = useMenuSetting();
function handleClose() {
setMenuSetting({
collapsed: true,
});
}
return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth };
},
});
</script>
<style lang="less">
@import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-layout-sider-wrapper';
.@{prefix-cls} {
.ant-drawer-body {
height: 100vh;
padding: 0;
}
.ant-drawer-header-no-title {
display: none;
}
}
</style>
......@@ -42,12 +42,17 @@ export function useSiderEvent() {
/**
* Handle related operations of menu folding
*/
export function useTrigger() {
const { getTrigger } = useMenuSetting();
export function useTrigger(getIsMobile: Ref<boolean>) {
const { getTrigger, getSplit } = useMenuSetting();
const showTrigger = computed(() => {
const trigger = unref(getTrigger);
return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
return (
trigger !== TriggerEnum.NONE &&
!unref(getIsMobile) &&
(trigger === TriggerEnum.FOOTER || unref(getSplit))
);
});
const getTriggerAttr = computed(() => {
......@@ -77,14 +82,7 @@ export function useTrigger() {
* @param dragBarRef
*/
export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
const { getMiniWidthNumber, getCollapsed, setMenuSetting, getCanDrag } = useMenuSetting();
const getDragBarStyle = computed(() => {
if (unref(getCollapsed)) {
return { left: `${unref(getMiniWidthNumber)}px` };
}
return {};
});
const { getMiniWidthNumber, getCollapsed, setMenuSetting } = useMenuSetting();
onMounted(() => {
nextTick(() => {
......@@ -93,16 +91,6 @@ export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
});
});
function renderDragLine() {
return (
<div
class={[`layout-sidebar__darg-bar`, { hide: !unref(getCanDrag) }]}
style={unref(getDragBarStyle)}
ref={dragBarRef}
/>
);
}
function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) {
document.onmousemove = function (innerE) {
let iT = (ele as any).left + (innerE.clientX - clientX);
......@@ -138,21 +126,22 @@ export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
}
function changeWrapWidth() {
const ele = unref(dragBarRef) as any;
const ele = unref(dragBarRef)?.$el;
if (!ele) {
return;
}
const side = unref(siderRef);
const wrap = (side || {}).$el;
ele &&
(ele.onmousedown = (e: any) => {
wrap.style.transition = 'unset';
const clientX = e?.clientX;
ele.left = ele.offsetLeft;
handleMouseMove(ele, wrap, clientX);
removeMouseup(ele);
ele.setCapture?.();
return false;
});
ele.onmousedown = (e: any) => {
wrap.style.transition = 'unset';
const clientX = e?.clientX;
ele.left = ele.offsetLeft;
handleMouseMove(ele, wrap, clientX);
removeMouseup(ele);
ele.setCapture?.();
return false;
};
}
return { renderDragLine };
return {};
}
import { InjectionKey, Ref } from 'vue';
import { createContext, useContext } from '/@/hooks/core/useContext';
export interface LayoutContextProps {
fullHeader: Ref<ComponentRef>;
}
const key: InjectionKey<LayoutContextProps> = Symbol();
export function createLayoutContext(context: LayoutContextProps) {
return createContext<LayoutContextProps>(context, key);
}
export function useLayoutContext() {
return useContext<LayoutContextProps>(key);
}
export default {
search: 'Search',
cancel: 'Cancel',
searchNotData: 'No search results yet',
toSearch: 'to search',
toNavigate: 'to navigate',
......
export default {
search: '搜索',
cancel: '取消',
searchNotData: '暂无搜索结果',
toSearch: '确认',
toNavigate: '切换',
......
......@@ -13,8 +13,9 @@ const key = Symbol();
let lastChangeTab: RouteLocationNormalized;
export function setLastChangeTab(lastChangeRoute: RouteLocationNormalized) {
mitt.emit(key, getRoute(lastChangeRoute));
lastChangeTab = getRoute(lastChangeRoute);
const r = getRoute(lastChangeRoute);
mitt.emit(key, r);
lastChangeTab = r;
}
export function listenerLastChangeTab(
......
......@@ -51,6 +51,5 @@ if (isDevMode()) {
if (isProdMode() && isUseMock()) {
setupProdMockServer();
}
// Used to share app instances in other modules
setApp(app);
import { AppRouteModule } from '/@/router/types.d';
import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types';
import { findPath, forEach, treeMap, treeToList } from '/@/utils/helper/treeHelper';
import { findPath, forEach, treeMap } from '/@/utils/helper/treeHelper';
import { cloneDeep } from 'lodash-es';
import { isUrl } from '/@/utils/is';
......@@ -10,10 +10,6 @@ export function getAllParentPath(treeData: any[], path: string) {
return (menuList || []).map((item) => item.path);
}
export function flatMenus(menus: Menu[]) {
return treeToList(menus);
}
// 拼接父级路径
function joinParentPath(list: any, node: any) {
let allPaths = getAllParentPath(list, node.path);
......
......@@ -5,6 +5,10 @@ import { getParentLayout, LAYOUT } from '/@/router/constant';
import dynamicImport from './dynamicImport';
import { cloneDeep } from 'lodash-es';
export type LayoutMapKey = 'LAYOUT';
const LayoutMap = new Map<LayoutMapKey, () => Promise<typeof import('*.vue')>>();
// 动态引入
function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
if (!routes) return;
......@@ -20,16 +24,14 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
});
}
function getLayoutComp(comp: string) {
return comp === 'LAYOUT' ? LAYOUT : '';
}
// Turn background objects into routing objects
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
LayoutMap.set('LAYOUT', LAYOUT);
routeList.forEach((route) => {
if (route.component) {
if ((route.component as string).toUpperCase() === 'LAYOUT') {
route.component = getLayoutComp(route.component);
route.component = LayoutMap.get(route.component);
} else {
route.children = [cloneDeep(route)];
route.component = LAYOUT;
......@@ -46,16 +48,6 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul
return (routeList as unknown) as T[];
}
export function getParams(data: any = {}) {
const { params = {} } = data;
let ret = '';
Object.keys(params).forEach((key) => {
const p = params[key];
ret += `/${p}`;
});
return ret;
}
// Return to the new routing structure, not affected by the original example
export function getRoute(route: RouteLocationNormalized): RouteLocationNormalized {
if (!route) return route;
......
import type { Menu, MenuModule } from '/@/router/types';
import type { RouteRecordNormalized } from 'vue-router';
import { appStore } from '/@/store/modules/app';
import { permissionStore } from '/@/store/modules/permission';
import { transformMenuModule, flatMenus, getAllParentPath } from '/@/router/helper/menuHelper';
import { transformMenuModule, getAllParentPath } from '/@/router/helper/menuHelper';
import { filter } from '/@/utils/helper/treeHelper';
import router from '/@/router';
import { PermissionModeEnum } from '/@/enums/appEnum';
......@@ -10,6 +11,8 @@ import { pathToRegexp } from 'path-to-regexp';
import modules from 'globby!/@/router/menus/modules/**/*.@(ts)';
const reg = /(((https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
const menuModules: MenuModule[] = [];
Object.keys(modules).forEach((key) => {
......@@ -38,18 +41,9 @@ const staticMenus: Menu[] = [];
async function getAsyncMenus() {
// 前端角色控制菜单 直接取菜单文件
if (!isBackMode()) {
return staticMenus;
}
return permissionStore.getBackMenuListState;
return !isBackMode() ? staticMenus : permissionStore.getBackMenuListState;
}
// 获取深层扁平化菜单
export const getFlatMenus = async (): Promise<Menu[]> => {
const menus = await getAsyncMenus();
return flatMenus(menus);
};
// 获取菜单 树级
export const getMenus = async (): Promise<Menu[]> => {
const menus = await getAsyncMenus();
......@@ -61,7 +55,7 @@ export const getMenus = async (): Promise<Menu[]> => {
export async function getCurrentParentPath(currentPath: string) {
const menus = await getAsyncMenus();
const allParentPath = await getAllParentPath(menus, currentPath);
return allParentPath[0];
return allParentPath?.[0];
}
// 获取1级菜单,删除children
......@@ -81,27 +75,24 @@ export async function getChildrenMenus(parentPath: string) {
return parent.children;
}
// 扁平化children
export async function getFlatChildrenMenus(children: Menu[]) {
return flatMenus(children);
}
// 通用过滤方法
function basicFilter(routes: RouteRecordNormalized[]) {
return (menu: Menu) => {
const matchRoute = routes.find((route) => {
if (route.meta.externalLink) {
const match = route.path.match(reg)?.[0];
if (match && match === menu.path) {
return true;
}
if (route.meta) {
if (route.meta.carryParam) {
return pathToRegexp(route.path).test(menu.path);
}
if (route.meta.ignoreAuth) return true;
if (route.meta?.carryParam) {
return pathToRegexp(route.path).test(menu.path);
}
const isSame = route.path === menu.path;
if (!isSame) return false;
if (route.meta?.ignoreAuth) return true;
return route.path === menu.path;
return isSame || pathToRegexp(route.path).test(menu.path);
});
if (!matchRoute) return false;
......
......@@ -38,7 +38,6 @@ const iframe: AppRouteModule = {
name: 'DocExternal',
component: IFrame,
meta: {
externalLink: true,
title: t('routes.demo.iframe.docExternal'),
},
},
......
......@@ -15,9 +15,6 @@ export interface RouteMeta {
// icon on tab
icon?: string;
// Jump address
frameSrc?: string;
// Outer link jump address
externalLink?: boolean;
// current page transition
transitionName?: string;
......
......@@ -89,7 +89,7 @@ const setting: ProjectConfig = {
// Whether to show no dom
show: true,
// Whether to show dom
hidden: true,
hidden: false,
// Menu width
menuWidth: 210,
// Menu mode
......
......@@ -50,7 +50,7 @@ export function isRegExp(val: unknown): val is RegExp {
return is(val, 'RegExp');
}
export function isArray(val: unknown): val is Array<any> {
export function isArray(val: any): val is Array<any> {
return val && Array.isArray(val);
}
......
......@@ -221,25 +221,35 @@
font-size: 23em;
}
@media (min-width: @screen-sm-max) and (max-width: @screen-md-max) {
font-size: 19em;
height: 50%;
font-size: 12em;
border-radius: 10px;
.meridiem {
font-size: 20px;
}
}
@media (min-width: @screen-xs-max) and (max-width: @screen-sm-max) {
font-size: 13em;
}
@media (max-width: @screen-xs) {
height: 50%;
font-size: 6em;
border-radius: 20px;
height: 30%;
font-size: 5em;
border-radius: 10px;
.meridiem {
font-size: 14px;
}
}
}
&__footer-date {
position: absolute;
bottom: 20px;
left: 50%;
width: 100%;
font-family: helvetica;
color: #bababa;
transform: translate(-50%, 0);
text-align: center;
.time {
font-size: 50px;
......
......@@ -1051,43 +1051,43 @@
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.272.tgz#27c7caee9764e0304161261ec08ffc2794944b66"
integrity sha512-FyiTc7UiXJ5cDfk09lv70sYOSi5uLyK+a0LnF1KgWmofkikL06p98ksNRN7stmHryOYarSy75xgi6MbgAwtltQ==
"@intlify/core@9.0.0-beta.12":
version "9.0.0-beta.12"
resolved "https://registry.npmjs.org/@intlify/core/-/core-9.0.0-beta.12.tgz#f7d2d09060b8e00ae37157e00a0daa1c86290802"
integrity sha512-0wdOS9d0ZEvGkbNIdaxEHQQOfAIuhv1Q8CSpNImThh8ZDD+5Sa38wTerHBO0/Rk0HfHUP/hjPqbxxRqITmSo1g==
dependencies:
"@intlify/message-compiler" "9.0.0-beta.12"
"@intlify/message-resolver" "9.0.0-beta.12"
"@intlify/runtime" "9.0.0-beta.12"
"@intlify/shared" "9.0.0-beta.12"
"@intlify/message-compiler@9.0.0-beta.12":
version "9.0.0-beta.12"
resolved "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.0.0-beta.12.tgz#836a49cfd057ecb2c536680cc01aa16693211891"
integrity sha512-EMzBDBIsFvWV9w0tRAHzn2BD1C7nkJkXYwDWinROmoL6C4jgKUgon+9Uxp7lV0H1E+7hUfhGj6zHdtJrwFhH+g==
dependencies:
"@intlify/message-resolver" "9.0.0-beta.12"
"@intlify/shared" "9.0.0-beta.12"
"@intlify/core-base@9.0.0-beta.13":
version "9.0.0-beta.13"
resolved "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.0.0-beta.13.tgz#fb6bc278209cb7bef44853a42160fedb0560c3f8"
integrity sha512-ukImWV+QvRmNZtCTLrSW391z46eMuBheCMPZh801nM3v0Dosfu2PtWO5/z8Q9Bsom4Q+PNQ5eBtOQj2yCAhVEA==
dependencies:
"@intlify/message-compiler" "9.0.0-beta.13"
"@intlify/message-resolver" "9.0.0-beta.13"
"@intlify/runtime" "9.0.0-beta.13"
"@intlify/shared" "9.0.0-beta.13"
"@intlify/message-compiler@9.0.0-beta.13":
version "9.0.0-beta.13"
resolved "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.0.0-beta.13.tgz#3b8ddcb2be3f80b28c6e4f6028c0b3ec4e709849"
integrity sha512-1z7716InFM8FdTAz64wqZvFuT4wL7WKF63v+vUEW4s9FLoL0U+xIccor9P5XHAvvG1gPMH/Zxd0deg/ULZ1Mcg==
dependencies:
"@intlify/message-resolver" "9.0.0-beta.13"
"@intlify/shared" "9.0.0-beta.13"
source-map "0.6.1"
"@intlify/message-resolver@9.0.0-beta.12":
version "9.0.0-beta.12"
resolved "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.0.0-beta.12.tgz#98cf346f5da0fdf3408ba132c24841295a4e02db"
integrity sha512-i8bmWzhiBH59YED3SXqvdUfwecl7OUPOU/8yvfdhg2rXuZ4e2chCPnLpPafXz6bi88HcRsWF4aRGlpwDVDYadg==
"@intlify/message-resolver@9.0.0-beta.13":
version "9.0.0-beta.13"
resolved "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.0.0-beta.13.tgz#ae6de0bf0e54093160442d465e719bf03fd0f146"
integrity sha512-mR1eSpRtB4jh11TpQTUyzjEwqZ6D30mJYREEfSrl5YKfUKwDQrulrOaIO8T5gVQG2m09vfxJHVrgfJ2hR8z/0Q==
"@intlify/runtime@9.0.0-beta.12":
version "9.0.0-beta.12"
resolved "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.0.0-beta.12.tgz#647a62a326d92690569798ef046d29e8daa25c96"
integrity sha512-4ucZHqk/VGhrQEgu9xU5tE/sJTNfqKBhQtaXyEgYHchL9PvLoS1HFwPjABHvWjo3aVcv4d2cGtUPBwH4oLROKA==
"@intlify/runtime@9.0.0-beta.13":
version "9.0.0-beta.13"
resolved "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.0.0-beta.13.tgz#8deff103ee6982c6d531314e9f965b90768d8a27"
integrity sha512-hcb3sg75SokuzNDG8IC6PJmwjsS/xdgevd99UNG1zKb7s5qFFb90ApvPDpiH0+R9TMQe11fZqg5dyrVBKqAV4A==
dependencies:
"@intlify/message-compiler" "9.0.0-beta.12"
"@intlify/message-resolver" "9.0.0-beta.12"
"@intlify/shared" "9.0.0-beta.12"
"@intlify/message-compiler" "9.0.0-beta.13"
"@intlify/message-resolver" "9.0.0-beta.13"
"@intlify/shared" "9.0.0-beta.13"
"@intlify/shared@9.0.0-beta.12":
version "9.0.0-beta.12"
resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.0.0-beta.12.tgz#e939575bc4047411b9fc65347779f5b3173c1130"
integrity sha512-XtHAzQ2KBcdN0Khc7ZDCo5GnKQK4Vv0GKD1BplCWntpA2d5XqjdDpFuKumvbiOjPvYtuCFnksJU0OgJiCWG+KQ==
"@intlify/shared@9.0.0-beta.13":
version "9.0.0-beta.13"
resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.0.0-beta.13.tgz#2d93d695f19fd699ea8b336066f9d6dfc185f094"
integrity sha512-/rqC3YEGHs3uu3XSsF1zdBKJb+on34Yn8Z58K3YxJsFxKPHa8mH73EUtN79hTZWh6Js4zEa/WsCgZCM62b8eJA==
"@koa/cors@^3.1.0":
version "3.1.0"
......@@ -8256,16 +8256,13 @@ vue-eslint-parser@^7.3.0:
esquery "^1.0.1"
lodash "^4.17.15"
vue-i18n@^9.0.0-beta.12:
version "9.0.0-beta.12"
resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.12.tgz#f6e2fc1cc366b8f16aa4754642931e937ebde303"
integrity sha512-hDnr+GsIGCIKRtZsdDczkhqyzbpLuPgEkH5bQyMzrKTLelXipLvIVmUCAsSjyR7xMHDCwP6AwVTIZwk6ENXkwg==
vue-i18n@^9.0.0-beta.13:
version "9.0.0-beta.13"
resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.13.tgz#89cf5dd1566025f441132231d15ed621ef70ba96"
integrity sha512-ZN6r5ITODu9NYAAbe1IGVUkNeamuleaXTLn5NMn/YZQ+5NSjDjysyVZVLkVOEOIw6bT2tLveyjsWlAZBVtfcPw==
dependencies:
"@intlify/core" "9.0.0-beta.12"
"@intlify/message-compiler" "9.0.0-beta.12"
"@intlify/message-resolver" "9.0.0-beta.12"
"@intlify/runtime" "9.0.0-beta.12"
"@intlify/shared" "9.0.0-beta.12"
"@intlify/core-base" "9.0.0-beta.13"
"@intlify/shared" "9.0.0-beta.13"
"@vue/devtools-api" "^6.0.0-beta.2"
vue-router@^4.0.1:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册