效果图:
代码:
公用组件部分
子组件1:page-detail.vue
<template>
<a-space direction="vertical" :size="12" class="page-detail">
<page-title v-if="title || $slots.title" class="page-detail__title">
<slot name="title">{
{ title }}</slot>
</page-title>
<a-spin :spinning="loading">
<slot>
<a-row class="page-detail__list">
<a-col
v-for="(field, index) of _fields"
:key="index"
:offset="field.offset ?? offset"
:span="field.span ?? span"
class="page-detail__item"
>
<span
class="page-detail__label"
:style="{
width: `${field.labelWidth ?? labelWidth}px`,
...(labelStyle ?? field.labelStyle)
}"
>
<slot :name="field.labelSlot" :text="field.label">
{
{ field.label }}:
</slot>
</span>
<span
class="page-detail__text"
:style="textStyle ?? field.textStyle"
>
<slot :name="field.textSlot" :text="field.text">
<ellipsis v-if="field.ellipsis">
{
{ field.text ?? '-' }}
</ellipsis>
<template v-else>
{
{ field.text ?? '-' }}
</template>
</slot>
</span>
</a-col>
</a-row>
</slot>
</a-spin>
</a-space>
</template>
<script setup>
import { computed } from 'vue';
import PageTitle from '../page-title/index.vue';
import Ellipsis from '../ellipsis/index.vue';
const props = defineProps({
title: String,
loading: Boolean,
offset: {
type: Number,
default: 0
},
span: {
type: Number,
default: 8
},
labelWidth: {
type: Number,
default: 120
},
labelStyle: Object,
textStyle: Object,
fields: {
type: Array,
default: () => []
}
});
const _fields = computed(() =>
props.fields.map((field) => ({
...field,
ellipsis: field.ellipsis === undefined ? true : !!field.ellipsis
}))
);
</script>
<style lang="less" scoped>
.page-detail {
width: 100%;
.page-detail__list {
padding: 20px 20px 0 10px;
background: #f5f7fa;
border-radius: 2px;
.page-detail__item {
display: flex;
margin-bottom: 16px;
.page-detail__label {
flex: 0 0 auto;
text-align: right;
color: #9c9c9c;
}
.page-detail__text {
flex: 1;
width: 0;
word-break: break-word;
}
}
}
}
</style>
子组件2: page-title.vue
<template>
<div class="page-title">
<slot>{
{ title }}</slot>
</div>
</template>
<script setup>
defineProps({
title: String
});
</script>
<style lang="less" scoped>
.page-title {
position: relative;
padding-left: 12px;
font-weight: 500;
font-size: 16px;
color: #000000d9;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border-left: 4px solid var(--primary-color);
border-top: 2px solid transparent;
border-bottom: 2px solid transparent;
height: 18px;
}
}
</style>
子组件3: ellipsis.vue
<template>
<div ref="ellipsisRef" class="ellipsis" @mouseenter="handleMouseEnter">
<a-tooltip v-if="overflow" :align="{ offset }">
<template #title>
<slot></slot>
</template>
<slot></slot>
</a-tooltip>
<span v-else>
<slot></slot>
</span>
</div>
</template>
<script setup>
import { ref } from 'vue';
const ellipsisRef = ref();
const overflow = ref(false);
const offset = ref(0);
const handleMouseEnter = () => {
const width = ellipsisRef.value?.getBoundingClientRect().width;
const contentWidth =
ellipsisRef.value?.children[0].getBoundingClientRect().width;
overflow.value = contentWidth > width;
offset.value = (width - contentWidth) / 2;
};
</script>
<style scoped>
.ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
父组件如何使用:
<page-detail title="这里是标题" :fields="fields">
<template #fileList>
<FileUpload disabled v-model:file-list="form.fileList" />
</template>
<empty v-if="!form" />
</page-detail>
const fields = computed(() => {
const fields = [
{ label: '报告编号', text: form.value?.reportCode },
{
label: '结果',
text: statusValueLabelReport.value?.[form.value?.result]
},
{
label: '分析担当',
text: form.value?.analysisDeptName
},
{
label: '分析日期',
text: form.value?.analysisDate
},
{ label: '下次分析', text: form.value?.nextAnalysis },
{
label: '频率',
text: statusValueLabelFrequency.value?.[form.value?.analysisFrequency]
},
{ label: '分析报告', textSlot: 'fileList' }
];
return fields;
});