79 changed files with 756 additions and 471 deletions
@ -1,2 +1,4 @@ |
|||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
||||
export const PageFooter = createAsyncComponent(() => import('./src/PageFooter.vue')); |
export const PageFooter = createAsyncComponent(() => import('./src/PageFooter.vue')); |
||||
|
|
||||
|
export { default as PageWrapper } from './src/PageWrapper.vue'; |
||||
|
|||||
@ -0,0 +1,153 @@ |
|||||
|
<template> |
||||
|
<div :class="getClass"> |
||||
|
<PageHeader :ghost="ghost" v-bind="$attrs" ref="headerRef"> |
||||
|
<template #default> |
||||
|
<template v-if="content"> |
||||
|
{{ content }} |
||||
|
</template> |
||||
|
<slot name="headerContent" v-else /> |
||||
|
</template> |
||||
|
<template #[item]="data" v-for="item in getHeaderSlots"> |
||||
|
<slot :name="item" v-bind="data" /> |
||||
|
</template> |
||||
|
</PageHeader> |
||||
|
<div :class="[`${prefixCls}-content`, $attrs.contentClass]" :style="getContentStyle"> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
<PageFooter v-if="getShowFooter" ref="footerRef"> |
||||
|
<template #left> |
||||
|
<slot name="leftFooter" /> |
||||
|
</template> |
||||
|
<template #right> |
||||
|
<slot name="rightFooter" /> |
||||
|
</template> |
||||
|
</PageFooter> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import type { CSSProperties, PropType } from 'vue'; |
||||
|
|
||||
|
import { defineComponent, computed, watch, nextTick, ref, unref } from 'vue'; |
||||
|
import PageFooter from './PageFooter.vue'; |
||||
|
import { usePageContext } from '/@/hooks/component/usePageContext'; |
||||
|
|
||||
|
import { useDesign } from '/@/hooks/web/useDesign'; |
||||
|
import { propTypes } from '/@/utils/propTypes'; |
||||
|
import { omit } from 'lodash-es'; |
||||
|
import { PageHeader } from 'ant-design-vue'; |
||||
|
export default defineComponent({ |
||||
|
name: 'PageWrapper', |
||||
|
components: { PageFooter, PageHeader }, |
||||
|
props: { |
||||
|
dense: propTypes.bool, |
||||
|
ghost: propTypes.bool, |
||||
|
content: propTypes.string, |
||||
|
contentStyle: { |
||||
|
type: Object as PropType<CSSProperties>, |
||||
|
}, |
||||
|
contentBackgrond: propTypes.bool, |
||||
|
contentFullHeight: propTypes.bool, |
||||
|
}, |
||||
|
setup(props, { slots }) { |
||||
|
const headerRef = ref<ComponentRef>(null); |
||||
|
const footerRef = ref<ComponentRef>(null); |
||||
|
const { prefixCls } = useDesign('page-wrapper'); |
||||
|
const { contentHeight, setPageHeight, pageHeight } = usePageContext(); |
||||
|
|
||||
|
const getClass = computed(() => { |
||||
|
return [ |
||||
|
prefixCls, |
||||
|
{ |
||||
|
[`${prefixCls}--dense`]: props.dense, |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter); |
||||
|
|
||||
|
const getHeaderSlots = computed(() => { |
||||
|
return Object.keys(omit(slots, 'default', 'leftFooter', 'rightFooter', 'headerContent')); |
||||
|
}); |
||||
|
|
||||
|
const getContentStyle = computed( |
||||
|
(): CSSProperties => { |
||||
|
const { contentBackgrond, contentFullHeight, contentStyle } = props; |
||||
|
const bg = contentBackgrond ? { backgroundColor: '#fff' } : {}; |
||||
|
if (!contentFullHeight) { |
||||
|
return { ...bg, ...contentStyle }; |
||||
|
} |
||||
|
return { |
||||
|
...bg, |
||||
|
...contentStyle, |
||||
|
minHeight: `${unref(pageHeight)}px`, |
||||
|
}; |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
watch( |
||||
|
() => contentHeight?.value, |
||||
|
(height) => { |
||||
|
if (!props.contentFullHeight) { |
||||
|
return; |
||||
|
} |
||||
|
nextTick(() => { |
||||
|
const footer = unref(footerRef); |
||||
|
const header = unref(headerRef); |
||||
|
let footetHeight = 0; |
||||
|
const footerEl = footer?.$el; |
||||
|
if (footerEl) { |
||||
|
footetHeight += footerEl?.offsetHeight ?? 0; |
||||
|
} |
||||
|
let headerHeight = 0; |
||||
|
const headerEl = header?.$el; |
||||
|
if (headerEl) { |
||||
|
headerHeight += headerEl?.offsetHeight ?? 0; |
||||
|
} |
||||
|
setPageHeight?.(height - footetHeight - headerHeight); |
||||
|
}); |
||||
|
}, |
||||
|
{ |
||||
|
immediate: true, |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
return { |
||||
|
getContentStyle, |
||||
|
footerRef, |
||||
|
headerRef, |
||||
|
getClass, |
||||
|
getHeaderSlots, |
||||
|
prefixCls, |
||||
|
getShowFooter, |
||||
|
pageHeight, |
||||
|
omit, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less"> |
||||
|
@prefix-cls: ~'@{namespace}-page-wrapper'; |
||||
|
|
||||
|
.@{prefix-cls} { |
||||
|
position: relative; |
||||
|
|
||||
|
.ant-page-header { |
||||
|
padding: 12px 16px; |
||||
|
|
||||
|
&:empty { |
||||
|
padding: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&-content { |
||||
|
// padding: 12px; |
||||
|
margin: 16px; |
||||
|
} |
||||
|
|
||||
|
&--dense { |
||||
|
.@{prefix-cls}-content { |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -1,2 +1,4 @@ |
|||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
export const BasicUpload = createAsyncComponent(() => import('./src/BasicUpload.vue')); |
// export const BasicUpload = createAsyncComponent(() => import('./src/BasicUpload.vue'));
|
||||
|
|
||||
|
export { default as BasicUpload } from './src/BasicUpload.vue'; |
||||
|
|||||
@ -0,0 +1,20 @@ |
|||||
|
import type { InjectionKey, ComputedRef, Ref } from 'vue'; |
||||
|
import { createContext, useContext } from '/@/hooks/core/useContext'; |
||||
|
|
||||
|
import {} from 'vue'; |
||||
|
|
||||
|
export interface PageContextProps { |
||||
|
contentHeight: ComputedRef<number>; |
||||
|
pageHeight: Ref<number>; |
||||
|
setPageHeight: (height: number) => Promise<void>; |
||||
|
} |
||||
|
|
||||
|
const key: InjectionKey<PageContextProps> = Symbol(); |
||||
|
|
||||
|
export function createPageContext(context: PageContextProps) { |
||||
|
return createContext<PageContextProps>(context, key, { native: true }); |
||||
|
} |
||||
|
|
||||
|
export function usePageContext() { |
||||
|
return useContext<PageContextProps>(key); |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
import type { InjectionKey, ComputedRef } from 'vue'; |
||||
|
import { createContext, useContext } from '/@/hooks/core/useContext'; |
||||
|
|
||||
|
import {} from 'vue'; |
||||
|
|
||||
|
export interface ContentContextProps { |
||||
|
contentHeight: ComputedRef<number>; |
||||
|
setPageHeight: (height: number) => Promise<void>; |
||||
|
} |
||||
|
|
||||
|
const key: InjectionKey<ContentContextProps> = Symbol(); |
||||
|
|
||||
|
export function createContentContext(context: ContentContextProps) { |
||||
|
return createContext<ContentContextProps>(context, key, { native: true }); |
||||
|
} |
||||
|
|
||||
|
export function useContentContext() { |
||||
|
return useContext<ContentContextProps>(key); |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
import { ref, computed, unref } from 'vue'; |
||||
|
import { createPageContext } from '/@/hooks/component/usePageContext'; |
||||
|
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; |
||||
|
export const headerHeightRef = ref(0); |
||||
|
|
||||
|
export function useContentViewHeight() { |
||||
|
const contentHeight = ref(window.innerHeight); |
||||
|
const pageHeight = ref(window.innerHeight); |
||||
|
const getViewHeight = computed(() => { |
||||
|
return unref(contentHeight) - unref(headerHeightRef) || 0; |
||||
|
}); |
||||
|
|
||||
|
useWindowSizeFn( |
||||
|
() => { |
||||
|
contentHeight.value = window.innerHeight; |
||||
|
}, |
||||
|
100, |
||||
|
{ immediate: true } |
||||
|
); |
||||
|
|
||||
|
async function setPageHeight(height: number) { |
||||
|
pageHeight.value = height; |
||||
|
} |
||||
|
|
||||
|
createPageContext({ |
||||
|
contentHeight: getViewHeight, |
||||
|
setPageHeight, |
||||
|
pageHeight, |
||||
|
}); |
||||
|
} |
||||
@ -1,10 +1,13 @@ |
|||||
<template> |
<template> |
||||
<div class="p-5"> |
<PageWrapper title="层级面包屑示例" content="子级页面面包屑会添加到当前层级后面"> |
||||
<router-link to="/feat/breadcrumb/children/childrenDetail">进入子级详情页</router-link> |
<router-link to="/feat/breadcrumb/children/childrenDetail">进入子级详情页</router-link> |
||||
</div> |
</PageWrapper> |
||||
</template> |
</template> |
||||
<script lang="ts"> |
<script lang="ts"> |
||||
import { defineComponent } from 'vue'; |
import { defineComponent } from 'vue'; |
||||
|
import { PageWrapper } from '/@/components/Page'; |
||||
|
|
||||
export default defineComponent({}); |
export default defineComponent({ |
||||
|
components: { PageWrapper }, |
||||
|
}); |
||||
</script> |
</script> |
||||
|
|||||
@ -1,10 +1,13 @@ |
|||||
<template> |
<template> |
||||
<div class="p-5"> |
<PageWrapper title="平级面包屑示例" content="子级页面面包屑会覆盖当前层级"> |
||||
<router-link to="/feat/breadcrumb/flatDetail">进入平级详情页</router-link> |
<router-link to="/feat/breadcrumb/flatDetail">进入平级详情页</router-link> |
||||
</div> |
</PageWrapper> |
||||
</template> |
</template> |
||||
<script lang="ts"> |
<script lang="ts"> |
||||
import { defineComponent } from 'vue'; |
import { defineComponent } from 'vue'; |
||||
|
import { PageWrapper } from '/@/components/Page'; |
||||
|
|
||||
export default defineComponent({}); |
export default defineComponent({ |
||||
|
components: { PageWrapper }, |
||||
|
}); |
||||
</script> |
</script> |
||||
|
|||||
Loading…
Reference in new issue