XXXForm组件

效果展示

2024-04-182.40.57-ezgif.com-video-to-gif-converter.gif

代码

XXXForm

<template>
  <div class="search-container">
    <el-form ref="formRef" class="form_is_hidden" :model="form" v-bind="formAttrs">
      <el-row :gutter="20" class="search-row">
        <el-col
          v-for="item in shows.columns"
          :key="item.inputType + JSON.stringify(item.values)"
          :span="item.span || 6"
        >
          <el-form-item v-bind="item.formItemAttrs || {}">
            <FormItem
              v-if="typeof item.values == 'string'"
              v-bind="item.inputAttrs || {}"
              v-model="form[item.values]"
              :input-type="item.inputType"
            />
            <FormItem
              v-else
              v-model:one="form[item.values[0]]"
              v-model:two="form[item.values[1]]"
              :input-type="item.inputType"
              v-bind="item.inputAttrs || {}"
            />
          </el-form-item>
        </el-col>
        <el-col :span="shows.btnsSpan" class="search-btn-container">
          <el-form-item class="search-btn" label="1" label-width="0">
            <el-tooltip
              v-if="shows.collapsedBtn"
              :content="collapsed ? '收起' : '展开'"
              placement="top"
              trigger="hover"
            >
              <el-button class="coll" text bg type="info" @click="toggleCollapse">
  
                <el-icon class="form_is_hidden_icon">
                  <DArrowRight v-if="!collapsed" />
                  <DArrowLeft v-else />
                </el-icon>
              </el-button>
            </el-tooltip>
            <el-button text bg type="info" @click="resetForm">重置</el-button>
            <el-button type="primary" @click="search">查询</el-button>

          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useVModel } from '@vueuse/core';
import { SearchFormColum } from './type';
import FormItem from './formItem.vue';
import { DArrowRight, DArrowLeft } from '@element-plus/icons-vue';

const props = defineProps({
  columns: {
    type: Array,
    default: () => [],
  },
  collapse: {
    type: Boolean,
    default: false,
  },
  modelValue: {
    type: Object,
    default: () => {
      return {};
    },
  },
  resttForm: {
    type: Object,
    default: () => {
      return {};
    },
  },
  formAttrs: {
    type: Object,
    default: () => {
      return {};
    },
  },
});
const emit = defineEmits(['update:modelValue', 'search', 'reset']);
const collapsed = ref(props.collapse);
const form = useVModel(props, 'modelValue', emit);
const shows = computed(() => {
  const cols = props.columns as SearchFormColum;
  const spans = cols?.map((i) => i.span || 6).reduce((a, b) => a + b, 0);
  if (spans > 18) {
    if (!collapsed.value) {
      let spanss = 0;
      let index = 0;
      try {
        cols?.forEach((i, idx) => {
          if (spanss < 18) {
            if ((i.span || 6) + spanss > 18) {
              throw new Error('');
            }
            spanss += i.span || 6;
            index = idx;
          } else {
            throw new Error('');
          }
        });
      } catch (error) {
        // console.log('跳出循环');
      }

      return {
        columns: cols.filter((_i, idx) => idx <= index),
        btnsSpan: 24 - spanss,
        collapsedBtn: true,
      };
    } else {
      const spanss = cols
        ?.map((i) => i.span || 6)
        .reduce((a, b) => {
          if (a + b > 24) {
            return b;
          } else if (a + b == 24) {
            return 0;
          } else {
            return a + b;
          }
        }, 0);
      const btnsSpan = spanss > 18 ? 24 : 24 - spanss;

      return {
        columns: cols,
        btnsSpan,
        collapsedBtn: true,
      };
    }
  } else {
    return {
      columns: cols,
      btnsSpan: 24 - spans,
      collapsedBtn: false,
    };
  }
});
const search = () => {
  emit('search');
};
const resetForm = () => {
  const resett = { ...props.resttForm };
  form.value = resett;
  emit('reset');
};
const toggleCollapse = () => {
  collapsed.value = !collapsed.value;
};
</script>
<style lang="less" scoped>
.search-container {
  // margin-bottom: 20px;

  .form_is_hidden_icon {
    transform: rotate(90deg);
  }

  .search-btn-container {
    .search-btn {
      :deep(.el-form-item__content) {
        justify-content: end;
      }

      :deep(.el-form-item__label) {
        color:transparent;
      }
    }
  }

  .search-row {
    // transition: height 0.3s;
    overflow: hidden;
  }
  .search-label {
    font-size: 14px;
    color: var(--hiwork-global-color);
    padding-bottom: 8px;
  }
}
:deep(.el-button).coll {
  padding: 10px 12px !important;
}
</style>

formItem

<template>
  <component :is="currentCom" />
</template>
<script lang="tsx" setup>
// import cityCascader from './cityCascader';
import DateMonthPicker from './components/dateMonthPicker';
import DateRangePicker from './components/dateRangePicker';
import DateYearPicker from './components/dateYearPicker';
import Select from './components/select';
import CountryAutoComplete from './components/countryAutoComplete.vue';
import CountryAutoCompleteOne from './components/countryAutoCompleteOne.vue';
import HiInput from './components/hiInput.vue';
import { computed } from 'vue';
const props = defineProps({
  inputType: {
    type: String,
    default: 'input',
  },
});

const currentCom = computed(() => {
  switch (props.inputType) {
    case 'date':
      return DateRangePicker;
    case 'month':
      return DateMonthPicker;
    case 'year':
      return DateYearPicker;
    // case 'cascader':
    //   return cityCascader;
    case 'select':
      return Select;
    case 'countryAuto':
      return CountryAutoComplete;
    case 'countryAuto1':
      return CountryAutoCompleteOne;
    default:
      return HiInput;
  }
});
</script>

XXXInput

<template>
  <!-- hiInput -->
  <el-input v-model="modelValue" @blur="hBlur" />
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
  modelValue: {
    type: String,
    default: '',
  },
});
const emit = defineEmits(['update:modelValue']);
const modelValue = computed({
  get() {
    return props.modelValue;
  },
  set(v) {
    emit('update:modelValue', v);
  },
});
const hBlur = () => {
  modelValue.value = modelValue.value.trim();
};
</script>

// dateRangePicker
import { ElDatePicker } from 'element-plus';
import { defineComponent, reactive, watchEffect } from 'vue';
import dayjs from 'dayjs';

export default defineComponent({
  props: {
    one: { type: String, default: () => '' },
    two: { type: String, default: () => '' },
    format: { type: String, default: () => '' },
  },
  emits: ['update:one', 'update:two'],
  // emits: {
  //   'update:one': (v: string) => true,
  //   'update:two': (v: string) => true,
  // },
  setup(props, { attrs, emit }) {
    const state = reactive({
      ivalue: [],
    });
    const updateValue = (
      v: (string | number | Date | dayjs.Dayjs | null | undefined)[],
    ) => {
      emit(
        'update:one',
        v ? dayjs(v[0]).format(props.format || 'YYYY-MM-DD') : '',
      );
      emit(
        'update:two',
        v ? dayjs(v[1]).format(props.format || 'YYYY-MM-DD') : '',
      );
    };
    watchEffect(() => {
      state.ivalue = [props.one, props.two] as any;
    });
    return () => (
      <ElDatePicker
        modelValue={state.ivalue as any}
        onUpdate:modelValue={updateValue}
        type='daterange'
        range-separator='-'
        start-placeholder='开始'
        end-placeholder='结束'
        {...attrs}
      />
    );
  },
});

// cityCascader
import { getLocationDetailInfo } from '@/axios/api';
import { ElCascader } from 'element-plus';
import { defineComponent, ref } from 'vue';

export default defineComponent((_p, { attrs }) => {
  const cityList = ref([]);

  getLocationDetailInfo().then((res) => {
    cityList.value = res.data;
  });

  return () => (
    <ElCascader
      {...attrs}
      placeholder='请选择省市区'
      style={'width: 100%'}
      props={{ children: 'child', value: 'name', label: 'name' }}
      options={cityList.value}
    />
  );
});

XXXInput,自己想要啥补充啥就行。

使用

props

参数说明类型可选值默认值
columns配置表单展示项object[]--
collapse是否收起booleantrue/falsefalse
modelValueform 对象{}-{}
resttForm重制 form 默认值{}-{}
formAttrs支持 el 的 form 属性{}-{}
cloumns 说明
参数说明类型可选值默认值
inputType输入框类型string--
spanformitem 占用空间(参考el-col)number1-246
formItemAttrs继承 el 的 formitem 的 props + emit,配置formitem 像 el 一样。{}--
valuesform 中对应的 keystring/string[]--
inputAttrs继承 el 的 input 的 props + emit,配置 input 像 el 一样。也是拓展属性的地方。{}--

emit

方法名说明类型
update:modelValue更新 form 对象-
search搜索-
reset重制-

使用示例

searchFormC

import { defineComponent, ref } from 'vue';
//@ts-ignore
import SearchForm from '@/components/searchForm/index.vue';
export default defineComponent((_p, { attrs }) => {
  const columns = ref([
    {
      values: 'nickname',
      inputType: 'input',
      span: 6,
      inputAttrs: {
        placeholder: '请输入',
      },
      formItemAttrs: {
        label: '姓名',
        labelWidth: '80px',
      },
    },
    {
      values: 'country',
      inputType: 'countryAuto1',
      inputAttrs: {
        placeholder: '请输入',
      },
      formItemAttrs: {
        label: '国家',
        labelWidth: '80px',
      },
    },
    {
      values: 'email',
      inputType: 'input',
      span: 6,
      inputAttrs: {
        placeholder: '请输入',
      },
      formItemAttrs: {
        label: '邮箱',
        labelWidth: '80px',
      },
    },
    {
      values: 'employeeCode',
      inputType: 'input',
      span: 12,
      inputAttrs: {
        placeholder: '请输入',
      },
      formItemAttrs: {
        label: '员工编号',
        labelWidth: '80px',
      },
    },
    {
      values: ['createStartTime', 'createEndTime'],
      inputType: 'date',
      span: 12,
      inputAttrs: {
        format: 'YYYY-MM-DD',
        placeholder: '请输入',
      },
      formItemAttrs: {
        label: '创建时间',
        labelWidth: '80px',
      },
    },
  ]);
  return () => (
    <SearchForm {...attrs} columns={columns.value} collapse={false} formAttrs={{ labelPosition: 'left' }} />
  );
});

vue

<template>
  <SearchFormC v-model="form" @search="search" @reset="rest" />
</template>

<script setup lang="ts">
import SearchFormC from './components/searchFormC'
import { ref } from 'vue';
const form = ref({})

const search = () => {
  console.log(form.value)
}

const rest = () => {
  console.log(form.value)
}
</script>
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值