Css 一个选择器实现带你实现高级搜索的展开收起

在表格页面中,我们经常用到带有展开收起功能的过滤表单,看似很简单的功能,但是实现起来通常不那么优雅。
我们使用grid布局来实现这个就非常简单:

.search-form {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-gap: 10px;

    &:not(.expend) {
        .el-form-item {
        // 前三个和最后一个一直显示,其他的隐藏
        &:nth-child(n + 4):not(:nth-last-child(-n + 1)) {
            display: none;
        }
        }
    }

    .el-form-item {
        margin-bottom: 0;
	}
}

我们把它封装成通用组件

SearchForm.vue

<script setup lang="ts">
import { computed, ref, useCssModule, watch } from 'vue'
import type { FormInstance, FormProps } from 'element-plus'
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'

interface SearchFormProps extends Omit<FormProps, 'inline'> {
  column?: number
}

const props = withDefaults(defineProps<SearchFormProps>(), {
  column: 4
})

const emits = defineEmits<{
  toggle: [boolean]
  reset: []
  confirm: []
}>()

const style = useCssModule()

const formRef = ref<FormInstance | null>(null)
const expand = ref(false)

const formProps = computed(() => {
  const { column, ...formProps } = props
  return formProps
})

const offset = computed(() => {
  const { column } = props
  return column + 1
})

const handleToggle = () => {
  expand.value = !expand.value
  emits('toggle', expand.value)
}

const handleReset = () => {
  emits('reset')
}

const handleConfirm = () => {
  emits('confirm')
}

const styleEl = ref<HTMLStyleElement | null>(null)

// 这里是因为nth-child选择器不支持传入变量,所以我们选择动态创建css
const handleGenerateCss = (column: number) => {
  const { searchForm } = style
  const css = `
    .${searchForm}:not(.expend) .el-form-item:nth-child(n + ${column}):not(:last-child) {
      display: none;
    }
  `
  const cssText = document.createTextNode(css)
  if (styleEl.value) {
    styleEl.value.removeChild(styleEl.value.firstChild)
    styleEl.value.appendChild(cssText)
  } else {
    const style = document.createElement('style')
    style.appendChild(cssText)
    styleEl.value = style
    document.head.appendChild(style)
  }
}

watch(
  () => props.column,
  (column) => {
    handleGenerateCss(column)
  },
  { immediate: true }
)

defineExpose({
  form: formRef
})
</script>

<template>
  <el-form
    ref="formRef"
    v-bind="formProps"
    @submit.prevent="handleConfirm"
    @reset.prevent="handleReset"
    :class="[style.searchForm, { expend: expand }]"
  >
    <slot></slot>
    <el-form-item>
      <el-button @click="handleToggle" :icon="expand ? ArrowUp : ArrowDown">
        {{ expand ? '收起' : '展开' }}
      </el-button>
      <el-button type="primary" native-type="submit">查询</el-button>
      <el-button native-type="reset">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<style module lang="scss">
.searchForm {
  display: grid;
  grid-template-columns: repeat(v-bind(column), 1fr);
  grid-gap: 10px;

  :global(.el-form-item) {
    margin-bottom: 0;

    &:last-child {
      grid-column-end: v-bind(offset);
    }
  }
}
</style>

Usage

<script setup lang="ts">
import { reactive, ref } from 'vue'
import SearchForm from './SearchForm.vue'

const formStore = reactive({
  name: '',
  region: '',
  type: '',
  age: '',
  title: '',
  color: '',
  tag: '',
  date: '',
  time: ''
})

</script>

<template>
  <SearchForm :model="formStore" labelWidth="100px">
    <el-form-item label="name" prop="name">
      <el-input v-model="formStore.name" />
    </el-form-item>
    <el-form-item label="region" prop="region">
      <el-input v-model="formStore.region" />
    </el-form-item>
    <el-form-item label="type" prop="type">
      <el-input v-model="formStore.type" />
    </el-form-item>
    <el-form-item label="age" prop="age">
      <el-input v-model="formStore.age" />
    </el-form-item>
    <el-form-item label="title" prop="title">
      <el-input v-model="formStore.title" />
    </el-form-item>
    <el-form-item label="color" prop="color">
      <el-input v-model="formStore.color" />
    </el-form-item>
    <el-form-item label="tag" prop="tag">
      <el-input v-model="formStore.tag" />
    </el-form-item>
    <el-form-item label="date" prop="date">
      <el-input v-model="formStore.date" />
    </el-form-item>
    <el-form-item label="time" prop="time">
      <el-input v-model="formStore.time" />
    </el-form-item>
    <el-form-item label="time" prop="time">
      <el-input v-model="formStore.time" />
    </el-form-item>
  </SearchForm>
</template>

posted @ 2023-11-06 11:47  demo_you  阅读(297)  评论(0)    收藏  举报