a
This commit is contained in:
176
src/components/SearchForm/index.vue
Normal file
176
src/components/SearchForm/index.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<div class="search-form-container">
|
||||
<el-form :model="formModel" ref="formRef" :inline="true" @keyup.enter="handleKeyupEnter" class="search-form-inline">
|
||||
<!-- 直接展示的表单项 -->
|
||||
<slot :visible="true" :expanded="isExpanded"></slot>
|
||||
|
||||
<!-- 可折叠的表单项区域 - 使用 display: contents 确保不影响布局 -->
|
||||
<div v-show="isExpanded" class="collapsible-content">
|
||||
<slot :visible="false" :expanded="isExpanded" @has-content="hasCollapsibleContent = true"></slot>
|
||||
</div>
|
||||
|
||||
<!-- 隐藏的检测元素,用于检测是否有可折叠内容 -->
|
||||
<div v-show="false" ref="detectionWrapperRef" class="detection-wrapper">
|
||||
<slot :visible="false" :expanded="true">
|
||||
<!-- 如果插槽有内容,这里会被渲染,我们可以通过检查这个元素来判断 -->
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮插槽(查询、重置等) -->
|
||||
<slot name="actions"></slot>
|
||||
|
||||
<!-- 展开/收起按钮(在操作按钮之后) -->
|
||||
<el-form-item v-if="hasCollapsibleItems" class="collapse-trigger-item">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="toggleExpand"
|
||||
class="toggle-btn"
|
||||
>
|
||||
<el-icon style="margin-right: 4px">
|
||||
<ArrowUp v-if="isExpanded" />
|
||||
<ArrowDown v-else />
|
||||
</el-icon>
|
||||
{{ isExpanded ? '收起' : '展开更多' }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="search-form">
|
||||
import { ref, watch, computed, onMounted, nextTick } from 'vue';
|
||||
import { ArrowUp, ArrowDown } from '@element-plus/icons-vue';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 表单数据模型
|
||||
*/
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 是否默认展开折叠区域
|
||||
*/
|
||||
defaultExpanded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 是否显示展开/折叠按钮
|
||||
* 如果不设置,将自动检测是否有可折叠内容(visible=false 的插槽)
|
||||
*/
|
||||
showCollapse: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['expand-change', 'keyup-enter']);
|
||||
|
||||
const formModel = props.model;
|
||||
const formRef = ref();
|
||||
const isExpanded = ref(props.defaultExpanded);
|
||||
const hasCollapsibleContent = ref(false);
|
||||
const detectionWrapperRef = ref<HTMLElement | null>(null);
|
||||
|
||||
// 检测是否有可折叠内容
|
||||
const checkCollapsibleContent = () => {
|
||||
// 如果 showCollapse 明确指定,则使用指定值
|
||||
if (props.showCollapse !== undefined) {
|
||||
hasCollapsibleContent.value = props.showCollapse
|
||||
return
|
||||
}
|
||||
|
||||
// 否则,通过检查隐藏的检测元素是否有内容来判断
|
||||
// 需要等待 DOM 渲染完成
|
||||
nextTick(() => {
|
||||
if (detectionWrapperRef.value) {
|
||||
// 检查检测元素是否有子元素(排除文本节点)
|
||||
const hasContent = detectionWrapperRef.value.children.length > 0 ||
|
||||
(!!detectionWrapperRef.value.textContent && detectionWrapperRef.value.textContent.trim() !== '')
|
||||
hasCollapsibleContent.value = hasContent
|
||||
} else {
|
||||
hasCollapsibleContent.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 是否有需要折叠的项
|
||||
const hasCollapsibleItems = computed(() => {
|
||||
if (props.showCollapse !== undefined) {
|
||||
return props.showCollapse
|
||||
}
|
||||
return hasCollapsibleContent.value
|
||||
});
|
||||
|
||||
// 处理回车键
|
||||
const handleKeyupEnter = () => {
|
||||
emit('keyup-enter');
|
||||
};
|
||||
|
||||
// 切换展开/收起状态
|
||||
const toggleExpand = () => {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
emit('expand-change', isExpanded.value);
|
||||
};
|
||||
|
||||
// 监听 defaultExpanded 变化
|
||||
watch(
|
||||
() => props.defaultExpanded,
|
||||
(newVal) => {
|
||||
isExpanded.value = newVal;
|
||||
}
|
||||
);
|
||||
|
||||
// 监听 showCollapse 变化
|
||||
watch(
|
||||
() => props.showCollapse,
|
||||
() => {
|
||||
checkCollapsibleContent()
|
||||
}
|
||||
);
|
||||
|
||||
// 初始化时检测
|
||||
onMounted(() => {
|
||||
checkCollapsibleContent()
|
||||
});
|
||||
|
||||
// 暴露方法供外部调用
|
||||
defineExpose({
|
||||
formRef,
|
||||
toggleExpand,
|
||||
expand: () => {
|
||||
isExpanded.value = true;
|
||||
emit('expand-change', true);
|
||||
},
|
||||
collapse: () => {
|
||||
isExpanded.value = false;
|
||||
emit('expand-change', false);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-form-container {
|
||||
|
||||
:deep(.search-form-inline) {
|
||||
.collapse-trigger-item {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// 可折叠内容区域 - 使用 contents 让包装器不影响布局
|
||||
.collapsible-content {
|
||||
display: contents;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user