feat(projects): 四种基本布局完成

This commit is contained in:
Soybean 2021-09-09 00:08:09 +08:00
parent 6b7f4cfccb
commit 86d4a207ee
20 changed files with 203 additions and 216 deletions

View File

@ -1,140 +0,0 @@
提交规范
前面我们已经统一代码规范,并且在提交代码时进行强约束来保证仓库代码质量。多人协作的项目中,在提交代码这个环节,也存在一种情况:不能保证每个人对提交信息的准确描述,因此会出现提交信息紊乱、风格不一致的情况。
如果 git commit 的描述信息精准,在后期维护和 Bug 处理时会变得有据可查,项目开发周期内还可以根据规范的提交信息快速生成开发日志,从而方便我们追踪项目和把控进度。
这里,我们使用社区最流行、最知名、最受认可的 Angular 团队提交规范。
commit message 格式规范
commit message 由 Header、Body、Footer 组成。
<Header>
<Body>
<Footer>
复制代码
Header
Header 部分包括三个字段 type必需、scope可选和 subject必需
<type>(<scope>): <subject>
复制代码
type
type 用于说明 commit 的提交类型(必须是以下几种之一)。
值描述feat新增一个功能fix修复一个 Bugdocs文档变更style代码格式不影响功能例如空格、分号等格式修正refactor代码重构perf改善性能test测试build变更项目构建或外部依赖例如 scopes: webpack、gulp、npm 等ci更改持续集成软件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等chore变更构建流程或辅助工具revert代码回退
scope
scope 用于指定本次 commit 影响的范围。scope 依据项目而定例如在业务项目中可以依据菜单或者功能模块划分如果是组件库开发则可以依据组件划分。scope 可省略)
subject
subject 是本次 commit 的简洁描述,长度约定在 50 个字符以内,通常遵循以下几个规范:
用动词开头第一人称现在时表述例如change 代替 changed 或 changes
第一个字母小写
结尾不加句号(.
Body
body 是对本次 commit 的详细描述可以分成多行。body 可省略)
跟 subject 类似用动词开头body 应该说明修改的原因和更改前后的行为对比。
Footer
如果本次提交的代码是突破性的变更或关闭缺陷,则 Footer 必需,否则可以省略。
突破性的变更
当前代码与上一个版本有突破性改变,则 Footer 以 BREAKING CHANGE 开头,后面是对变动的描述、以及变动的理由。
关闭缺陷
如果当前提交是针对特定的 issue那么可以在 Footer 部分填写需要关闭的单个 issue 或一系列 issues。
参考例子
feat
feat(browser): onUrlChange event (popstate/hashchange/polling)
Added new event to browser:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available
Breaks $browser.onHashChange, which was removed (use onUrlChange instead)
复制代码
fix
fix(compile): couple of unit tests for IE9
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
Closes #392
Breaks foo.bar api, foo.baz should be used instead
复制代码
style
style(location): add couple of missing semi colons
复制代码
chore
chore(release): v3.4.2
复制代码
规范 commit message 的好处
首行就是简洁实用的关键信息,方便在 git history 中快速浏览。
具有更加详细的 body 和 footer可以清晰的看出某次提交的目的和影响。
可以通过 type 过滤出想要查找的信息,也可以通过关键字快速查找相关提交。
可以直接从 commit 生成 change log。

View File

@ -1,5 +1,11 @@
<template>
<n-config-provider :locale="zhCN" :date-locale="dateZhCN" :theme="dark" :theme-overrides="theme.themeOverrids">
<n-config-provider
class="h-full"
:locale="zhCN"
:date-locale="dateZhCN"
:theme="dark"
:theme-overrides="theme.themeOverrids"
>
<n-loading-bar-provider>
<n-dialog-provider>
<n-notification-provider>

View File

@ -59,6 +59,8 @@ interface HeaderStyle {
interface MenuStyle {
/** 菜单宽度 */
width: number;
/** 混合菜单的宽度 */
mixWidth: number;
/** 菜单折叠时的宽度 */
collapsedWidth: number;
/** 固定菜单 */

View File

@ -0,0 +1,10 @@
<template>
<n-layout-footer>
<h3 class="flex-center h-48px text-18px text-error">页脚</h3>
</n-layout-footer>
</template>
<script lang="ts" setup>
import { NLayoutFooter } from 'naive-ui';
</script>
<style scoped></style>

View File

@ -1,25 +1,68 @@
<template>
<div class="global-header flex-y-center justify-end">
<header-item class="w-40px h-full" @click="toggle">
<icon-gridicons-fullscreen-exit v-if="isFullscreen" class="text-16px" />
<icon-gridicons-fullscreen v-else class="text-16px" />
</header-item>
<header-item class="w-40px h-full" @click="openSettingDrawer">
<icon-mdi-light-cog class="text-16px" />
</header-item>
</div>
<header v-if="fixedHeader && theme.navStyle.mode !== 'horizontal-mix'" class="w-full header-height"></header>
<n-layout-header :inverted="headerInverted" :position="position" :style="{ zIndex }">
<div class="global-header header-height flex-y-center w-full">
<div v-if="!theme.isVerticalNav" class="menu-width h-full">
<global-logo />
</div>
<div class="flex-1 flex justify-end h-full">
<header-item class="w-40px h-full" @click="toggle">
<icon-gridicons-fullscreen-exit v-if="isFullscreen" class="text-16px" />
<icon-gridicons-fullscreen v-else class="text-16px" />
</header-item>
<header-item class="w-40px h-full" @click="openSettingDrawer">
<icon-mdi-light-cog class="text-16px" />
</header-item>
</div>
</div>
</n-layout-header>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { NLayoutHeader } from 'naive-ui';
import { useFullscreen } from '@vueuse/core';
import { useAppStore } from '@/store';
import { useAppStore, useThemeStore } from '@/store';
import { HeaderItem } from './components';
import { GlobalLogo } from '../common';
defineProps({
zIndex: {
type: Number,
default: 0
}
});
const { openSettingDrawer } = useAppStore();
const theme = useThemeStore();
const { isFullscreen, toggle } = useFullscreen();
const inverted = computed(() => {
return theme.navStyle.theme !== 'light';
});
const fixedHeader = computed(() => theme.headerStyle.fixed || theme.navStyle.mode === 'horizontal-mix');
const position = computed(() => (fixedHeader.value ? 'absolute' : 'static'));
const headerInverted = computed(() => {
return theme.navStyle.theme !== 'dark' ? inverted.value : !inverted.value;
});
const headerHeight = computed(() => {
const { height } = theme.headerStyle;
return `${height}px`;
});
const menuWidth = computed(() => {
const { width } = theme.menuStyle;
return `${width}px`;
});
</script>
<style scoped>
.global-header {
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
}
.header-height {
height: v-bind(headerHeight);
}
.menu-width {
width: v-bind(menuWidth);
}
</style>

View File

@ -0,0 +1,52 @@
<template>
<n-layout-sider
class="sider-shadow h-full"
:style="{ zIndex }"
:native-scrollbar="false"
:inverted="inverted"
collapse-mode="width"
:collapsed="app.menu.collapsed"
:collapsed-width="theme.menuStyle.collapsedWidth"
:width="menuWidth"
@collapse="handleMenuCollapse(true)"
@expand="handleMenuCollapse(false)"
>
<global-logo v-if="theme.isVerticalNav" />
<global-menu />
</n-layout-sider>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { NLayoutSider } from 'naive-ui';
import { useThemeStore, useAppStore } from '@/store';
import { GlobalLogo, GlobalMenu } from '../common';
defineProps({
zIndex: {
type: Number,
default: 0
}
});
const theme = useThemeStore();
const app = useAppStore();
const { handleMenuCollapse } = useAppStore();
const inverted = computed(() => {
return theme.navStyle.theme !== 'light';
});
const menuWidth = computed(() => {
const { collapsed } = app.menu;
const { mode } = theme.navStyle;
const { collapsedWidth, width, mixWidth } = theme.menuStyle;
const modeWidth = mode === 'vertical-mix' ? mixWidth : width;
return collapsed ? collapsedWidth : modeWidth;
});
</script>
<style scoped>
.sider-shadow {
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
}
</style>

View File

@ -5,7 +5,7 @@
<n-switch :value="theme.menuStyle.splitMenu" @update:value="handleSplitMenu" />
</setting-menu-item>
<setting-menu-item label="固定头部">
<n-switch :value="theme.headerStyle.fixed" @update:value="handleFixedHeader" />
<n-switch :value="splitMenu" :disabled="disabledSplitMenu" @update:value="handleFixedHeader" />
</setting-menu-item>
<setting-menu-item label="头部高度">
<n-input-number
@ -21,19 +21,40 @@
class="w-120px"
size="small"
:value="theme.menuStyle.width"
:disabled="disabledMenuWidth"
:step="10"
@update:value="handleMenuWidth"
/>
</setting-menu-item>
<setting-menu-item label="左侧混合菜单展开宽度">
<n-input-number
class="w-120px"
size="small"
:value="theme.menuStyle.mixWidth"
:disabled="disabledMixMenuWidth"
:step="5"
@update:value="handleMixMenuWidth"
/>
</setting-menu-item>
</n-space>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { NDivider, NSpace, NSwitch, NInputNumber } from 'naive-ui';
import { useThemeStore } from '@/store';
import { SettingMenuItem } from '../common';
const theme = useThemeStore();
const { handleSplitMenu, handleFixedHeader, handleHeaderHeight, handleMenuWidth } = useThemeStore();
const { handleSplitMenu, handleFixedHeader, handleHeaderHeight, handleMenuWidth, handleMixMenuWidth } = useThemeStore();
const disabledSplitMenu = computed(() => theme.navStyle.mode === 'horizontal-mix');
const splitMenu = computed(() => theme.headerStyle.fixed || disabledSplitMenu.value);
const disabledMenuWidth = computed(() => {
const { mode } = theme.navStyle;
return mode !== 'vertical' && mode !== 'horizontal-mix';
});
const disabledMixMenuWidth = computed(() => theme.navStyle.mode !== 'vertical-mix');
</script>
<style scoped></style>

View File

@ -7,10 +7,11 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { useAppStore } from '@/store';
import { useAppStore, useThemeStore } from '@/store';
const app = useAppStore();
const showTitle = computed(() => !app.menu.collapsed);
const theme = useThemeStore();
const showTitle = computed(() => !app.menu.collapsed && theme.navStyle.mode !== 'vertical-mix');
const title = import.meta.env.VITE_APP_TITLE as string;
</script>
<style scoped></style>

View File

@ -1,5 +1,7 @@
<template>
<div></div>
<div>
<h3 class="text-center text-18px text-error">菜单</h3>
</div>
</template>
<script lang="ts" setup></script>

View File

@ -0,0 +1,4 @@
import GlobalLogo from './GlobalLogo.vue';
import GlobalMenu from './GlobalMenu.vue';
export { GlobalLogo, GlobalMenu };

View File

@ -1,6 +1,6 @@
import GlobalSider from './GlobalSider/index.vue';
import GlobalHeader from './GlobalHeader/index.vue';
import GlobalLogo from './GlobalLogo/index.vue';
import GlobalMenu from './GlobalMenu/index.vue';
import GlobalFooter from './GlobalFooter/index.vue';
import SettingDrawer from './SettingDrawer/index.vue';
export { GlobalHeader, GlobalLogo, GlobalMenu, SettingDrawer };
export { GlobalSider, GlobalHeader, GlobalFooter, SettingDrawer };

View File

@ -1,67 +1,44 @@
<template>
<n-layout has-sider :position="position">
<n-layout-sider
class="layout-sider min-h-100vh z-11"
:native-scrollbar="false"
:inverted="inverted"
collapse-mode="width"
:collapsed="app.menu.collapsed"
:collapsed-width="theme.menuStyle.collapsedWidth"
:width="menuWidth"
@collapse="handleMenuCollapse(true)"
@expand="handleMenuCollapse(false)"
>
<global-logo />
<global-menu />
</n-layout-sider>
<n-layout :inverted="inverted">
<n-layout-header :position="position" :inverted="headerInverted" class="z-10">
<global-header class="header-height" />
</n-layout-header>
<n-layout-content class="main-padding flex-auto min-h-100vh">
<router-view />
</n-layout-content>
<n-layout-footer></n-layout-footer>
</n-layout>
<n-layout class="h-full" has-sider>
<global-sider v-if="theme.isVerticalNav" :z-index="2" />
<global-header v-if="isHorizontalMix" :z-index="2" />
<div class="flex-1-hidden flex h-full">
<global-sider v-if="isHorizontalMix" class="sider-margin" :z-index="1" />
<n-scrollbar class="h-full" :x-scrollable="true">
<div class="inline-flex-col-stretch w-full min-h-100vh" :class="{ 'content-padding': isHorizontalMix }">
<global-header v-if="!isHorizontalMix" :z-index="1" />
<n-layout-content class="flex-auto" :class="{ 'bg-[#f5f7f9]': !theme.darkMode }">
<router-view />
</n-layout-content>
<global-footer />
</div>
</n-scrollbar>
</div>
<setting-drawer />
</n-layout>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { NLayout, NLayoutSider, NLayoutHeader, NLayoutContent, NLayoutFooter } from 'naive-ui';
import { useThemeStore, useAppStore } from '@/store';
import { GlobalHeader, GlobalLogo, GlobalMenu, SettingDrawer } from './components';
import { NLayout, NScrollbar, NLayoutContent } from 'naive-ui';
import { useThemeStore } from '@/store';
import { GlobalSider, GlobalHeader, GlobalFooter, SettingDrawer } from './components';
const theme = useThemeStore();
const app = useAppStore();
const { handleMenuCollapse } = useAppStore();
const position = computed(() => (theme.headerStyle.fixed ? 'absolute' : 'static'));
const menuWidth = computed(() => {
const { collapsed } = app.menu;
const { collapsedWidth, width } = theme.menuStyle;
return collapsed ? collapsedWidth : width;
});
const inverted = computed(() => {
return theme.navStyle.theme !== 'light';
});
const headerInverted = computed(() => {
return theme.navStyle.theme !== 'dark' ? inverted.value : !inverted.value;
});
const isHorizontalMix = computed(() => theme.navStyle.mode === 'horizontal-mix');
const headerHeight = computed(() => {
const { height } = theme.headerStyle;
return `${height}px`;
});
</script>
<style scoped>
.layout-sider {
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
:deep(.n-scrollbar-rail) {
z-index: 11;
}
.header-height {
height: v-bind(headerHeight);
.sider-margin {
margin-top: v-bind(headerHeight);
}
.main-padding {
.content-padding {
padding-top: v-bind(headerHeight);
}
</style>

View File

@ -1,2 +0,0 @@
/** 请求超时时间 */
export const REQUEST_TIMEOUT = 15 * 1000;

View File

@ -1,7 +1,5 @@
import { createRequest } from './request';
import { REQUEST_TIMEOUT } from './config';
export const adminRequest = createRequest({
baseURL: import.meta.env.VITE_HTTP_URL_EMOSS_ADMIN as string,
timeout: REQUEST_TIMEOUT
baseURL: import.meta.env.VITE_HTTP_URL_EMOSS_ADMIN as string
});

View File

@ -28,6 +28,8 @@ export function handleResponse<T>(handleFunc: HandleFunc<T>, ...requests: Reques
/**
*
* @param file -
* @param key -
*/
export async function transformFile(file: File[] | File, key: string) {
const formData = new FormData();

View File

@ -1,5 +1,5 @@
import type { ThemeSettings } from '../../interface';
import { EnumAnimate } from '../../enum';
import type { ThemeSettings } from '../interface';
import { EnumAnimate } from '../enum';
const themeColorList = [
'#409EFF',
@ -38,6 +38,7 @@ const themeSettings: ThemeSettings = {
},
menuStyle: {
width: 200,
mixWidth: 80,
collapsedWidth: 64,
fixed: true,
splitMenu: false

View File

@ -50,6 +50,10 @@ const themeStore = defineStore({
colorLoading
}
};
},
isVerticalNav(): boolean {
const { mode } = this.navStyle;
return mode === 'vertical' || mode === 'vertical-mix';
}
},
actions: {
@ -75,6 +79,12 @@ const themeStore = defineStore({
this.menuStyle.width = width;
}
},
/** 更改混合菜单展开的宽度 */
handleMixMenuWidth(width: number | null) {
if (width !== null) {
this.menuStyle.mixWidth = width;
}
},
/** 更改头部的高度(不包含tab标签) */
handleHeaderHeight(height: number | null) {
if (height !== null) {

View File

@ -7,8 +7,6 @@ body,
height: 100%;
}
html {
min-width: 1320px;
min-height: 650px;
font-size: 16px;
}
#app {

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="min-h-500px">
<n-space>
<n-button v-for="item in actions" :key="item.key" type="primary" @click="handleClick(item.key)">
{{ item.label }}
@ -21,6 +21,7 @@
<span class="text-warning">warning</span>
<span class="text-error">error</span>
</div>
<p v-for="i in 100" :key="i">{{ i }}</p>
</div>
</template>

View File

@ -18,6 +18,7 @@ export default defineConfig({
'inline-flex-y-center': 'inline-flex items-center',
'flex-1-hidden': 'flex-1 overflow-hidden',
'flex-col-stretch': 'flex flex-col items-stretch',
'inline-flex-col-stretch': 'flex flex-col items-stretch',
'absolute-center': 'absolute left-0 top-0 flex justify-center items-center w-full h-full',
'absolute-lt': 'absolute left-0 top-0',
'absolute-lb': 'absolute left-0 bottom-0',