a
This commit is contained in:
185
src/components/DetailPopover/index.vue
Normal file
185
src/components/DetailPopover/index.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user