186 lines
4.4 KiB
Vue
186 lines
4.4 KiB
Vue
<template>
|
|
<el-popover
|
|
:placement="placement"
|
|
:width="width"
|
|
:trigger="trigger"
|
|
:popper-class="popperClass">
|
|
<template #reference>
|
|
<slot name="reference"></slot>
|
|
</template>
|
|
|
|
<!-- 弹出内容 -->
|
|
<div class="detail-popover">
|
|
<!-- 标题 -->
|
|
<div v-if="title" class="detail-title">
|
|
<el-icon v-if="titleIcon" class="title-icon">
|
|
<component :is="titleIcon" />
|
|
</el-icon>
|
|
<span>{{ title }}</span>
|
|
</div>
|
|
|
|
<!-- 详情项列表 -->
|
|
<div
|
|
v-for="(item, index) in items"
|
|
:key="index"
|
|
:class="['detail-section', { 'horizontal': item.layout === 'horizontal' }]">
|
|
<div v-if="item.label" class="section-label">
|
|
<el-icon v-if="item.labelIcon" class="label-icon">
|
|
<component :is="item.labelIcon" />
|
|
</el-icon>
|
|
<span>{{ item.label }}</span>
|
|
</div>
|
|
<div class="section-content" :class="item.contentClass">
|
|
<!-- 使用插槽自定义内容 -->
|
|
<slot
|
|
v-if="$slots[`content-${index}`]"
|
|
:name="`content-${index}`"
|
|
:item="item">
|
|
</slot>
|
|
<!-- 默认显示文本内容 -->
|
|
<template v-else>
|
|
<component
|
|
v-if="item.component"
|
|
:is="item.component"
|
|
v-bind="item.componentProps || {}">
|
|
</component>
|
|
<span v-else :class="item.contentClass">{{ item.content }}</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 自定义内容插槽 -->
|
|
<slot name="custom-content"></slot>
|
|
</div>
|
|
</el-popover>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { Component } from 'vue'
|
|
|
|
export interface DetailItem {
|
|
label?: string // 标签文本
|
|
content?: string | number // 内容文本
|
|
labelIcon?: Component // 标签图标
|
|
layout?: 'horizontal' | 'vertical' // 布局方向,默认 vertical
|
|
contentClass?: string // 内容区域的自定义类名
|
|
component?: Component // 自定义组件
|
|
componentProps?: Record<string, any> // 自定义组件的 props
|
|
}
|
|
|
|
interface Props {
|
|
title?: string // 标题
|
|
titleIcon?: Component // 标题图标
|
|
items?: DetailItem[] // 详情项列表
|
|
placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end'
|
|
width?: string | number // Popover 宽度
|
|
trigger?: 'click' | 'focus' | 'hover' | 'contextmenu' // 触发方式
|
|
popperClass?: string // Popover 自定义类名
|
|
}
|
|
|
|
withDefaults(defineProps<Props>(), {
|
|
title: '',
|
|
titleIcon: undefined,
|
|
items: () => [],
|
|
placement: 'right',
|
|
width: 300,
|
|
trigger: 'click',
|
|
popperClass: ''
|
|
})
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
export default {
|
|
name: 'DetailPopover'
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.detail-popover {
|
|
padding: 0;
|
|
|
|
.detail-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 16px;
|
|
border-bottom: 2px solid #EBEEF5;
|
|
|
|
.title-icon {
|
|
color: var(--el-color-primary);
|
|
font-size: 18px;
|
|
}
|
|
}
|
|
|
|
.detail-section {
|
|
margin-bottom: 20px;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
// 横向布局
|
|
&.horizontal {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
|
|
.section-label {
|
|
margin-bottom: 0;
|
|
white-space: nowrap;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.section-content {
|
|
flex: 1;
|
|
}
|
|
}
|
|
|
|
// 纵向布局(默认)
|
|
&:not(.horizontal) {
|
|
.section-label {
|
|
margin-bottom: 10px;
|
|
}
|
|
}
|
|
|
|
.section-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 13px;
|
|
color: #909399;
|
|
font-weight: 500;
|
|
|
|
.label-icon {
|
|
font-size: 14px;
|
|
color: var(--el-color-primary);
|
|
}
|
|
}
|
|
|
|
.section-content {
|
|
font-size: 14px;
|
|
color: #303133;
|
|
line-height: 1.6;
|
|
word-break: break-all;
|
|
|
|
// 新专业样式(蓝色高亮)
|
|
&.new-major {
|
|
color: var(--el-color-primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
// 支持嵌套的样式类
|
|
:deep(.new-major) {
|
|
color: var(--el-color-primary);
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|