wip: refactor layout

This commit is contained in:
vben
2020-11-23 23:24:13 +08:00
parent 234c1d1fae
commit ba068ba1df
79 changed files with 1393 additions and 1196 deletions

View File

@@ -1,7 +1,8 @@
import AppLocalPicker from './src/AppLocalPicker.vue';
import AppFooterToolbar from './src/AppFooterToolbar.vue';
import AppPageFooter from './src/AppPageFooter.vue';
import AppLogo from './src/AppLogo.vue';
import { withInstall } from '../util';
export { AppLocalPicker, AppFooterToolbar };
export { AppLocalPicker, AppPageFooter, AppLogo };
export default withInstall(AppLocalPicker, AppFooterToolbar);
export default withInstall(AppLocalPicker, AppPageFooter, AppLogo);

View File

@@ -1,60 +0,0 @@
<template>
<div class="app-footer" :style="{ width: getWidth }">
<div class="app-footer__left">
<slot name="left" />
</div>
<div class="app-footer__right">
<slot name="right" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, unref } from 'vue';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
import { appStore } from '/@/store/modules/app';
import { menuStore } from '/@/store/modules/menu';
export default defineComponent({
name: 'AppFooterToolbar',
setup() {
const getMiniWidth = computed(() => {
const {
menuSetting: { collapsedShowTitle },
} = appStore.getProjectConfig;
return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
});
const getWidth = computed(() => {
const { getCollapsedState, getMenuWidthState } = menuStore;
const width = getCollapsedState ? unref(getMiniWidth) : getMenuWidthState;
return `calc(100% - ${width}px)`;
});
return { getWidth };
},
});
</script>
<style lang="less" scoped>
.app-footer {
position: fixed;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
width: 100%;
align-items: center;
padding: 0 24px;
line-height: 44px;
background: #fff;
border-top: 1px solid #f0f0f0;
box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
0 -12px 48px 16px rgba(0, 0, 0, 0.03);
transition: width 0.3s;
&__left {
flex: 1 1;
}
}
</style>

View File

@@ -5,29 +5,44 @@
:selectedKeys="selectedKeys"
@menuEvent="handleMenuEvent"
>
<GlobalOutlined class="app-locale" />
<span class="app-local-picker">
<GlobalOutlined class="app-local-picker__icon" />
<span v-if="showText">{{ getLangText }}</span>
</span>
</Dropdown>
</template>
<script lang="ts">
import { defineComponent, ref, watchEffect, unref } from 'vue';
import { defineComponent, ref, watchEffect, unref, computed } from 'vue';
import { Dropdown, DropMenu } from '/@/components/Dropdown';
import { GlobalOutlined } from '@ant-design/icons-vue';
import { useLocale } from '/@/hooks/web/useLocale';
import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
import { LocaleType } from '/@/locales/types';
export default defineComponent({
name: 'AppLocalPicker',
components: { GlobalOutlined, Dropdown },
props: {
showText: {
type: Boolean,
default: true,
},
},
setup() {
const { localeList } = useLocaleSetting();
const selectedKeys = ref<string[]>([]);
const { changeLocale, getLang } = useLocale();
const getLangText = computed(() => {
const key = selectedKeys.value[0];
if (!key) return '';
return localeList.find((item) => item.event === key)?.text;
});
watchEffect(() => {
selectedKeys.value = [unref(getLang)];
});
@@ -41,13 +56,19 @@
toggleLocale(menu.event as string);
}
return { localeList, handleMenuEvent, selectedKeys };
return { localeList, handleMenuEvent, selectedKeys, getLangText };
},
});
</script>
<style lang="less" scoped>
.app-locale {
.app-local-picker {
display: flex;
align-items: center;
cursor: pointer;
&__icon {
margin-right: 4px;
}
}
</style>

View File

@@ -0,0 +1,72 @@
<template>
<div class="app-logo anticon" :class="theme" @click="handleGoHome">
<img src="/@/assets/images/logo.png" />
<div class="app-logo__title ml-2 ellipsis">{{ globSetting.title }}</div>
</div>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { useGlobSetting } from '/@/hooks/setting';
import { useGo } from '/@/hooks/web/usePage';
import { PageEnum } from '/@/enums/pageEnum';
export default defineComponent({
name: 'AppLogo',
props: {
/**
* The theme of the current parent component
*/
theme: {
type: String as PropType<string>,
},
},
setup() {
const globSetting = useGlobSetting();
const go = useGo();
function handleGoHome(): void {
go(PageEnum.BASE_HOME);
}
return {
handleGoHome,
globSetting,
};
},
});
</script>
<style lang="less" scoped>
@import (reference) '../../../design/index.less';
.app-logo {
display: flex;
align-items: center;
padding-left: 16px;
cursor: pointer;
&.light {
border-bottom: 1px solid @border-color-base;
}
&.light &__title {
color: @primary-color;
}
&.dark &__title {
color: @white;
}
&__title {
font-size: 18px;
font-weight: 700;
opacity: 0;
transition: all 0.5s;
.respond-to(medium,{
opacity: 1;
});
}
}
</style>

View File

@@ -0,0 +1,46 @@
<template>
<div class="app-footer" :style="{ width: getCalcContentWidth }">
<div class="app-footer__left">
<slot name="left" />
</div>
<div class="app-footer__right">
<slot name="right" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
export default defineComponent({
name: 'AppFooterToolbar',
setup() {
const { getCalcContentWidth } = useMenuSetting();
return { getCalcContentWidth };
},
});
</script>
<style lang="less" scoped>
.app-footer {
position: fixed;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
width: 100%;
align-items: center;
padding: 0 24px;
line-height: 44px;
background: #fff;
border-top: 1px solid #f0f0f0;
box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
0 -12px 48px 16px rgba(0, 0, 0, 0.03);
transition: width 0.4s;
&__left {
flex: 1 1;
}
}
</style>

View File

@@ -1,16 +1,15 @@
<!--
* @Author: Vben
* @Description:Access control component for fine-grained access control.
Access control component for fine-grained access control.
-->
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, computed, unref } from 'vue';
import { defineComponent, unref } from 'vue';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { RoleEnum } from '/@/enums/roleEnum';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { usePermission } from '/@/hooks/web/usePermission';
import { appStore } from '/@/store/modules/app';
import { getSlot } from '/@/utils/helper/tsxHelper';
@@ -29,9 +28,8 @@
},
},
setup(props, { slots }) {
const getModeRef = computed(() => {
return appStore.getProjectConfig.permissionMode;
});
const { getPermissionMode } = useRootSetting();
const { hasPermission } = usePermission();
/**
* Render role button
@@ -41,7 +39,6 @@
if (!value) {
return getSlot(slots);
}
const { hasPermission } = usePermission();
return hasPermission(value) ? getSlot(slots) : null;
}
@@ -52,12 +49,11 @@
if (!value) {
return getSlot(slots);
}
const { hasPermission } = usePermission();
return hasPermission(value) ? getSlot(slots) : null;
}
return () => {
const mode = unref(getModeRef);
const mode = unref(getPermissionMode);
// Role-based value control
if (mode === PermissionModeEnum.ROLE) {
return renderRoleAuth();

View File

@@ -33,7 +33,7 @@ export default defineComponent({
});
/**
* @description: Whether to use title
* @description: Whether to setting title
*/
const useWrapper = computed(() => {
return !!unref(getMergeProps).title;

View File

@@ -15,7 +15,7 @@ export default defineComponent({
const getMenuList = computed(() => props.dropMenuList);
function handleClickMenu({ key }: any) {
const menu = unref(getMenuList).find((item) => item.event === key);
const menu = unref(getMenuList).find((item) => `${item.event}` === `${key}`);
emit('menuEvent', menu);
}

View File

@@ -104,7 +104,7 @@
*/
function handleInputClick(e: Event) {
const files = e && (e.target as HTMLInputElement).files;
const rawFile = files && files[0]; // only use files[0]
const rawFile = files && files[0]; // only setting files[0]
if (!rawFile) return;
upload(rawFile);
}

View File

@@ -2,7 +2,7 @@ import { Component } from 'vue';
import type { ComponentType } from './types/index';
/**
* Component list, register here to use it in the form
* Component list, register here to setting it in the form
*/
import {
Input,

View File

@@ -31,7 +31,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
wrapperCol: globWrapperCol,
} = unref(propsRef) as any;
// If labelWidth is set globally, all items use
// If labelWidth is set globally, all items setting
if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
return { labelCol, wrapperCol };
}

View File

@@ -68,7 +68,7 @@ export interface FormItem {
*/
labelAlign?: 'left' | 'right';
/**
* a key of model. In the use of validate and resetFields method, the attribute is required
* a key of model. In the setting of validate and resetFields method, the attribute is required
*/
name?: NamePath;
/**
@@ -76,7 +76,7 @@ export interface FormItem {
*/
rules?: object | object[];
/**
* Whether to automatically associate form fields. In most cases, you can use automatic association.
* Whether to automatically associate form fields. In most cases, you can setting automatic association.
* If the conditions for automatic association are not met, you can manually associate them. See the notes below.
*/
autoLink?: boolean;

View File

@@ -1 +1,5 @@
export { default as BasicMenu } from './src/BasicMenu';
import BasicMenu from './src/BasicMenu';
import { withInstall } from '../util';
export default withInstall(BasicMenu);
export { BasicMenu };

View File

@@ -1,3 +1,5 @@
import './index.less';
import type { MenuState } from './types';
import type { Menu as MenuType } from '/@/router/types';
@@ -9,11 +11,10 @@ import MenuContent from './MenuContent';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum';
import { menuStore } from '/@/store/modules/menu';
import { appStore } from '/@/store/modules/app';
import { useSearchInput } from './useSearchInput';
import { useOpenKeys } from './useOpenKeys';
import { useSearchInput } from './hooks/useSearchInput';
import { useOpenKeys } from './hooks/useOpenKeys';
import { useRouter } from 'vue-router';
import { isFunction } from '/@/utils/is';
@@ -23,7 +24,7 @@ import { menuHasChildren } from './helper';
import { getCurrentParentPath } from '/@/router/menus';
import { basicProps } from './props';
import './index.less';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
export default defineComponent({
name: 'BasicMenu',
props: basicProps,
@@ -39,6 +40,8 @@ export default defineComponent({
selectedKeys: [],
collapsedOpenKeys: [],
});
const { getCollapsed } = useMenuSetting();
const { currentRoute } = useRouter();
const { items, flatItems, isAppMenu, mode, accordion } = toRefs(props);
@@ -61,7 +64,7 @@ export default defineComponent({
const getOpenKeys = computed(() => {
if (props.isAppMenu) {
return menuStore.getCollapsedState ? menuState.collapsedOpenKeys : menuState.openKeys;
return unref(getCollapsed) ? menuState.collapsedOpenKeys : menuState.openKeys;
}
return menuState.openKeys;
});
@@ -95,20 +98,20 @@ export default defineComponent({
cls.push('basic-menu__sidebar-hor');
}
if (!props.isTop && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) {
if (!props.isHorizontal && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) {
cls.push('basic-menu__second');
}
return cls;
});
const showTitle = computed(() => props.collapsedShowTitle && menuStore.getCollapsedState);
const showTitle = computed(() => props.collapsedShowTitle && unref(getCollapsed));
watch(
() => currentRoute.value.name,
(name: string) => {
if (name === 'Redirect') return;
handleMenuChange();
props.isTop && appStore.getProjectConfig.menuSetting.split && getParentPath();
props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath();
}
);
@@ -180,7 +183,7 @@ export default defineComponent({
<MenuContent
item={menu}
level={index}
isTop={props.isTop}
isHorizontal={props.isHorizontal}
showTitle={unref(showTitle)}
searchValue={menuState.searchValue}
/>,
@@ -196,7 +199,7 @@ export default defineComponent({
showTitle={unref(showTitle)}
item={menu}
level={index}
isTop={props.isTop}
isHorizontal={props.isHorizontal}
searchValue={menuState.searchValue}
/>,
],
@@ -214,7 +217,7 @@ export default defineComponent({
const inlineCollapsedObj = isInline
? props.isAppMenu
? {
inlineCollapsed: menuStore.getCollapsedState,
inlineCollapsed: unref(getCollapsed),
}
: { inlineCollapsed: props.inlineCollapsed }
: {};
@@ -246,7 +249,6 @@ export default defineComponent({
});
return () => {
const { getCollapsedState } = menuStore;
const { mode } = props;
return mode === MenuModeEnum.HORIZONTAL ? (
renderMenu()
@@ -258,7 +260,7 @@ export default defineComponent({
theme={props.theme as ThemeEnum}
onChange={handleInputChange}
onClick={handleInputClick}
collapsed={getCollapsedState}
collapsed={unref(getCollapsed)}
/>
<section style={unref(getMenuWrapStyle)} class="basic-menu__content">
{renderMenu()}

View File

@@ -26,7 +26,7 @@ export default defineComponent({
type: Number as PropType<number>,
default: 0,
},
isTop: {
isHorizontal: {
type: Boolean as PropType<boolean>,
default: true,
},
@@ -40,8 +40,8 @@ export default defineComponent({
}
function renderTag() {
const { item, showTitle, isTop } = props;
if (!item || showTitle || isTop) return null;
const { item, showTitle, isHorizontal } = props;
if (!item || showTitle || isHorizontal) return null;
const { tag } = item;
if (!tag) return null;

View File

@@ -1,12 +1,12 @@
import { MenuModeEnum } from '/@/enums/menuEnum';
import type { Menu as MenuType } from '/@/router/types';
import type { MenuState } from './types';
import type { MenuState } from '../types';
import type { Ref } from 'vue';
import { unref } from 'vue';
import { menuStore } from '/@/store/modules/menu';
import { getAllParentPath } from '/@/utils/helper/menuHelper';
import { es6Unique } from '/@/utils';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
export function useOpenKeys(
menuState: MenuState,
@@ -16,6 +16,7 @@ export function useOpenKeys(
mode: Ref<MenuModeEnum>,
accordion: Ref<boolean>
) {
const { getCollapsed } = useMenuSetting();
/**
* @description:
*/
@@ -49,7 +50,7 @@ export function useOpenKeys(
rootSubMenuKeys.push(path);
}
}
if (!menuStore.getCollapsedState || !unref(isAppMenu)) {
if (!unref(getCollapsed) || !unref(isAppMenu)) {
const latestOpenKey = openKeys.find((key) => menuState.openKeys.indexOf(key) === -1);
if (rootSubMenuKeys.indexOf(latestOpenKey as string) === -1) {
menuState.openKeys = openKeys;

View File

@@ -1,5 +1,5 @@
import type { Menu as MenuType } from '/@/router/types';
import type { MenuState } from './types';
import type { MenuState } from '../types';
import type { Ref } from 'vue';
import { isString } from '/@/utils/is';

View File

@@ -8,6 +8,10 @@ export const basicProps = {
type: Array as PropType<Menu[]>,
default: () => [],
},
flatItems: {
type: Array as PropType<Menu[]>,
default: () => [],
},
appendClass: {
type: Boolean as PropType<boolean>,
default: false,
@@ -16,10 +20,6 @@ export const basicProps = {
type: Boolean as PropType<boolean>,
default: false,
},
flatItems: {
type: Array as PropType<Menu[]>,
default: () => [],
},
// 是否显示搜索框
search: {
type: Boolean as PropType<boolean>,
@@ -55,7 +55,7 @@ export const basicProps = {
type: Boolean as PropType<boolean>,
default: true,
},
isTop: {
isHorizontal: {
type: Boolean as PropType<boolean>,
default: false,
},

View File

@@ -35,7 +35,7 @@ export const basicProps = Object.assign({}, modalProps, {
},
// Warm reminder message
helpMessage: [String, Array] as PropType<string | string[]>,
// Whether to use wrapper
// Whether to setting wrapper
useWrapper: {
type: Boolean as PropType<boolean>,
default: true,

View File

@@ -190,7 +190,7 @@ export interface ColumnProps<T> {
onFilterDropdownVisibleChange?: (visible: boolean) => void;
/**
* When using columns, you can use this property to configure the properties that support the slot,
* When using columns, you can setting this property to configure the properties that support the slot,
* such as slots: { filterIcon: 'XXX'}
* @type object
*/

View File

@@ -86,7 +86,7 @@ export interface PaginationProps {
size?: string;
/**
* whether to use simple mode
* whether to setting simple mode
* @type boolean
*/
simple?: boolean;

View File

@@ -1,4 +1,4 @@
// Any plugins you want to use has to be imported
// Any plugins you want to setting has to be imported
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/
// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration

View File

@@ -31,6 +31,7 @@ import {
PageHeader,
Result,
Empty,
Avatar,
} from 'ant-design-vue';
import { getApp } from '/@/setup/App';
@@ -76,5 +77,6 @@ export function registerGlobComp() {
.use(PageHeader)
.use(Result)
.use(Empty)
.use(Avatar)
.use(Tabs);
}

3
src/components/types.ts Normal file
View File

@@ -0,0 +1,3 @@
import { defineComponent } from 'vue';
export type Component = ReturnType<typeof defineComponent>;