table_search.vue 10.2 KB
<script lang="ts" setup name="TableSearch">
import { computed, reactive, ref } from 'vue'
import { Search } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
import { debounce } from "@/utils/common"
const emits = defineEmits([
  "tableSearch",
  "toolFilterChange",
  "loadMore",
  "remoteMethod",
  "treeSelectLoad",
  "selectChange",
  "treeSelectNodeChange",
  "treeSelectNodeClick",
  "cascaderChange",
]);
const props = defineProps({
  itemList: {
    type: Array,
    default: []
  },
  formId: {
    type: String,
    default: '',
  },
  init: {
    type: Boolean,
    default: true
  }
})
const formRef = ref()
const formInline: any = computed(() => {
  let items = setFormFields(props.itemList);
  return reactive(items);
})
const formItemList: any = computed(() => {
  return reactive(props.itemList);
})
const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
  nextTick(() => {
    const form = formInline.value
    emits('tableSearch', form, true)
  })
}

defineExpose({
  formInline,
  formRef,
})
const setFormFields = (list: any) => {
  let obj = {}
  list.map((item: any) => {
    obj[item.field] = item.default == undefined ? '' : item.default
    if (item.children) {
      obj = { ...obj, ...setFormFields(item.children) }
    }
  })
  return obj
}
const onSubmit = () => {
  const form = formInline.value
  emits('tableSearch', form)
}
const radioGroupChange = (val) => {
  emits('toolFilterChange', val, 'radio-group')
}
const loadMore = () => {
  emits('loadMore')
}
const treeSelectLoad = (node, resolve, item) => {
  emits("treeSelectLoad", node, resolve, item);
};
const selectChange = (val, row) => {
  const info = formInline.value;
  emits("selectChange", val, row, info);
};
const handleTreeSelectNodeChange = (node, item) => {
  emits("treeSelectNodeChange", node, item);
}
const handleTreeSelectNodeClick = (node, item) => {
  emits("treeSelectNodeClick", node, item);
}
const cascaderChange = (val, item) => {
  emits("cascaderChange", val, item);
  if(item.blur){
    const formCascader = formCascaderRef.value[0] || formCascaderRef.value;
    formCascader.togglePopperVisible(false)
  }
}

const getCascaderCheckedData = () => {
  const formCascader = formCascaderRef.value[0] || formCascaderRef.value;
  const data = formCascader.getCheckedNodes();
  return data;
};
const inputChange = (val) => {
  onSubmit()
}
const formCascaderRef = ref()
onMounted(() => {
  props.init && onSubmit()
})
</script>

<template>
  <el-form ref="formRef" :inline="true" :model="formInline" class="demo-form-inline" @submit.native.prevent>
    <template v-for="(item, index) in formItemList">
      <el-form-item v-if="item.visible ?? true" :key="'item_' + index"
        :class="{ 'width_auto': item.type == 'radio-button' }" :prop="item.field">
        <template v-if="item.type == 'select'">
          <el-select collapse-tags collapse-tags-tooltip :class="{ 'is-multiple': item.multiple }"
            v-model="formInline[item.field]" :placeholder="item.placeholder" :clearable="item.clearable"
            :filterable="item.filterable" :multiple="item.multiple" :max-collapse-tags="3"
            :disabled="item.disabled ?? false" @change="(val) => selectChange(val, item)">
            <el-option v-for="opt in item.options" :label="item.props?.label ? opt[item.props.label] : opt.label"
              :value="item.props?.value ? opt[item.props.value] : opt.value" />
          </el-select>
        </template>
        <template v-else-if="item.type == 'input-select'">
          <el-input v-model.trim="formInline[item.field]" :placeholder="item.placeholder" class="input-with-select"
            :maxlength="item.maxlength ?? ''" :suffix-icon="Search">
            <template #prepend>
              <span v-for="child in item.children">
                <el-select v-model="formItemList[child.field]" :placeholder="child.placeholder"
                  :clearable="item.clearable" style="vertical-align: inherit;">
                  <el-option v-for="opts in child.options"
                    :label="child.props?.label ? opts[item.props.label] : opts.label"
                    :value="child.props?.value ? opts[item.props.value] : opts.value" />
                </el-select>
              </span>
            </template>
          </el-input>
        </template>
        <template v-else-if="item.type == 'tree-select'">
          <div class="select_slots_panel">
            <div class="slot-prefix" v-for="child in item.children">
              <el-select v-model="formInline[child.field]" :placeholder="child.placeholder"
                @change="(val) => selectChange(val, child)">
                <el-option v-for="cpt in child.options" :label="cpt.label" :value="cpt.value" />
              </el-select>
            </div>
          </div>
          <div class="slot-default">
            <el-tree-select v-model="formInline[item.field]" :data="item.options" :placeholder="item.placeholder"
              :clearable="item.clearable" :filterable="item.filterable" :disabled="item.disabled"
              :lazy="item.lazy ?? true" :check-strictly="item.checkStrictly ?? false" :node-key="item.nodeKey ?? 'guid'"
              :load="(node, resolve) => treeSelectLoad(node, resolve, item)" :props="item.props"
              :multiple="item.multiple ?? false" :render-after-expand="true" :teleported="item.teleported ?? true"
              :default-expanded-keys="item.expandKeys" :collapse-tags-tooltip="item.collapseTagsTooltip ?? false"
              :collapse-tags="item.collapseTags ?? false" :max-collapse-tags="item.maxTags ?? 1"
              :show-checkbox="item.showCheckbox ?? false" @change="(val) => selectChange(val, item)"
              @current-change="(node) => handleTreeSelectNodeChange(node, item)"
              @node-click="(node) => handleTreeSelectNodeClick(node, item)">

              <!-- <template #default="{ node, data }">
                  <template v-if="item.getName">
                    <div
                      class="left-code"
                      style="
                        display: inline-flex;
                        font-size: 12px;
                        line-height: 21px;
                        margin-left: 4px;
                        overflow: hidden;
                        text-overflow: ellipsis;
                      "
                    >
                      {{ item.getName(data, node) }}
                    </div>
                  </template>
<span v-else>{{ data[item.props.label] }}</span>
</template> -->
            </el-tree-select>
          </div>

        </template>
        <template v-else-if="item.type == 'date-time'">
          <el-date-picker v-model="formInline[item.field]" type="datetimerange" start-placeholder="开始时间"
            end-placeholder="结束时间" />
        </template>
        <template v-else-if="item.type == 'radio-button'">
          <el-radio-group v-model="formInline[item.field]" @change="radioGroupChange">
            <el-radio-button v-for="opts in item.options" :label="opts.label" />
          </el-radio-group>
        </template>
        <template v-else-if="item.type == 'select-slots'">
          <div class="select_slots_panel">
            <div class="slot-prefix" v-for="child in item.children">
              <el-select v-model="formInline[child.field]" :placeholder="child.placeholder">
                <el-option v-for="cpt in child.options" :label="cpt.label" :value="cpt.value" />
              </el-select>
            </div>
            <div class="slot-default">
              <el-select :class="{ 'is-multiple': item.multiple }" v-model="formInline[item.field]"
                :placeholder="item.placeholder" :multiple="item.multiple" collapse-tags collapse-tags-tooltip
                @change="(val) => selectChange(val, item)" :max-collapse-tags="3" :filterable="item.filterable"
                :clearable="item.clearable" v-loadmore="loadMore">
                <el-option v-for="opt in item.options" :label="opt.label" :value="opt.value" />
              </el-select>
            </div>
          </div>
        </template>
        <template v-else-if="item.type == 'cascader'">
          <el-cascader ref="formCascaderRef" :class="[item.col, { is_block: item.block }]"
            v-model="formInline[item.field]" :options="item.options" :props="item.props" :placeholder="item.placeholder"
            :show-all-levels="item.showAllLevels ?? true" :clearable="item.clearable"
            :filterable="item.filterable ?? false" :collapse-tags="item.collapse ?? false"
            :collapse-tags-tooltip="item.tagsTooltip ?? false" :max-collapse-tags="item.maxTags ?? 1"
            :disabled="item.disabled ?? false" :popper-class="item.popperClass ?? ''" @change="(val) => cascaderChange(val, item)" />
        </template>
        <template v-else>
          <el-input v-model.trim="formInline[item.field]" :placeholder="item.placeholder" :clearable="item.clearable"
            @clear="inputChange" />
        </template>
      </el-form-item>
    </template>
    <el-form-item>
      <el-button type="primary" @click="onSubmit" v-preReClick>查询</el-button>
      <el-button @click="resetForm(formRef)" v-preReClick>清空</el-button>
    </el-form-item>
  </el-form>
</template>

<style lang="scss" scoped>
.el-form {
  :deep(.el-form-item) {
    margin-bottom: 8px;
    margin-right: 8px;

    .el-input__wrapper {
      width: 230px;

      &.el-date-editor {
        width: auto;

        .el-range-separator {
          // flex: unset;
        }
      }
    }

    .el-select {
      width: 140px;

      &.is-multiple {
        width: 400px;

      }
    }

    .select_item {
      width: 140px;
    }

    &.width_auto {
      width: auto;

      .el-select {
        width: 100%;
      }

      .select_item {
        width: 100%;
      }
    }

    .select_slots_panel {
      display: flex;

      .slot-prefix {
        margin-right: -1px;

        .el-input__wrapper {
          border-radius: var(--el-input-border-radius, var(--el-border-radius-base)) 0 0 var(--el-input-border-radius, var(--el-border-radius-base));
        }
      }

      .slot-default {
        .el-input__wrapper {
          border-radius: 0 var(--el-input-border-radius, var(--el-border-radius-base)) var(--el-input-border-radius, var(--el-border-radius-base)) 0;
        }
      }

    }

  }
}
</style>