merge code pull
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
<el-tag
|
||||
:type="type"
|
||||
:size="size"
|
||||
:class="['clickable-tag', { 'has-action': actualRightIcon !== null }]"
|
||||
:class="['clickable-tag', { 'has-action': actualRightIcon !== null}]"
|
||||
:style="{ width: width ? `${width}px` : 'auto' }"
|
||||
@click="handleClick">
|
||||
<!-- 左侧图标 -->
|
||||
<el-icon
|
||||
@@ -43,6 +44,7 @@ interface Props {
|
||||
leftIcon?: any // 左侧图标组件
|
||||
middleIcon?: any // 中间图标组件(如警告图标)
|
||||
rightIcon?: any // 右侧图标组件(默认为 Right null 则不显示)
|
||||
width?: string | number // 自定义宽度
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -50,7 +52,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
size: 'default',
|
||||
leftIcon: undefined,
|
||||
middleIcon: undefined,
|
||||
rightIcon: undefined
|
||||
rightIcon: undefined,
|
||||
width: undefined
|
||||
})
|
||||
|
||||
// 获取实际的右侧图标:未传值时使用默认图标,传 null 则不显示
|
||||
@@ -97,7 +100,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.middle-icon {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
162
src/components/DetailPopover/README.md
Normal file
162
src/components/DetailPopover/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# DetailPopover 详情弹窗组件
|
||||
|
||||
一个通用的详情弹窗组件,用于展示结构化的详情信息。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 自定义标题和标题图标
|
||||
- ✅ 支持多个详情项(label + content)
|
||||
- ✅ 支持横向/纵向布局
|
||||
- ✅ 支持自定义内容(插槽或组件)
|
||||
- ✅ 支持标签图标
|
||||
- ✅ 支持内容区域自定义样式类
|
||||
|
||||
## Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|-----|------|------|-------|--------|
|
||||
| title | 标题 | string | - | '' |
|
||||
| titleIcon | 标题图标组件 | Component | - | undefined |
|
||||
| items | 详情项列表 | DetailItem[] | - | [] |
|
||||
| placement | 弹出位置 | string | top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end | right |
|
||||
| width | Popover 宽度 | string \| number | - | 300 |
|
||||
| trigger | 触发方式 | string | click/focus/hover/contextmenu | click |
|
||||
| popperClass | Popover 自定义类名 | string | - | '' |
|
||||
|
||||
## DetailItem 接口
|
||||
|
||||
```typescript
|
||||
interface DetailItem {
|
||||
label?: string // 标签文本
|
||||
content?: string | number // 内容文本
|
||||
labelIcon?: Component // 标签图标
|
||||
layout?: 'horizontal' | 'vertical' // 布局方向,默认 vertical
|
||||
contentClass?: string // 内容区域的自定义类名
|
||||
component?: Component // 自定义组件
|
||||
componentProps?: Record<string, any> // 自定义组件的 props
|
||||
}
|
||||
```
|
||||
|
||||
## Slots
|
||||
|
||||
| 插槽名 | 说明 | 参数 |
|
||||
|--------|------|------|
|
||||
| reference | 触发元素 | - |
|
||||
| content-{index} | 自定义第 index 项的内容 | { item: DetailItem } |
|
||||
| custom-content | 自定义内容(显示在所有详情项之后) | - |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<DetailPopover
|
||||
title="专业变更详情"
|
||||
:title-icon="InfoFilled"
|
||||
:width="320"
|
||||
:items="[
|
||||
{ label: '旧专业', content: '计算机应用技术' },
|
||||
{ label: '新专业', content: '软件技术', contentClass: 'new-major' }
|
||||
]">
|
||||
<template #reference>
|
||||
<span class="link">查看详情</span>
|
||||
</template>
|
||||
</DetailPopover>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DetailPopover from '/@/components/DetailPopover/index.vue'
|
||||
import { InfoFilled } from '@element-plus/icons-vue'
|
||||
</script>
|
||||
```
|
||||
|
||||
### 横向布局
|
||||
|
||||
```vue
|
||||
<DetailPopover
|
||||
title="异动审核详情"
|
||||
:items="[
|
||||
{
|
||||
label: '审核状态',
|
||||
layout: 'horizontal',
|
||||
content: '待审核'
|
||||
}
|
||||
]">
|
||||
<template #reference>
|
||||
<el-button>查看</el-button>
|
||||
</template>
|
||||
</DetailPopover>
|
||||
```
|
||||
|
||||
### 使用插槽自定义内容
|
||||
|
||||
```vue
|
||||
<DetailPopover
|
||||
title="异动审核详情"
|
||||
:items="[
|
||||
{ label: '审核状态', layout: 'horizontal' },
|
||||
{ label: '备注信息', contentClass: 'reason-content' }
|
||||
]">
|
||||
<template #reference>
|
||||
<ClickableTag>待审核</ClickableTag>
|
||||
</template>
|
||||
|
||||
<!-- 自定义第一项内容 -->
|
||||
<template #content-0>
|
||||
<ClickableTag type="warning">待审核</ClickableTag>
|
||||
</template>
|
||||
|
||||
<!-- 自定义第二项内容 -->
|
||||
<template #content-1>
|
||||
<div class="reason-content">
|
||||
<el-icon><Warning /></el-icon>
|
||||
<span>需要补充材料</span>
|
||||
</div>
|
||||
</template>
|
||||
</DetailPopover>
|
||||
```
|
||||
|
||||
### 使用标签图标
|
||||
|
||||
```vue
|
||||
<DetailPopover
|
||||
title="详情"
|
||||
:items="[
|
||||
{
|
||||
label: '状态',
|
||||
labelIcon: CircleCheck,
|
||||
content: '已完成'
|
||||
}
|
||||
]">
|
||||
<template #reference>
|
||||
<el-button>查看</el-button>
|
||||
</template>
|
||||
</DetailPopover>
|
||||
```
|
||||
|
||||
## 样式自定义
|
||||
|
||||
可以通过 `contentClass` 为内容区域添加自定义样式类:
|
||||
|
||||
```vue
|
||||
<DetailPopover
|
||||
:items="[
|
||||
{
|
||||
label: '新专业',
|
||||
content: '软件技术',
|
||||
contentClass: 'new-major' // 添加自定义样式类
|
||||
}
|
||||
]">
|
||||
<template #reference>
|
||||
<span>查看</span>
|
||||
</template>
|
||||
</DetailPopover>
|
||||
|
||||
<style scoped>
|
||||
.new-major {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
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