mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 19:29:04 +08:00
refactor: adjust all sample pages and use page components (#4118)
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, type CSSProperties, nextTick, ref, watchEffect } from 'vue';
|
||||
|
||||
import { VbenTooltip } from '@vben-core/shadcn-ui';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* 是否启用点击文本展开全部
|
||||
* @default false
|
||||
*/
|
||||
expand?: boolean;
|
||||
/**
|
||||
* 文本最大行数
|
||||
* @default 1
|
||||
*/
|
||||
line?: number;
|
||||
/**
|
||||
* 文本最大宽度
|
||||
* @default '100%'
|
||||
*/
|
||||
maxWidth?: number | string;
|
||||
/**
|
||||
* 提示框位置
|
||||
* @default 'top'
|
||||
*/
|
||||
placement?: 'bottom' | 'left' | 'right' | 'top';
|
||||
/**
|
||||
* 是否启用文本提示框
|
||||
* @default true
|
||||
*/
|
||||
tooltip?: boolean;
|
||||
/**
|
||||
* 提示框背景颜色,优先级高于 overlayStyle
|
||||
*/
|
||||
tooltipBackgroundColor?: string;
|
||||
/**
|
||||
* 提示文本字体颜色,优先级高于 overlayStyle
|
||||
*/
|
||||
tooltipColor?: string;
|
||||
/**
|
||||
* 提示文本字体大小,单位px,优先级高于 overlayStyle
|
||||
*/
|
||||
tooltipFontSize?: number;
|
||||
/**
|
||||
* 提示框内容最大宽度,单位px,默认不设置时,提示文本内容自动与展示文本宽度保持一致
|
||||
*/
|
||||
tooltipMaxWidth?: number;
|
||||
/**
|
||||
* 提示框内容区域样式
|
||||
* @default { textAlign: 'justify' }
|
||||
*/
|
||||
tooltipOverlayStyle?: CSSProperties;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
expand: false,
|
||||
line: 1,
|
||||
maxWidth: '100%',
|
||||
placement: 'top',
|
||||
tooltip: true,
|
||||
tooltipBackgroundColor: '',
|
||||
tooltipColor: '',
|
||||
tooltipFontSize: 14,
|
||||
tooltipMaxWidth: undefined,
|
||||
tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
|
||||
});
|
||||
const emit = defineEmits<{ expandChange: [boolean] }>();
|
||||
|
||||
const textMaxWidth = computed(() => {
|
||||
if (typeof props.maxWidth === 'number') {
|
||||
return `${props.maxWidth}px`;
|
||||
}
|
||||
return props.maxWidth;
|
||||
});
|
||||
const showTooltip = ref(false);
|
||||
const ellipsis = ref();
|
||||
const defaultTooltipMaxWidth = ref();
|
||||
watchEffect(() => {
|
||||
showTooltip.value = props.tooltip;
|
||||
});
|
||||
watchEffect(
|
||||
() => {
|
||||
if (props.tooltip && ellipsis.value) {
|
||||
defaultTooltipMaxWidth.value =
|
||||
props.tooltipMaxWidth ?? ellipsis.value.offsetWidth + 24;
|
||||
}
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
function onExpand() {
|
||||
const { style } = ellipsis.value;
|
||||
const isExpanded = !style['-webkit-line-clamp'];
|
||||
if (props.tooltip) {
|
||||
showTooltip.value = !isExpanded;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
style['-webkit-line-clamp'] = isExpanded ? props.line : '';
|
||||
});
|
||||
|
||||
emit('expandChange', !isExpanded);
|
||||
}
|
||||
|
||||
function handleExpand() {
|
||||
if (props.expand) {
|
||||
onExpand();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<VbenTooltip
|
||||
:content-style="{
|
||||
...tooltipOverlayStyle,
|
||||
maxWidth: `${defaultTooltipMaxWidth}px`,
|
||||
fontSize: `${tooltipFontSize}px`,
|
||||
color: tooltipColor,
|
||||
backgroundColor: tooltipBackgroundColor,
|
||||
}"
|
||||
:disabled="!showTooltip"
|
||||
:side="placement"
|
||||
>
|
||||
<slot name="tooltip">
|
||||
<slot></slot>
|
||||
</slot>
|
||||
|
||||
<template #trigger>
|
||||
<div
|
||||
ref="ellipsis"
|
||||
:class="{
|
||||
'!cursor-pointer': expand,
|
||||
['inline-block truncate']: line === 1,
|
||||
[$style.ellipsisMultiLine]: line > 1,
|
||||
}"
|
||||
:style="`-webkit-line-clamp: ${line}; max-width: ${textMaxWidth};`"
|
||||
class="cursor-text overflow-hidden"
|
||||
@click="handleExpand"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
.ellipsisMultiLine {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1 @@
|
||||
export { default as EllipsisText } from './ellipsis-text.vue';
|
2
packages/effects/common-ui/src/components/index.ts
Normal file
2
packages/effects/common-ui/src/components/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './ellipsis-text';
|
||||
export * from './page';
|
1
packages/effects/common-ui/src/components/page/index.ts
Normal file
1
packages/effects/common-ui/src/components/page/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Page } from './page.vue';
|
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'PageFooter',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { PageHeaderProps } from './page.ts';
|
||||
|
||||
defineOptions({
|
||||
name: 'PageHeader',
|
||||
});
|
||||
|
||||
const props = defineProps<PageHeaderProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-card px-6 py-4">
|
||||
<div class="mb-2 flex justify-between text-xl font-bold leading-10">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
11
packages/effects/common-ui/src/components/page/page.ts
Normal file
11
packages/effects/common-ui/src/components/page/page.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
interface PageHeaderProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface Props extends PageHeaderProps {
|
||||
contentClass?: string;
|
||||
showFooter?: boolean;
|
||||
}
|
||||
|
||||
export type { PageHeaderProps, Props };
|
38
packages/effects/common-ui/src/components/page/page.vue
Normal file
38
packages/effects/common-ui/src/components/page/page.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import type { Props } from './page';
|
||||
|
||||
import PageFooter from './page-footer.vue';
|
||||
import PageHeader from './page-header.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'Page',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
description: '',
|
||||
showFooter: false,
|
||||
title: '',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative h-full">
|
||||
<PageHeader
|
||||
v-if="description || $slots.description || title"
|
||||
:title="props.title"
|
||||
>
|
||||
<template #default>
|
||||
<template v-if="description">{{ description }}</template>
|
||||
<slot v-else name="description"></slot>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div :class="contentClass" class="m-4">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<PageFooter v-if="props.showFooter">
|
||||
<template #default>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</PageFooter>
|
||||
</div>
|
||||
</template>
|
Reference in New Issue
Block a user