Commit c774a6d3a03d9507a9023d600aa9dd9592f52fb3

Authored by vben
1 parent 683d1f52

feat: support mobile layout adaptation

CHANGELOG.zh_CN.md
... ... @@ -3,17 +3,21 @@
3 3 ### ✨ Features
4 4  
5 5 - 移除左侧菜单搜索,新增顶部菜单搜索功能
  6 +- layout 移动端适配。页面未适配
6 7  
7 8 ### ⚡ Performance Improvements
8 9  
9 10 - 异步引入组件
10 11 - 优化整体结构
  12 +- 替换菜单默认滚动条为滚动组件
  13 +- 菜单性能优化
11 14  
12 15 ### 🎫 Chores
13 16  
14 17 - 返回顶部样式调整,避免遮住其他元素
15 18 - 升级`ant-design-vue`到`2.0.0-rc.5`
16 19 - 刷新按钮布局调整
  20 +- `route.meta` 移除 `externalLink` 属性
17 21  
18 22 ### 🐛 Bug Fixes
19 23  
... ...
package.json
... ... @@ -37,7 +37,7 @@
37 37 "sortablejs": "^1.12.0",
38 38 "vditor": "^3.7.2",
39 39 "vue": "^3.0.4",
40   - "vue-i18n": "^9.0.0-beta.12",
  40 + "vue-i18n": "^9.0.0-beta.13",
41 41 "vue-router": "^4.0.1",
42 42 "vue-types": "^3.0.1",
43 43 "vuex": "^4.0.0-rc.2",
... ...
src/components/Application/src/search/AppSearch.vue
1 1 <template>
2   - <div :class="prefixCls" v-if="getShowSearch" @click="handleSearch">
  2 + <div :class="prefixCls" v-if="getShowSearch" @click.stop="handleSearch">
3 3 <Tooltip>
4 4 <template #title> {{ t('component.app.search') }} </template>
5 5 <SearchOutlined />
6 6 </Tooltip>
7 7  
8   - <transition name="zoom-fade" mode="out-in">
9   - <AppSearchModal @close="handleClose" v-if="showModal" />
10   - </transition>
  8 + <AppSearchModal @close="handleClose" :visible="showModal" />
11 9 </div>
12 10 </template>
13 11 <script lang="ts">
... ...
src/components/Application/src/search/AppSearchModal.vue
1 1 <template>
2   - <div :class="prefixCls" @click.stop>
3   - <ClickOutSide @clickOutside="handleClose">
4   - <div :class="`${prefixCls}-content`">
5   - <a-input
6   - :class="`${prefixCls}-input`"
7   - :placeholder="t('component.app.search')"
8   - allow-clear
9   - @change="handleSearch"
10   - >
11   - <template #prefix>
12   - <SearchOutlined />
13   - </template>
14   - </a-input>
15   - <div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
16   - {{ t('component.app.searchNotData') }}
17   - </div>
18   - <ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
19   - <li
20   - :ref="setRefs(index)"
21   - v-for="(item, index) in searchResult"
22   - :key="item.path"
23   - :data-index="index"
24   - @mouseenter="handleMouseenter"
25   - @click="handleEnter"
26   - :class="[
27   - `${prefixCls}-list__item`,
28   - {
29   - [`${prefixCls}-list__item--active`]: activeIndex === index,
30   - },
31   - ]"
32   - >
33   - <div :class="`${prefixCls}-list__item-icon`">
34   - <g-icon :icon="item.icon || 'mdi:form-select'" :size="20" />
  2 + <Teleport to="body">
  3 + <transition name="zoom-fade" mode="out-in">
  4 + <div :class="getClass" @click.stop v-if="visible">
  5 + <ClickOutSide @clickOutside="handleClose">
  6 + <div :class="`${prefixCls}-content`">
  7 + <div :class="`${prefixCls}-input__wrapper`">
  8 + <a-input
  9 + :class="`${prefixCls}-input`"
  10 + :placeholder="t('component.app.search')"
  11 + allow-clear
  12 + @change="handleSearch"
  13 + >
  14 + <template #prefix>
  15 + <SearchOutlined />
  16 + </template>
  17 + </a-input>
  18 + <span :class="`${prefixCls}-cancel`" @click="handleClose">{{
  19 + t('component.app.cancel')
  20 + }}</span>
35 21 </div>
36   - <div :class="`${prefixCls}-list__item-text`">{{ item.name }}</div>
37   - <div :class="`${prefixCls}-list__item-enter`">
38   - <g-icon icon="ant-design:enter-outlined" :size="20" />
  22 +
  23 + <div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
  24 + {{ t('component.app.searchNotData') }}
39 25 </div>
40   - </li>
41   - </ul>
42   - <AppSearchFooter />
  26 + <ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
  27 + <li
  28 + :ref="setRefs(index)"
  29 + v-for="(item, index) in searchResult"
  30 + :key="item.path"
  31 + :data-index="index"
  32 + @mouseenter="handleMouseenter"
  33 + @click="handleEnter"
  34 + :class="[
  35 + `${prefixCls}-list__item`,
  36 + {
  37 + [`${prefixCls}-list__item--active`]: activeIndex === index,
  38 + },
  39 + ]"
  40 + >
  41 + <div :class="`${prefixCls}-list__item-icon`">
  42 + <g-icon :icon="item.icon || 'mdi:form-select'" :size="20" />
  43 + </div>
  44 + <div :class="`${prefixCls}-list__item-text`">{{ item.name }}</div>
  45 + <div :class="`${prefixCls}-list__item-enter`">
  46 + <g-icon icon="ant-design:enter-outlined" :size="20" />
  47 + </div>
  48 + </li>
  49 + </ul>
  50 + <AppSearchFooter />
  51 + </div>
  52 + </ClickOutSide>
43 53 </div>
44   - </ClickOutSide>
45   - </div>
  54 + </transition>
  55 + </Teleport>
46 56 </template>
47 57 <script lang="ts">
48 58 import { defineComponent, computed, unref, ref } from 'vue';
... ... @@ -54,15 +64,20 @@
54 64 import AppSearchFooter from './AppSearchFooter.vue';
55 65 import { useI18n } from '/@/hooks/web/useI18n';
56 66 import { ClickOutSide } from '/@/components/ClickOutSide';
  67 + import { useAppInject } from '/@/hooks/web/useAppInject';
57 68 export default defineComponent({
58 69 name: 'AppSearchModal',
59 70 components: { SearchOutlined, ClickOutSide, AppSearchFooter },
60 71 emits: ['close'],
  72 + props: {
  73 + visible: Boolean,
  74 + },
61 75 setup(_, { emit }) {
62 76 const scrollWrap = ref<ElRef>(null);
63 77 const { prefixCls } = useDesign('app-search-modal');
64 78 const { t } = useI18n();
65 79 const [refs, setRefs] = useRefs();
  80 + const { getIsMobile } = useAppInject();
66 81  
67 82 const {
68 83 handleSearch,
... ... @@ -77,9 +92,19 @@
77 92 return !keyword || unref(searchResult).length === 0;
78 93 });
79 94  
  95 + const getClass = computed(() => {
  96 + return [
  97 + prefixCls,
  98 + {
  99 + [`${prefixCls}--mobile`]: unref(getIsMobile),
  100 + },
  101 + ];
  102 + });
  103 +
80 104 return {
81 105 t,
82 106 prefixCls,
  107 + getClass,
83 108 handleSearch,
84 109 searchResult,
85 110 activeIndex,
... ... @@ -98,12 +123,12 @@
98 123 <style lang="less" scoped>
99 124 @import (reference) '../../../../design/index.less';
100 125 @prefix-cls: ~'@{namespace}-app-search-modal';
101   -
  126 + @footer-prefix-cls: ~'@{namespace}-app-search-footer';
102 127 .@{prefix-cls} {
103 128 position: fixed;
104 129 top: 0;
105 130 left: 0;
106   - z-index: 100;
  131 + z-index: 800;
107 132 display: flex;
108 133 width: 100%;
109 134 height: 100%;
... ... @@ -113,6 +138,43 @@
113 138 justify-content: center;
114 139 // backdrop-filter: blur(2px);
115 140  
  141 + &--mobile {
  142 + padding: 0;
  143 +
  144 + > div {
  145 + width: 100%;
  146 + }
  147 +
  148 + .@{prefix-cls}-input {
  149 + width: calc(100% - 38px);
  150 + }
  151 +
  152 + .@{prefix-cls}-cancel {
  153 + display: inline-block;
  154 + }
  155 +
  156 + .@{prefix-cls}-content {
  157 + width: 100%;
  158 + height: 100%;
  159 + border-radius: 0;
  160 + }
  161 +
  162 + .@{footer-prefix-cls} {
  163 + display: none;
  164 + }
  165 +
  166 + .@{prefix-cls}-list {
  167 + height: calc(100% - 80px);
  168 + max-height: unset;
  169 +
  170 + &__item {
  171 + &-enter {
  172 + opacity: 0 !important;
  173 + }
  174 + }
  175 + }
  176 + }
  177 +
116 178 &-content {
117 179 position: relative;
118 180 width: 532px;
... ... @@ -124,10 +186,16 @@
124 186 flex-direction: column;
125 187 }
126 188  
  189 + &-input__wrapper {
  190 + display: flex;
  191 + padding: 14px 14px 0 14px;
  192 + justify-content: space-between;
  193 + align-items: center;
  194 + }
  195 +
127 196 &-input {
128   - width: calc(100% - 28px);
  197 + width: 100%;
129 198 height: 56px;
130   - margin: 14px 14px 0 14px;
131 199 font-size: 1.5em;
132 200 color: #1c1e21;
133 201  
... ... @@ -136,6 +204,12 @@
136 204 }
137 205 }
138 206  
  207 + &-cancel {
  208 + display: none;
  209 + font-size: 1em;
  210 + color: #666;
  211 + }
  212 +
139 213 &-not-data {
140 214 display: flex;
141 215 width: 100%;
... ...
src/components/Menu/src/BasicMenu.vue
... ... @@ -151,7 +151,7 @@
151 151 if (props.mode !== MenuModeEnum.HORIZONTAL) {
152 152 setOpenKeys(path);
153 153 }
154   - if (unref(getIsTopMenu)) {
  154 + if (props.isHorizontal && unref(getSplit)) {
155 155 const parentPath = await getCurrentParentPath(path);
156 156 menuState.selectedKeys = [parentPath];
157 157 } else {
... ...
src/hooks/setting/useMenuSetting.ts
... ... @@ -50,11 +50,7 @@ const getShowTopMenu = computed(() =&gt; {
50 50 });
51 51  
52 52 const getShowHeaderTrigger = computed(() => {
53   - if (
54   - unref(getMenuType) === MenuTypeEnum.TOP_MENU ||
55   - !unref(getShowMenu) ||
56   - !unref(getMenuHidden)
57   - ) {
  53 + if (unref(getMenuType) === MenuTypeEnum.TOP_MENU || !unref(getShowMenu) || unref(getMenuHidden)) {
58 54 return false;
59 55 }
60 56  
... ... @@ -79,7 +75,11 @@ const getMiniWidthNumber = computed(() =&gt; {
79 75 });
80 76  
81 77 const getCalcContentWidth = computed(() => {
82   - const width = unref(getIsTopMenu) || !unref(getShowMenu) ? 0 : unref(getRealWidth);
  78 + const width =
  79 + unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden))
  80 + ? 0
  81 + : unref(getRealWidth);
  82 +
83 83 return `calc(100% - ${unref(width)}px)`;
84 84 });
85 85  
... ...
src/layouts/default/header/LayoutHeader.tsx deleted 100644 → 0
1   -import './index.less';
2   -
3   -import type { FunctionalComponent } from 'vue';
4   -import type { Component } from '/@/components/types';
5   -
6   -import { defineComponent, unref, computed } from 'vue';
7   -
8   -import { Layout, Tooltip, Badge } from 'ant-design-vue';
9   -import { AppLogo } from '/@/components/Application';
10   -import LayoutMenu from '../menu';
11   -import LockAction from './actions/LockAction';
12   -import LayoutTrigger from '../trigger/index.vue';
13   -import NoticeAction from './notice/NoticeActionItem.vue';
14   -import { LockOutlined, BugOutlined } from '@ant-design/icons-vue';
15   -
16   -import { AppSearch } from '/@/components/Application';
17   -import { useModal } from '/@/components/Modal';
18   -
19   -import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
20   -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
21   -import { useRootSetting } from '/@/hooks/setting/useRootSetting';
22   -import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
23   -
24   -import { useRouter } from 'vue-router';
25   -
26   -import { errorStore } from '/@/store/modules/error';
27   -
28   -import { PageEnum } from '/@/enums/pageEnum';
29   -import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
30   -import { AppLocalePicker } from '/@/components/Application';
31   -import { useI18n } from '/@/hooks/web/useI18n';
32   -import { propTypes } from '/@/utils/propTypes';
33   -
34   -import { UserDropDown, LayoutBreadcrumb, FullScreen } from './components';
35   -import { useAppInject } from '/@/hooks/web/useAppInject';
36   -import { useDesign } from '../../../hooks/web/useDesign';
37   -interface TooltipItemProps {
38   - title: string;
39   -}
40   -
41   -const TooltipItem: FunctionalComponent<TooltipItemProps> = (props, { slots }) => {
42   - return (
43   - <Tooltip>
44   - {{
45   - title: () => props.title,
46   - default: () => slots.default?.(),
47   - }}
48   - </Tooltip>
49   - );
50   -};
51   -
52   -export default defineComponent({
53   - name: 'LayoutHeader',
54   - props: {
55   - fixed: propTypes.bool,
56   - },
57   - setup(props) {
58   - const { t } = useI18n();
59   - const { prefixCls } = useDesign('layout-header');
60   - const { getShowTopMenu, getShowHeaderTrigger, getSplit } = useMenuSetting();
61   - const { getShowLocale } = useLocaleSetting();
62   - const { getUseErrorHandle } = useRootSetting();
63   -
64   - const {
65   - getHeaderTheme,
66   - getUseLockPage,
67   - getShowFullScreen,
68   - getShowNotice,
69   - getShowContent,
70   - getShowBread,
71   - getShowHeaderLogo,
72   - } = useHeaderSetting();
73   -
74   - const { push } = useRouter();
75   - const [register, { openModal }] = useModal();
76   - const { getIsMobile } = useAppInject();
77   -
78   - const headerClass = computed(() => {
79   - const theme = unref(getHeaderTheme);
80   - return theme ? `${prefixCls}__header--${theme}` : '';
81   - });
82   -
83   - const getSplitType = computed(() => {
84   - return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE;
85   - });
86   -
87   - const getMenuMode = computed(() => {
88   - return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
89   - });
90   -
91   - function handleToErrorList() {
92   - push(PageEnum.ERROR_LOG_PAGE).then(() => {
93   - errorStore.commitErrorListCountState(0);
94   - });
95   - }
96   -
97   - function handleLockPage() {
98   - openModal(true);
99   - }
100   -
101   - function renderHeaderLeft() {
102   - return (
103   - <>
104   - {unref(getShowContent) && (
105   - <div class={`${prefixCls}__left`}>
106   - {unref(getShowHeaderTrigger) && (
107   - <LayoutTrigger theme={unref(getHeaderTheme)} sider={false} />
108   - )}
109   - {unref(getShowBread) && !unref(getIsMobile) && (
110   - <LayoutBreadcrumb theme={unref(getHeaderTheme)} />
111   - )}
112   - </div>
113   - )}
114   - </>
115   - );
116   - }
117   -
118   - function renderHeaderContent() {
119   - return (
120   - <div class={`${prefixCls}__content`}>
121   - {unref(getShowTopMenu) && !unref(getIsMobile) && (
122   - <div class={[`${prefixCls}__menu `]}>
123   - {/* <div class={[`layout-header__menu `]}> */}
124   - <LayoutMenu
125   - isHorizontal={true}
126   - // class={`justify-${unref(getTopMenuAlign)}`}
127   - theme={unref(getHeaderTheme)}
128   - splitType={unref(getSplitType)}
129   - menuMode={unref(getMenuMode)}
130   - />
131   - </div>
132   - )}
133   - </div>
134   - );
135   - }
136   -
137   - function renderActionDefault(Comp: Component | any, event: Fn) {
138   - return (
139   - <div class={`${prefixCls}__action-item`} onClick={event}>
140   - <Comp class={`${prefixCls}__action-icon`} />
141   - </div>
142   - );
143   - }
144   -
145   - function renderAction() {
146   - return (
147   - <div class={`${prefixCls}__action`}>
148   - {!unref(getIsMobile) && <AppSearch class={`${prefixCls}__action-item`} />}
149   -
150   - {unref(getUseErrorHandle) && !unref(getIsMobile) && (
151   - <TooltipItem title={t('layout.header.tooltipErrorLog')}>
152   - {() => (
153   - <Badge
154   - count={errorStore.getErrorListCountState}
155   - offset={[0, 10]}
156   - dot
157   - overflowCount={99}
158   - >
159   - {() => renderActionDefault(BugOutlined, handleToErrorList)}
160   - </Badge>
161   - )}
162   - </TooltipItem>
163   - )}
164   -
165   - {unref(getUseLockPage) && !unref(getIsMobile) && (
166   - <TooltipItem title={t('layout.header.tooltipLock')}>
167   - {() => renderActionDefault(LockOutlined, handleLockPage)}
168   - </TooltipItem>
169   - )}
170   -
171   - {unref(getShowNotice) && !unref(getIsMobile) && (
172   - <TooltipItem title={t('layout.header.tooltipNotify')}>
173   - {() => <NoticeAction />}
174   - </TooltipItem>
175   - )}
176   -
177   - {unref(getShowFullScreen) && !unref(getIsMobile) && <FullScreen />}
178   -
179   - <UserDropDown theme={unref(getHeaderTheme)} />
180   -
181   - {unref(getShowLocale) && (
182   - <AppLocalePicker
183   - reload={true}
184   - showText={false}
185   - class={`${prefixCls}__action-item locale`}
186   - />
187   - )}
188   - </div>
189   - );
190   - }
191   -
192   - function renderHeaderDefault() {
193   - return (
194   - <>
195   - {unref(getShowHeaderLogo) && (
196   - <AppLogo class={`${prefixCls}__logo`} theme={unref(getHeaderTheme)} />
197   - )}
198   - {renderHeaderLeft()}
199   - {renderHeaderContent()}
200   - {renderAction()}
201   - <LockAction onRegister={register} />
202   - </>
203   - );
204   - }
205   -
206   - return () => {
207   - return (
208   - <Layout.Header class={[prefixCls, unref(headerClass), { fixed: props.fixed }]}>
209   - {() => renderHeaderDefault()}
210   - </Layout.Header>
211   - );
212   - };
213   - },
214   -});
src/layouts/default/header/LayoutMultipleHeader.less deleted 100644 → 0
1   -@import (reference) '../../../design/index.less';
2   -
3   -.multiple-tab-header {
4   - margin-left: 1px;
5   - transition: width 0.2s;
6   - flex: 0 0 auto;
7   -
8   - &.dark {
9   - margin-left: 0;
10   - }
11   -
12   - &.fixed {
13   - position: fixed;
14   - top: 0;
15   - z-index: @multiple-tab-fixed-z-index;
16   - width: 100%;
17   - }
18   -}
src/layouts/default/header/LayoutMultipleHeader.tsx renamed to src/layouts/default/header/MultipleHeader.vue
1   -import './LayoutMultipleHeader.less';
2   -
3   -import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue';
4   -
5   -import LayoutHeader from './index.vue';
6   -import MultipleTabs from '../tabs/index.vue';
7   -
8   -import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
9   -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
10   -import { useFullContent } from '/@/hooks/web/useFullContent';
11   -import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
12   -import { useLayoutContext } from '../useLayoutContext';
13   -import { useAppInject } from '/@/hooks/web/useAppInject';
14   -
15   -export default defineComponent({
16   - name: 'LayoutMultipleHeader',
17   - setup() {
18   - const placeholderHeightRef = ref(0);
19   - const fullHeaderHeightRef = ref(0);
20   - const headerElRef = ref<ComponentRef>(null);
21   - const tabElRef = ref<ComponentRef>(null);
22   -
23   - const injectValue = useLayoutContext();
24   -
25   - const { getCalcContentWidth, getSplit } = useMenuSetting();
26   - const { getIsMobile } = useAppInject();
27   - const {
28   - getFixed,
29   - getShowInsetHeaderRef,
30   - getShowFullHeaderRef,
31   - getShowHeader,
32   - getUnFixedAndFull,
33   - getHeaderTheme,
34   - } = useHeaderSetting();
35   -
36   - const { getFullContent } = useFullContent();
37   -
38   - const { getShowMultipleTab } = useMultipleTabSetting();
39   -
40   - const getShowTabs = computed(() => {
41   - return unref(getShowMultipleTab) && !unref(getFullContent);
42   - });
43   -
44   - const getPlaceholderDomStyle = computed(
45   - (): CSSProperties => {
46   - return {
47   - height: `${unref(placeholderHeightRef)}px`,
48   - };
49   - }
50   - );
51   -
52   - const getIsShowPlaceholderDom = computed(() => {
53   - return unref(getFixed) || unref(getShowFullHeaderRef);
54   - });
55   -
56   - const getWrapStyle = computed(
57   - (): CSSProperties => {
58   - const style: CSSProperties = {};
59   - if (unref(getFixed)) {
60   - style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth);
61   - }
62   - if (unref(getShowFullHeaderRef)) {
63   - style.top = `${unref(fullHeaderHeightRef)}px`;
  1 +<template>
  2 + <div :style="getPlaceholderDomStyle" v-if="getIsShowPlaceholderDom" />
  3 + <div :style="getWrapStyle" :class="getClass">
  4 + <LayoutHeader v-if="getShowInsetHeaderRef" />
  5 + <MultipleTabs v-if="getShowTabs" />
  6 + </div>
  7 +</template>
  8 +<script lang="ts">
  9 + import { defineComponent, unref, computed, CSSProperties } from 'vue';
  10 +
  11 + import LayoutHeader from './index.vue';
  12 + import MultipleTabs from '../tabs/index.vue';
  13 +
  14 + import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  15 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  16 + import { useFullContent } from '/@/hooks/web/useFullContent';
  17 + import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
  18 + import { useAppInject } from '/@/hooks/web/useAppInject';
  19 + import { useDesign } from '/@/hooks/web/useDesign';
  20 +
  21 + const HEADER_HEIGHT = 48;
  22 +
  23 + const TABS_HEIGHT = 32;
  24 + export default defineComponent({
  25 + name: 'LayoutMultipleHeader',
  26 + components: { LayoutHeader, MultipleTabs },
  27 + setup() {
  28 + const { prefixCls } = useDesign('layout-multiple-header');
  29 +
  30 + const { getCalcContentWidth, getSplit } = useMenuSetting();
  31 + const { getIsMobile } = useAppInject();
  32 + const {
  33 + getFixed,
  34 + getShowInsetHeaderRef,
  35 + getShowFullHeaderRef,
  36 + getHeaderTheme,
  37 + } = useHeaderSetting();
  38 +
  39 + const { getFullContent } = useFullContent();
  40 +
  41 + const { getShowMultipleTab } = useMultipleTabSetting();
  42 +
  43 + const getShowTabs = computed(() => {
  44 + return unref(getShowMultipleTab) && !unref(getFullContent);
  45 + });
  46 +
  47 + const getIsShowPlaceholderDom = computed(() => {
  48 + return unref(getFixed) || unref(getShowFullHeaderRef);
  49 + });
  50 +
  51 + const getWrapStyle = computed(
  52 + (): CSSProperties => {
  53 + const style: CSSProperties = {};
  54 + if (unref(getFixed)) {
  55 + style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth);
  56 + }
  57 + if (unref(getShowFullHeaderRef)) {
  58 + style.top = `${HEADER_HEIGHT}px`;
  59 + }
  60 + return style;
64 61 }
65   - return style;
66   - }
67   - );
68   -
69   - const getIsFixed = computed(() => {
70   - return unref(getFixed) || unref(getShowFullHeaderRef);
71   - });
72   -
73   - watch(
74   - () => [
75   - unref(getFixed),
76   - unref(getShowFullHeaderRef),
77   - unref(getShowHeader),
78   - unref(getShowMultipleTab),
79   - ],
80   - () => {
81   - if (unref(getUnFixedAndFull)) return;
82   - nextTick(() => {
83   - const headerEl = unref(headerElRef)?.$el;
84   - const tabEl = unref(tabElRef)?.$el;
85   - const fullHeaderEl = unref(injectValue.fullHeader)?.$el;
  62 + );
86 63  
87   - let height = 0;
88   - if (headerEl && !unref(getShowFullHeaderRef) && !unref(getSplit)) {
89   - height += headerEl.offsetHeight;
90   - }
  64 + const getIsFixed = computed(() => {
  65 + return unref(getFixed) || unref(getShowFullHeaderRef);
  66 + });
91 67  
92   - if (tabEl) {
93   - height += tabEl.offsetHeight;
  68 + const getPlaceholderDomStyle = computed(
  69 + (): CSSProperties => {
  70 + let height = 0;
  71 + if (unref(getShowFullHeaderRef) || !unref(getSplit)) {
  72 + height += HEADER_HEIGHT;
94 73 }
95   -
96   - if (fullHeaderEl && unref(getShowFullHeaderRef)) {
97   - const fullHeaderHeight = fullHeaderEl.offsetHeight;
98   - height += fullHeaderHeight;
99   - fullHeaderHeightRef.value = fullHeaderHeight;
  74 + if (unref(getShowMultipleTab)) {
  75 + height += TABS_HEIGHT;
100 76 }
101   -
102   - placeholderHeightRef.value = height;
103   - });
104   - },
105   - {
106   - immediate: true,
107   - }
108   - );
109   -
110   - return () => {
111   - return (
112   - <>
113   - {unref(getIsShowPlaceholderDom) && <div style={unref(getPlaceholderDomStyle)} />}
114   - <div
115   - style={unref(getWrapStyle)}
116   - class={['multiple-tab-header', unref(getHeaderTheme), { fixed: unref(getIsFixed) }]}
117   - >
118   - {unref(getShowInsetHeaderRef) && <LayoutHeader ref={headerElRef} />}
119   - {unref(getShowTabs) && <MultipleTabs ref={tabElRef} />}
120   - </div>
121   - </>
  77 + return {
  78 + height: `${height}px`,
  79 + };
  80 + }
122 81 );
123   - };
124   - },
125   -});
  82 +
  83 + const getClass = computed(() => {
  84 + return [
  85 + prefixCls,
  86 + `${prefixCls}--${unref(getHeaderTheme)}`,
  87 + { [`${prefixCls}--fixed`]: unref(getIsFixed) },
  88 + ];
  89 + });
  90 +
  91 + return {
  92 + getClass,
  93 + prefixCls,
  94 + getPlaceholderDomStyle,
  95 + getIsFixed,
  96 + getWrapStyle,
  97 + getIsShowPlaceholderDom,
  98 + getShowTabs,
  99 + getShowInsetHeaderRef,
  100 + };
  101 + },
  102 + });
  103 +</script>
  104 +<style lang="less" scoped>
  105 + @import (reference) '../../../design/index.less';
  106 + @prefix-cls: ~'@{namespace}-layout-multiple-header';
  107 +
  108 + .@{prefix-cls} {
  109 + margin-left: 1px;
  110 + transition: width 0.2s;
  111 + flex: 0 0 auto;
  112 +
  113 + &--dark {
  114 + margin-left: 0;
  115 + }
  116 +
  117 + &--fixed {
  118 + position: fixed;
  119 + top: 0;
  120 + z-index: @multiple-tab-fixed-z-index;
  121 + width: 100%;
  122 + }
  123 + }
  124 +</style>
... ...
src/layouts/default/header/index.less
... ... @@ -2,6 +2,8 @@
2 2 @header-trigger-prefix-cls: ~'@{namespace}-layout-header-trigger';
3 3 @header-prefix-cls: ~'@{namespace}-layout-header';
4 4 @locale-prefix-cls: ~'@{namespace}-app-locale-picker';
  5 +@breadcrumb-prefix-cls: ~'@{namespace}-layout-breadcrumb';
  6 +@logo-prefix-cls: ~'@{namespace}-app-logo';
5 7  
6 8 .@{header-prefix-cls} {
7 9 display: flex;
... ... @@ -14,6 +16,30 @@
14 16 align-items: center;
15 17 justify-content: space-between;
16 18  
  19 + &--mobile {
  20 + .@{breadcrumb-prefix-cls},
  21 + .error-action,
  22 + .notify-item,
  23 + .fullscreen-item {
  24 + display: none;
  25 + }
  26 +
  27 + .@{logo-prefix-cls} {
  28 + min-width: unset;
  29 + padding-right: 0;
  30 +
  31 + &__title {
  32 + display: none;
  33 + }
  34 + }
  35 + .@{header-trigger-prefix-cls} {
  36 + padding: 0 4px 0 8px !important;
  37 + }
  38 + .@{header-prefix-cls}-action {
  39 + padding-right: 4px;
  40 + }
  41 + }
  42 +
17 43 &--fixed {
18 44 position: fixed;
19 45 top: 0;
... ... @@ -78,7 +104,7 @@
78 104  
79 105 &-action {
80 106 display: flex;
81   - min-width: 200px;
  107 + min-width: 180px;
82 108 padding-right: 12px;
83 109 align-items: center;
84 110  
... ...
src/layouts/default/header/index.vue
... ... @@ -3,17 +3,17 @@
3 3 <!-- left start -->
4 4 <div :class="`${prefixCls}-left`">
5 5 <!-- logo -->
6   - <AppLogo v-if="getShowHeaderLogo" :class="`${prefixCls}-logo`" :theme="getHeaderTheme" />
7   -
8   - <LayoutTrigger
9   - v-if="getShowContent && getShowHeaderTrigger"
  6 + <AppLogo
  7 + v-if="getShowHeaderLogo || getIsMobile"
  8 + :class="`${prefixCls}-logo`"
10 9 :theme="getHeaderTheme"
11   - :sider="false"
12 10 />
13   - <LayoutBreadcrumb
14   - v-if="getShowContent && getShowBread && !getIsMobile"
  11 + <LayoutTrigger
  12 + v-if="(getShowContent && getShowHeaderTrigger && !getSplit) || getIsMobile"
15 13 :theme="getHeaderTheme"
  14 + :sider="false"
16 15 />
  16 + <LayoutBreadcrumb v-if="getShowContent && getShowBread" :theme="getHeaderTheme" />
17 17 </div>
18 18 <!-- left end -->
19 19  
... ... @@ -30,15 +30,15 @@
30 30  
31 31 <!-- action -->
32 32 <div :class="`${prefixCls}-action`">
33   - <AppSearch v-if="!getIsMobile" :class="`${prefixCls}-action__item`" />
  33 + <AppSearch :class="`${prefixCls}-action__item `" />
34 34  
35   - <ErrorAction v-if="getUseErrorHandle && !getIsMobile" :class="`${prefixCls}-action__item`" />
  35 + <ErrorAction v-if="getUseErrorHandle" :class="`${prefixCls}-action__item error-action`" />
36 36  
37   - <LockItem v-if="getUseLockPage && !getIsMobile" :class="`${prefixCls}-action__item`" />
  37 + <LockItem v-if="getUseLockPage" :class="`${prefixCls}-action__item lock-item`" />
38 38  
39   - <Notify v-if="getShowNotice && !getIsMobile" :class="`${prefixCls}-action__item`" />
  39 + <Notify v-if="getShowNotice" :class="`${prefixCls}-action__item notify-item`" />
40 40  
41   - <FullScreen v-if="getShowFullScreen && !getIsMobile" :class="`${prefixCls}-action__item`" />
  41 + <FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" />
42 42  
43 43 <UserDropDown :theme="getHeaderTheme" />
44 44  
... ... @@ -123,7 +123,11 @@
123 123 const theme = unref(getHeaderTheme);
124 124 return [
125 125 prefixCls,
126   - { [`${prefixCls}--fixed`]: props.fixed, [`${prefixCls}--${theme}`]: theme },
  126 + {
  127 + [`${prefixCls}--fixed`]: props.fixed,
  128 + [`${prefixCls}--mobile`]: unref(getIsMobile),
  129 + [`${prefixCls}--${theme}`]: theme,
  130 + },
127 131 ];
128 132 });
129 133  
... ... @@ -145,6 +149,7 @@
145 149 getShowBread,
146 150 getShowContent,
147 151 getSplitType,
  152 + getSplit,
148 153 getMenuMode,
149 154 getShowTopMenu,
150 155 getShowLocale,
... ...
src/layouts/default/index.vue
1 1 <template>
2 2 <Layout :class="prefixCls">
3 3 <LayoutFeatures />
4   - <LayoutHeader fixed ref="headerRef" v-if="getShowFullHeaderRef" />
  4 + <LayoutHeader fixed v-if="getShowFullHeaderRef" />
5 5 <Layout>
6   - <LayoutSideBar v-if="getShowSidebar" />
  6 + <LayoutSideBar v-if="getShowSidebar || getIsMobile" />
7 7 <Layout :class="`${prefixCls}__main`">
8 8 <LayoutMultipleHeader />
9 9 <LayoutContent />
... ... @@ -14,21 +14,21 @@
14 14 </template>
15 15  
16 16 <script lang="ts">
17   - import { defineComponent, ref } from 'vue';
  17 + import { defineComponent } from 'vue';
18 18 import { Layout } from 'ant-design-vue';
19 19 import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
20 20  
21 21 import LayoutHeader from './header/index.vue';
22 22 import LayoutContent from './content/index.vue';
23   - import LayoutSideBar from './sider';
24   - import LayoutMultipleHeader from './header/LayoutMultipleHeader';
  23 + import LayoutSideBar from './sider/index.vue';
  24 + import LayoutMultipleHeader from './header/MultipleHeader.vue';
25 25  
26 26 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
27 27 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
28 28 import { useDesign } from '/@/hooks/web/useDesign';
29   - import { createLayoutContext } from './useLayoutContext';
30 29  
31 30 import { registerGlobComp } from '/@/components/registerGlobComp';
  31 + import { useAppInject } from '/@/hooks/web/useAppInject';
32 32  
33 33 export default defineComponent({
34 34 name: 'DefaultLayout',
... ... @@ -47,11 +47,9 @@
47 47 // default layout It is loaded after login. So it won’t be packaged to the first screen
48 48 registerGlobComp();
49 49  
50   - const headerRef = ref<ComponentRef>(null);
51   -
52 50 const { prefixCls } = useDesign('default-layout');
53 51  
54   - createLayoutContext({ fullHeader: headerRef });
  52 + const { getIsMobile } = useAppInject();
55 53  
56 54 const { getShowFullHeaderRef } = useHeaderSetting();
57 55  
... ... @@ -60,8 +58,8 @@
60 58 return {
61 59 getShowFullHeaderRef,
62 60 getShowSidebar,
63   - headerRef,
64 61 prefixCls,
  62 + getIsMobile,
65 63 };
66 64 },
67 65 });
... ...
src/layouts/default/menu/index.less
1 1 @import (reference) '../../../design/index.less';
2 2  
3   -.layout-menu {
4   - &__logo {
  3 +@prefix-cls: ~'@{namespace}-layout-menu';
  4 +@logo-prefix-cls: ~'@{namespace}-app-logo';
  5 +
  6 +.@{prefix-cls} {
  7 + &-logo {
5 8 height: @header-height;
6 9 padding: 10px 4px 10px 10px;
7 10  
... ... @@ -10,4 +13,12 @@
10 13 height: @logo-width;
11 14 }
12 15 }
  16 +
  17 + &--mobile {
  18 + .@{logo-prefix-cls} {
  19 + &__title {
  20 + opacity: 1;
  21 + }
  22 + }
  23 + }
13 24 }
... ...
src/layouts/default/menu/index.tsx
1 1 import './index.less';
2 2  
3   -import { PropType, toRef } from 'vue';
  3 +import type { PropType, CSSProperties } from 'vue';
4 4  
5   -import { computed, defineComponent, unref } from 'vue';
  5 +import { computed, defineComponent, unref, toRef } from 'vue';
6 6 import { BasicMenu } from '/@/components/Menu';
7 7 import { AppLogo } from '/@/components/Application';
8 8  
... ... @@ -17,7 +17,8 @@ import { openWindow } from &#39;/@/utils&#39;;
17 17 import { propTypes } from '/@/utils/propTypes';
18 18 import { isUrl } from '/@/utils/is';
19 19 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
20   -import { CSSProperties } from 'vue';
  20 +import { useAppInject } from '/@/hooks/web/useAppInject';
  21 +import { useDesign } from '/@/hooks/web/useDesign';
21 22  
22 23 export default defineComponent({
23 24 name: 'LayoutMenu',
... ... @@ -50,9 +51,15 @@ export default defineComponent({
50 51 } = useMenuSetting();
51 52 const { getShowLogo } = useRootSetting();
52 53  
  54 + const { prefixCls } = useDesign('layout-menu');
  55 +
53 56 const { menusRef } = useSplitMenu(toRef(props, 'splitType'));
54 57  
55   - const getComputedMenuMode = computed(() => props.menuMode || unref(getMenuMode));
  58 + const { getIsMobile } = useAppInject();
  59 +
  60 + const getComputedMenuMode = computed(() =>
  61 + unref(getIsMobile) ? MenuModeEnum.INLINE : props.menuMode || unref(getMenuMode)
  62 + );
56 63  
57 64 const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme));
58 65  
... ... @@ -69,6 +76,16 @@ export default defineComponent({
69 76 };
70 77 }
71 78 );
  79 +
  80 + const getLogoClass = computed(() => {
  81 + return [
  82 + `${prefixCls}-logo`,
  83 + unref(getComputedMenuTheme),
  84 + {
  85 + [`${prefixCls}--mobile`]: unref(getIsMobile),
  86 + },
  87 + ];
  88 + });
72 89 /**
73 90 * click menu
74 91 * @param menu
... ... @@ -91,12 +108,12 @@ export default defineComponent({
91 108 }
92 109  
93 110 function renderHeader() {
94   - if (!unref(getIsShowLogo)) return null;
  111 + if (!unref(getIsShowLogo) && !unref(getIsMobile)) return null;
95 112  
96 113 return (
97 114 <AppLogo
98 115 showTitle={!unref(getCollapsed)}
99   - class={[`layout-menu__logo`, unref(getComputedMenuTheme)]}
  116 + class={unref(getLogoClass)}
100 117 theme={unref(getComputedMenuTheme)}
101 118 />
102 119 );
... ... @@ -128,7 +145,6 @@ export default defineComponent({
128 145 ) : (
129 146 renderMenu()
130 147 )}
131   - ;
132 148 </>
133 149 );
134 150 };
... ...
src/layouts/default/menu/useLayoutMenu.ts
... ... @@ -10,11 +10,13 @@ import { useMenuSetting } from &#39;/@/hooks/setting/useMenuSetting&#39;;
10 10  
11 11 import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus';
12 12 import { permissionStore } from '/@/store/modules/permission';
  13 +import { useAppInject } from '/@/hooks/web/useAppInject';
13 14  
14 15 export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
15 16 // Menu array
16 17 const menusRef = ref<Menu[]>([]);
17 18 const { currentRoute } = useRouter();
  19 + const { getIsMobile } = useAppInject();
18 20 const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
19 21  
20 22 const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
... ... @@ -36,7 +38,7 @@ export function useSplitMenu(splitType: Ref&lt;MenuSplitTyeEnum&gt;) {
36 38 watch(
37 39 [() => unref(currentRoute).path, () => unref(splitType)],
38 40 async ([path]: [string, MenuSplitTyeEnum]) => {
39   - if (unref(splitNotLeft)) return;
  41 + if (unref(splitNotLeft) || unref(getIsMobile)) return;
40 42  
41 43 const parentPath = await getCurrentParentPath(path);
42 44 parentPath && throttleHandleSplitLeftMenu(parentPath);
... ... @@ -65,24 +67,24 @@ export function useSplitMenu(splitType: Ref&lt;MenuSplitTyeEnum&gt;) {
65 67  
66 68 // Handle left menu split
67 69 async function handleSplitLeftMenu(parentPath: string) {
68   - if (unref(getSplitLeft)) return;
  70 + if (unref(getSplitLeft) || unref(getIsMobile)) return;
69 71  
70 72 // spilt mode left
71 73 const children = await getChildrenMenus(parentPath);
72 74 if (!children) {
73   - setMenuSetting({ hidden: false });
  75 + setMenuSetting({ hidden: true });
74 76 menusRef.value = [];
75 77 return;
76 78 }
77 79  
78   - setMenuSetting({ hidden: true });
  80 + setMenuSetting({ hidden: false });
79 81 menusRef.value = children;
80 82 }
81 83  
82 84 // get menus
83 85 async function genMenus() {
84 86 // normal mode
85   - if (unref(normalType)) {
  87 + if (unref(normalType) || unref(getIsMobile)) {
86 88 menusRef.value = await getMenus();
87 89 return;
88 90 }
... ...
src/layouts/default/sider/DragBar.vue 0 → 100644
  1 +<template>
  2 + <div :class="getClass" :style="getDragBarStyle" />
  3 +</template>
  4 +<script lang="ts">
  5 + import { defineComponent, computed, unref } from 'vue';
  6 +
  7 + import { useDesign } from '/@/hooks/web/useDesign';
  8 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  9 +
  10 + export default defineComponent({
  11 + name: 'DargBar',
  12 + props: {
  13 + mobile: Boolean,
  14 + },
  15 + setup(props) {
  16 + const { getMiniWidthNumber, getCollapsed, getCanDrag } = useMenuSetting();
  17 +
  18 + const { prefixCls } = useDesign('darg-bar');
  19 + const getDragBarStyle = computed(() => {
  20 + if (unref(getCollapsed)) {
  21 + return { left: `${unref(getMiniWidthNumber)}px` };
  22 + }
  23 + return {};
  24 + });
  25 +
  26 + const getClass = computed(() => {
  27 + return [
  28 + prefixCls,
  29 + {
  30 + [`${prefixCls}--hide`]: !unref(getCanDrag) || props.mobile,
  31 + },
  32 + ];
  33 + });
  34 +
  35 + return {
  36 + prefixCls,
  37 + getDragBarStyle,
  38 + getClass,
  39 + };
  40 + },
  41 + });
  42 +</script>
  43 +<style lang="less" scoped>
  44 + @import (reference) '../../../design/index.less';
  45 + @prefix-cls: ~'@{namespace}-darg-bar';
  46 +
  47 + .@{prefix-cls} {
  48 + position: absolute;
  49 + top: 0;
  50 + right: -2px;
  51 + z-index: @side-drag-z-index;
  52 + width: 2px;
  53 + height: 100%;
  54 + cursor: col-resize;
  55 + border-top: none;
  56 + border-bottom: none;
  57 +
  58 + &--hide {
  59 + display: none;
  60 + }
  61 +
  62 + &:hover {
  63 + background: @primary-color;
  64 + box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
  65 + }
  66 + }
  67 +</style>
... ...
src/layouts/default/sider/index.tsx renamed to src/layouts/default/sider/LayoutSider.tsx
... ... @@ -12,6 +12,7 @@ import { useTrigger, useDragLine, useSiderEvent } from &#39;./useLayoutSider&#39;;
12 12 import { useAppInject } from '/@/hooks/web/useAppInject';
13 13 import { useDesign } from '/@/hooks/web/useDesign';
14 14  
  15 +import DragBar from './DragBar.vue';
15 16 export default defineComponent({
16 17 name: 'LayoutSideBar',
17 18 setup() {
... ... @@ -31,11 +32,11 @@ export default defineComponent({
31 32  
32 33 const { prefixCls } = useDesign('layout-sideBar');
33 34  
34   - const { getTriggerAttr, getTriggerSlot } = useTrigger();
35   -
36 35 const { getIsMobile } = useAppInject();
37 36  
38   - const { renderDragLine } = useDragLine(sideRef, dragBarRef);
  37 + const { getTriggerAttr, getTriggerSlot } = useTrigger(getIsMobile);
  38 +
  39 + useDragLine(sideRef, dragBarRef);
39 40  
40 41 const { getCollapsedWidth, onBreakpointChange, onCollapseChange } = useSiderEvent();
41 42  
... ... @@ -48,7 +49,7 @@ export default defineComponent({
48 49 });
49 50  
50 51 const showClassSideBarRef = computed(() => {
51   - return unref(getSplit) ? unref(getMenuHidden) : true;
  52 + return unref(getSplit) ? !unref(getMenuHidden) : true;
52 53 });
53 54  
54 55 const getSiderClass = computed(() => {
... ... @@ -57,7 +58,7 @@ export default defineComponent({
57 58 {
58 59 [`${prefixCls}--fixed`]: unref(getMenuFixed),
59 60 hidden: !unref(showClassSideBarRef),
60   - [`${prefixCls}--mix`]: unref(getIsMixMode),
  61 + [`${prefixCls}--mix`]: unref(getIsMixMode) && !unref(getIsMobile),
61 62 },
62 63 ];
63 64 });
... ... @@ -84,7 +85,7 @@ export default defineComponent({
84 85 menuMode={unref(getMode)}
85 86 splitType={unref(getSplitType)}
86 87 />
87   - {renderDragLine()}
  88 + <DragBar ref={dragBarRef} />
88 89 </>
89 90 );
90 91 }
... ... @@ -101,7 +102,7 @@ export default defineComponent({
101 102 collapsible
102 103 class={unref(getSiderClass)}
103 104 width={unref(getMenuWidth)}
104   - collapsed={unref(getCollapsed)}
  105 + collapsed={unref(getIsMobile) ? false : unref(getCollapsed)}
105 106 collapsedWidth={unref(getCollapsedWidth)}
106 107 theme={unref(getMenuTheme)}
107 108 onCollapse={onCollapseChange}
... ...
src/layouts/default/sider/index.less
... ... @@ -44,27 +44,6 @@
44 44 z-index: 10;
45 45 }
46 46  
47   - &__darg-bar {
48   - position: absolute;
49   - top: 0;
50   - right: -2px;
51   - z-index: @side-drag-z-index;
52   - width: 2px;
53   - height: 100%;
54   - cursor: col-resize;
55   - border-top: none;
56   - border-bottom: none;
57   -
58   - &.hide {
59   - display: none;
60   - }
61   -
62   - &:hover {
63   - background: @primary-color;
64   - box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
65   - }
66   - }
67   -
68 47 & .ant-layout-sider-trigger {
69 48 height: 36px;
70 49 line-height: 36px;
... ...
src/layouts/default/sider/index.vue 0 → 100644
  1 +<template>
  2 + <Drawer
  3 + v-if="getIsMobile"
  4 + placement="left"
  5 + :class="prefixCls"
  6 + :width="getMenuWidth"
  7 + :getContainer="null"
  8 + :visible="!getCollapsed"
  9 + @close="handleClose"
  10 + >
  11 + <Sider />
  12 + </Drawer>
  13 + <Sider v-else />
  14 +</template>
  15 +<script lang="ts">
  16 + import { defineComponent } from 'vue';
  17 +
  18 + import Sider from './LayoutSider';
  19 + import { Drawer } from 'ant-design-vue';
  20 + import { useAppInject } from '/@/hooks/web/useAppInject';
  21 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  22 + import { useDesign } from '/@/hooks/web/useDesign';
  23 + export default defineComponent({
  24 + name: 'SiderWrapper',
  25 + components: { Sider, Drawer },
  26 + setup() {
  27 + const { prefixCls } = useDesign('layout-sider-wrapper');
  28 + const { getIsMobile } = useAppInject();
  29 + const { setMenuSetting, getCollapsed, getMenuWidth } = useMenuSetting();
  30 +
  31 + function handleClose() {
  32 + setMenuSetting({
  33 + collapsed: true,
  34 + });
  35 + }
  36 +
  37 + return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth };
  38 + },
  39 + });
  40 +</script>
  41 +<style lang="less">
  42 + @import (reference) '../../../design/index.less';
  43 + @prefix-cls: ~'@{namespace}-layout-sider-wrapper';
  44 + .@{prefix-cls} {
  45 + .ant-drawer-body {
  46 + height: 100vh;
  47 + padding: 0;
  48 + }
  49 +
  50 + .ant-drawer-header-no-title {
  51 + display: none;
  52 + }
  53 + }
  54 +</style>
... ...
src/layouts/default/sider/useLayoutSider.tsx
... ... @@ -42,12 +42,17 @@ export function useSiderEvent() {
42 42 /**
43 43 * Handle related operations of menu folding
44 44 */
45   -export function useTrigger() {
46   - const { getTrigger } = useMenuSetting();
  45 +export function useTrigger(getIsMobile: Ref<boolean>) {
  46 + const { getTrigger, getSplit } = useMenuSetting();
47 47  
48 48 const showTrigger = computed(() => {
49 49 const trigger = unref(getTrigger);
50   - return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
  50 +
  51 + return (
  52 + trigger !== TriggerEnum.NONE &&
  53 + !unref(getIsMobile) &&
  54 + (trigger === TriggerEnum.FOOTER || unref(getSplit))
  55 + );
51 56 });
52 57  
53 58 const getTriggerAttr = computed(() => {
... ... @@ -77,14 +82,7 @@ export function useTrigger() {
77 82 * @param dragBarRef
78 83 */
79 84 export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
80   - const { getMiniWidthNumber, getCollapsed, setMenuSetting, getCanDrag } = useMenuSetting();
81   -
82   - const getDragBarStyle = computed(() => {
83   - if (unref(getCollapsed)) {
84   - return { left: `${unref(getMiniWidthNumber)}px` };
85   - }
86   - return {};
87   - });
  85 + const { getMiniWidthNumber, getCollapsed, setMenuSetting } = useMenuSetting();
88 86  
89 87 onMounted(() => {
90 88 nextTick(() => {
... ... @@ -93,16 +91,6 @@ export function useDragLine(siderRef: Ref&lt;any&gt;, dragBarRef: Ref&lt;any&gt;) {
93 91 });
94 92 });
95 93  
96   - function renderDragLine() {
97   - return (
98   - <div
99   - class={[`layout-sidebar__darg-bar`, { hide: !unref(getCanDrag) }]}
100   - style={unref(getDragBarStyle)}
101   - ref={dragBarRef}
102   - />
103   - );
104   - }
105   -
106 94 function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) {
107 95 document.onmousemove = function (innerE) {
108 96 let iT = (ele as any).left + (innerE.clientX - clientX);
... ... @@ -138,21 +126,22 @@ export function useDragLine(siderRef: Ref&lt;any&gt;, dragBarRef: Ref&lt;any&gt;) {
138 126 }
139 127  
140 128 function changeWrapWidth() {
141   - const ele = unref(dragBarRef) as any;
  129 + const ele = unref(dragBarRef)?.$el;
  130 + if (!ele) {
  131 + return;
  132 + }
142 133 const side = unref(siderRef);
143   -
144 134 const wrap = (side || {}).$el;
145   - ele &&
146   - (ele.onmousedown = (e: any) => {
147   - wrap.style.transition = 'unset';
148   - const clientX = e?.clientX;
149   - ele.left = ele.offsetLeft;
150   - handleMouseMove(ele, wrap, clientX);
151   - removeMouseup(ele);
152   - ele.setCapture?.();
153   - return false;
154   - });
  135 + ele.onmousedown = (e: any) => {
  136 + wrap.style.transition = 'unset';
  137 + const clientX = e?.clientX;
  138 + ele.left = ele.offsetLeft;
  139 + handleMouseMove(ele, wrap, clientX);
  140 + removeMouseup(ele);
  141 + ele.setCapture?.();
  142 + return false;
  143 + };
155 144 }
156 145  
157   - return { renderDragLine };
  146 + return {};
158 147 }
... ...
src/layouts/default/useLayoutContext.ts deleted 100644 → 0
1   -import { InjectionKey, Ref } from 'vue';
2   -import { createContext, useContext } from '/@/hooks/core/useContext';
3   -
4   -export interface LayoutContextProps {
5   - fullHeader: Ref<ComponentRef>;
6   -}
7   -
8   -const key: InjectionKey<LayoutContextProps> = Symbol();
9   -
10   -export function createLayoutContext(context: LayoutContextProps) {
11   - return createContext<LayoutContextProps>(context, key);
12   -}
13   -
14   -export function useLayoutContext() {
15   - return useContext<LayoutContextProps>(key);
16   -}
src/locales/lang/en/component/app.ts
1 1 export default {
2 2 search: 'Search',
  3 + cancel: 'Cancel',
3 4 searchNotData: 'No search results yet',
4 5 toSearch: 'to search',
5 6 toNavigate: 'to navigate',
... ...
src/locales/lang/zh_CN/component/app.ts
1 1 export default {
2 2 search: '搜索',
  3 + cancel: '取消',
3 4 searchNotData: '暂无搜索结果',
4 5 toSearch: '确认',
5 6 toNavigate: '切换',
... ...
src/logics/mitt/tabChange.ts
... ... @@ -13,8 +13,9 @@ const key = Symbol();
13 13 let lastChangeTab: RouteLocationNormalized;
14 14  
15 15 export function setLastChangeTab(lastChangeRoute: RouteLocationNormalized) {
16   - mitt.emit(key, getRoute(lastChangeRoute));
17   - lastChangeTab = getRoute(lastChangeRoute);
  16 + const r = getRoute(lastChangeRoute);
  17 + mitt.emit(key, r);
  18 + lastChangeTab = r;
18 19 }
19 20  
20 21 export function listenerLastChangeTab(
... ...
src/main.ts
... ... @@ -51,6 +51,5 @@ if (isDevMode()) {
51 51 if (isProdMode() && isUseMock()) {
52 52 setupProdMockServer();
53 53 }
54   -
55 54 // Used to share app instances in other modules
56 55 setApp(app);
... ...
src/router/helper/menuHelper.ts
1 1 import { AppRouteModule } from '/@/router/types.d';
2 2 import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types';
3 3  
4   -import { findPath, forEach, treeMap, treeToList } from '/@/utils/helper/treeHelper';
  4 +import { findPath, forEach, treeMap } from '/@/utils/helper/treeHelper';
5 5 import { cloneDeep } from 'lodash-es';
6 6 import { isUrl } from '/@/utils/is';
7 7  
... ... @@ -10,10 +10,6 @@ export function getAllParentPath(treeData: any[], path: string) {
10 10 return (menuList || []).map((item) => item.path);
11 11 }
12 12  
13   -export function flatMenus(menus: Menu[]) {
14   - return treeToList(menus);
15   -}
16   -
17 13 // 拼接父级路径
18 14 function joinParentPath(list: any, node: any) {
19 15 let allPaths = getAllParentPath(list, node.path);
... ...
src/router/helper/routeHelper.ts
... ... @@ -5,6 +5,10 @@ import { getParentLayout, LAYOUT } from &#39;/@/router/constant&#39;;
5 5 import dynamicImport from './dynamicImport';
6 6 import { cloneDeep } from 'lodash-es';
7 7  
  8 +export type LayoutMapKey = 'LAYOUT';
  9 +
  10 +const LayoutMap = new Map<LayoutMapKey, () => Promise<typeof import('*.vue')>>();
  11 +
8 12 // 动态引入
9 13 function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
10 14 if (!routes) return;
... ... @@ -20,16 +24,14 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
20 24 });
21 25 }
22 26  
23   -function getLayoutComp(comp: string) {
24   - return comp === 'LAYOUT' ? LAYOUT : '';
25   -}
26   -
27 27 // Turn background objects into routing objects
28 28 export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
  29 + LayoutMap.set('LAYOUT', LAYOUT);
  30 +
29 31 routeList.forEach((route) => {
30 32 if (route.component) {
31 33 if ((route.component as string).toUpperCase() === 'LAYOUT') {
32   - route.component = getLayoutComp(route.component);
  34 + route.component = LayoutMap.get(route.component);
33 35 } else {
34 36 route.children = [cloneDeep(route)];
35 37 route.component = LAYOUT;
... ... @@ -46,16 +48,6 @@ export function transformObjToRoute&lt;T = AppRouteModule&gt;(routeList: AppRouteModul
46 48 return (routeList as unknown) as T[];
47 49 }
48 50  
49   -export function getParams(data: any = {}) {
50   - const { params = {} } = data;
51   - let ret = '';
52   - Object.keys(params).forEach((key) => {
53   - const p = params[key];
54   - ret += `/${p}`;
55   - });
56   - return ret;
57   -}
58   -
59 51 // Return to the new routing structure, not affected by the original example
60 52 export function getRoute(route: RouteLocationNormalized): RouteLocationNormalized {
61 53 if (!route) return route;
... ...
src/router/menus/index.ts
1 1 import type { Menu, MenuModule } from '/@/router/types';
2 2 import type { RouteRecordNormalized } from 'vue-router';
  3 +
3 4 import { appStore } from '/@/store/modules/app';
4 5 import { permissionStore } from '/@/store/modules/permission';
5   -import { transformMenuModule, flatMenus, getAllParentPath } from '/@/router/helper/menuHelper';
  6 +import { transformMenuModule, getAllParentPath } from '/@/router/helper/menuHelper';
6 7 import { filter } from '/@/utils/helper/treeHelper';
7 8 import router from '/@/router';
8 9 import { PermissionModeEnum } from '/@/enums/appEnum';
... ... @@ -10,6 +11,8 @@ import { pathToRegexp } from &#39;path-to-regexp&#39;;
10 11  
11 12 import modules from 'globby!/@/router/menus/modules/**/*.@(ts)';
12 13  
  14 +const reg = /(((https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
  15 +
13 16 const menuModules: MenuModule[] = [];
14 17  
15 18 Object.keys(modules).forEach((key) => {
... ... @@ -38,18 +41,9 @@ const staticMenus: Menu[] = [];
38 41  
39 42 async function getAsyncMenus() {
40 43 // 前端角色控制菜单 直接取菜单文件
41   - if (!isBackMode()) {
42   - return staticMenus;
43   - }
44   - return permissionStore.getBackMenuListState;
  44 + return !isBackMode() ? staticMenus : permissionStore.getBackMenuListState;
45 45 }
46 46  
47   -// 获取深层扁平化菜单
48   -export const getFlatMenus = async (): Promise<Menu[]> => {
49   - const menus = await getAsyncMenus();
50   - return flatMenus(menus);
51   -};
52   -
53 47 // 获取菜单 树级
54 48 export const getMenus = async (): Promise<Menu[]> => {
55 49 const menus = await getAsyncMenus();
... ... @@ -61,7 +55,7 @@ export const getMenus = async (): Promise&lt;Menu[]&gt; =&gt; {
61 55 export async function getCurrentParentPath(currentPath: string) {
62 56 const menus = await getAsyncMenus();
63 57 const allParentPath = await getAllParentPath(menus, currentPath);
64   - return allParentPath[0];
  58 + return allParentPath?.[0];
65 59 }
66 60  
67 61 // 获取1级菜单,删除children
... ... @@ -81,27 +75,24 @@ export async function getChildrenMenus(parentPath: string) {
81 75 return parent.children;
82 76 }
83 77  
84   -// 扁平化children
85   -export async function getFlatChildrenMenus(children: Menu[]) {
86   - return flatMenus(children);
87   -}
88   -
89 78 // 通用过滤方法
90 79 function basicFilter(routes: RouteRecordNormalized[]) {
91 80 return (menu: Menu) => {
92 81 const matchRoute = routes.find((route) => {
93   - if (route.meta.externalLink) {
  82 + const match = route.path.match(reg)?.[0];
  83 + if (match && match === menu.path) {
94 84 return true;
95 85 }
96 86  
97   - if (route.meta) {
98   - if (route.meta.carryParam) {
99   - return pathToRegexp(route.path).test(menu.path);
100   - }
101   - if (route.meta.ignoreAuth) return true;
  87 + if (route.meta?.carryParam) {
  88 + return pathToRegexp(route.path).test(menu.path);
102 89 }
  90 + const isSame = route.path === menu.path;
  91 + if (!isSame) return false;
  92 +
  93 + if (route.meta?.ignoreAuth) return true;
103 94  
104   - return route.path === menu.path;
  95 + return isSame || pathToRegexp(route.path).test(menu.path);
105 96 });
106 97  
107 98 if (!matchRoute) return false;
... ...
src/router/routes/modules/demo/iframe.ts
... ... @@ -38,7 +38,6 @@ const iframe: AppRouteModule = {
38 38 name: 'DocExternal',
39 39 component: IFrame,
40 40 meta: {
41   - externalLink: true,
42 41 title: t('routes.demo.iframe.docExternal'),
43 42 },
44 43 },
... ...
src/router/types.d.ts
... ... @@ -15,9 +15,6 @@ export interface RouteMeta {
15 15 // icon on tab
16 16 icon?: string;
17 17 // Jump address
18   - frameSrc?: string;
19   - // Outer link jump address
20   - externalLink?: boolean;
21 18  
22 19 // current page transition
23 20 transitionName?: string;
... ...
src/settings/projectSetting.ts
... ... @@ -89,7 +89,7 @@ const setting: ProjectConfig = {
89 89 // Whether to show no dom
90 90 show: true,
91 91 // Whether to show dom
92   - hidden: true,
  92 + hidden: false,
93 93 // Menu width
94 94 menuWidth: 210,
95 95 // Menu mode
... ...
src/utils/is.ts
... ... @@ -50,7 +50,7 @@ export function isRegExp(val: unknown): val is RegExp {
50 50 return is(val, 'RegExp');
51 51 }
52 52  
53   -export function isArray(val: unknown): val is Array<any> {
  53 +export function isArray(val: any): val is Array<any> {
54 54 return val && Array.isArray(val);
55 55 }
56 56  
... ...
src/views/sys/lock/LockPage.vue
... ... @@ -221,25 +221,35 @@
221 221 font-size: 23em;
222 222 }
223 223 @media (min-width: @screen-sm-max) and (max-width: @screen-md-max) {
224   - font-size: 19em;
  224 + height: 50%;
  225 + font-size: 12em;
  226 + border-radius: 10px;
  227 +
  228 + .meridiem {
  229 + font-size: 20px;
  230 + }
225 231 }
226 232 @media (min-width: @screen-xs-max) and (max-width: @screen-sm-max) {
227 233 font-size: 13em;
228 234 }
229 235 @media (max-width: @screen-xs) {
230   - height: 50%;
231   - font-size: 6em;
232   - border-radius: 20px;
  236 + height: 30%;
  237 + font-size: 5em;
  238 + border-radius: 10px;
  239 +
  240 + .meridiem {
  241 + font-size: 14px;
  242 + }
233 243 }
234 244 }
235 245  
236 246 &__footer-date {
237 247 position: absolute;
238 248 bottom: 20px;
239   - left: 50%;
  249 + width: 100%;
240 250 font-family: helvetica;
241 251 color: #bababa;
242   - transform: translate(-50%, 0);
  252 + text-align: center;
243 253  
244 254 .time {
245 255 font-size: 50px;
... ...
yarn.lock
... ... @@ -1051,43 +1051,43 @@
1051 1051 resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.272.tgz#27c7caee9764e0304161261ec08ffc2794944b66"
1052 1052 integrity sha512-FyiTc7UiXJ5cDfk09lv70sYOSi5uLyK+a0LnF1KgWmofkikL06p98ksNRN7stmHryOYarSy75xgi6MbgAwtltQ==
1053 1053  
1054   -"@intlify/core@9.0.0-beta.12":
1055   - version "9.0.0-beta.12"
1056   - resolved "https://registry.npmjs.org/@intlify/core/-/core-9.0.0-beta.12.tgz#f7d2d09060b8e00ae37157e00a0daa1c86290802"
1057   - integrity sha512-0wdOS9d0ZEvGkbNIdaxEHQQOfAIuhv1Q8CSpNImThh8ZDD+5Sa38wTerHBO0/Rk0HfHUP/hjPqbxxRqITmSo1g==
1058   - dependencies:
1059   - "@intlify/message-compiler" "9.0.0-beta.12"
1060   - "@intlify/message-resolver" "9.0.0-beta.12"
1061   - "@intlify/runtime" "9.0.0-beta.12"
1062   - "@intlify/shared" "9.0.0-beta.12"
1063   -
1064   -"@intlify/message-compiler@9.0.0-beta.12":
1065   - version "9.0.0-beta.12"
1066   - resolved "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.0.0-beta.12.tgz#836a49cfd057ecb2c536680cc01aa16693211891"
1067   - integrity sha512-EMzBDBIsFvWV9w0tRAHzn2BD1C7nkJkXYwDWinROmoL6C4jgKUgon+9Uxp7lV0H1E+7hUfhGj6zHdtJrwFhH+g==
1068   - dependencies:
1069   - "@intlify/message-resolver" "9.0.0-beta.12"
1070   - "@intlify/shared" "9.0.0-beta.12"
  1054 +"@intlify/core-base@9.0.0-beta.13":
  1055 + version "9.0.0-beta.13"
  1056 + resolved "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.0.0-beta.13.tgz#fb6bc278209cb7bef44853a42160fedb0560c3f8"
  1057 + integrity sha512-ukImWV+QvRmNZtCTLrSW391z46eMuBheCMPZh801nM3v0Dosfu2PtWO5/z8Q9Bsom4Q+PNQ5eBtOQj2yCAhVEA==
  1058 + dependencies:
  1059 + "@intlify/message-compiler" "9.0.0-beta.13"
  1060 + "@intlify/message-resolver" "9.0.0-beta.13"
  1061 + "@intlify/runtime" "9.0.0-beta.13"
  1062 + "@intlify/shared" "9.0.0-beta.13"
  1063 +
  1064 +"@intlify/message-compiler@9.0.0-beta.13":
  1065 + version "9.0.0-beta.13"
  1066 + resolved "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.0.0-beta.13.tgz#3b8ddcb2be3f80b28c6e4f6028c0b3ec4e709849"
  1067 + integrity sha512-1z7716InFM8FdTAz64wqZvFuT4wL7WKF63v+vUEW4s9FLoL0U+xIccor9P5XHAvvG1gPMH/Zxd0deg/ULZ1Mcg==
  1068 + dependencies:
  1069 + "@intlify/message-resolver" "9.0.0-beta.13"
  1070 + "@intlify/shared" "9.0.0-beta.13"
1071 1071 source-map "0.6.1"
1072 1072  
1073   -"@intlify/message-resolver@9.0.0-beta.12":
1074   - version "9.0.0-beta.12"
1075   - resolved "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.0.0-beta.12.tgz#98cf346f5da0fdf3408ba132c24841295a4e02db"
1076   - integrity sha512-i8bmWzhiBH59YED3SXqvdUfwecl7OUPOU/8yvfdhg2rXuZ4e2chCPnLpPafXz6bi88HcRsWF4aRGlpwDVDYadg==
  1073 +"@intlify/message-resolver@9.0.0-beta.13":
  1074 + version "9.0.0-beta.13"
  1075 + resolved "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.0.0-beta.13.tgz#ae6de0bf0e54093160442d465e719bf03fd0f146"
  1076 + integrity sha512-mR1eSpRtB4jh11TpQTUyzjEwqZ6D30mJYREEfSrl5YKfUKwDQrulrOaIO8T5gVQG2m09vfxJHVrgfJ2hR8z/0Q==
1077 1077  
1078   -"@intlify/runtime@9.0.0-beta.12":
1079   - version "9.0.0-beta.12"
1080   - resolved "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.0.0-beta.12.tgz#647a62a326d92690569798ef046d29e8daa25c96"
1081   - integrity sha512-4ucZHqk/VGhrQEgu9xU5tE/sJTNfqKBhQtaXyEgYHchL9PvLoS1HFwPjABHvWjo3aVcv4d2cGtUPBwH4oLROKA==
  1078 +"@intlify/runtime@9.0.0-beta.13":
  1079 + version "9.0.0-beta.13"
  1080 + resolved "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.0.0-beta.13.tgz#8deff103ee6982c6d531314e9f965b90768d8a27"
  1081 + integrity sha512-hcb3sg75SokuzNDG8IC6PJmwjsS/xdgevd99UNG1zKb7s5qFFb90ApvPDpiH0+R9TMQe11fZqg5dyrVBKqAV4A==
1082 1082 dependencies:
1083   - "@intlify/message-compiler" "9.0.0-beta.12"
1084   - "@intlify/message-resolver" "9.0.0-beta.12"
1085   - "@intlify/shared" "9.0.0-beta.12"
  1083 + "@intlify/message-compiler" "9.0.0-beta.13"
  1084 + "@intlify/message-resolver" "9.0.0-beta.13"
  1085 + "@intlify/shared" "9.0.0-beta.13"
1086 1086  
1087   -"@intlify/shared@9.0.0-beta.12":
1088   - version "9.0.0-beta.12"
1089   - resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.0.0-beta.12.tgz#e939575bc4047411b9fc65347779f5b3173c1130"
1090   - integrity sha512-XtHAzQ2KBcdN0Khc7ZDCo5GnKQK4Vv0GKD1BplCWntpA2d5XqjdDpFuKumvbiOjPvYtuCFnksJU0OgJiCWG+KQ==
  1087 +"@intlify/shared@9.0.0-beta.13":
  1088 + version "9.0.0-beta.13"
  1089 + resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.0.0-beta.13.tgz#2d93d695f19fd699ea8b336066f9d6dfc185f094"
  1090 + integrity sha512-/rqC3YEGHs3uu3XSsF1zdBKJb+on34Yn8Z58K3YxJsFxKPHa8mH73EUtN79hTZWh6Js4zEa/WsCgZCM62b8eJA==
1091 1091  
1092 1092 "@koa/cors@^3.1.0":
1093 1093 version "3.1.0"
... ... @@ -8256,16 +8256,13 @@ vue-eslint-parser@^7.3.0:
8256 8256 esquery "^1.0.1"
8257 8257 lodash "^4.17.15"
8258 8258  
8259   -vue-i18n@^9.0.0-beta.12:
8260   - version "9.0.0-beta.12"
8261   - resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.12.tgz#f6e2fc1cc366b8f16aa4754642931e937ebde303"
8262   - integrity sha512-hDnr+GsIGCIKRtZsdDczkhqyzbpLuPgEkH5bQyMzrKTLelXipLvIVmUCAsSjyR7xMHDCwP6AwVTIZwk6ENXkwg==
  8259 +vue-i18n@^9.0.0-beta.13:
  8260 + version "9.0.0-beta.13"
  8261 + resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.13.tgz#89cf5dd1566025f441132231d15ed621ef70ba96"
  8262 + integrity sha512-ZN6r5ITODu9NYAAbe1IGVUkNeamuleaXTLn5NMn/YZQ+5NSjDjysyVZVLkVOEOIw6bT2tLveyjsWlAZBVtfcPw==
8263 8263 dependencies:
8264   - "@intlify/core" "9.0.0-beta.12"
8265   - "@intlify/message-compiler" "9.0.0-beta.12"
8266   - "@intlify/message-resolver" "9.0.0-beta.12"
8267   - "@intlify/runtime" "9.0.0-beta.12"
8268   - "@intlify/shared" "9.0.0-beta.12"
  8264 + "@intlify/core-base" "9.0.0-beta.13"
  8265 + "@intlify/shared" "9.0.0-beta.13"
8269 8266 "@vue/devtools-api" "^6.0.0-beta.2"
8270 8267  
8271 8268 vue-router@^4.0.1:
... ...