index.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <template>
  2. <ElForm :inline="false" :model="formInline" class="demo-form-inline" label-width="100px">
  3. <ElRow :gutter="20">
  4. <ElCol v-for="(field, index) in fields" :key="index" :md="span">
  5. <ElFormItem :label="field.label">
  6. <component v-if="field.type === 'input'" is="el-input" v-model="formInline[field.model]"
  7. :placeholder="field.placeholder" clearable class="el-inputs" />
  8. <component v-else-if="field.type === 'select'" is="el-select" v-model="formInline[field.model]"
  9. :placeholder="field.placeholder" clearable class="el-inputs" v-bind="field">
  10. <el-option v-for="(item, index) in field.options" :key="index" :label="item[field.props?.label || 'label']"
  11. :value="item[field.props?.value || 'value']">
  12. <template #default="scope">
  13. <slot :name="`${field.model}-option`" :item="item" >
  14. {{ item[field.props?.label || 'label'] }}
  15. </slot>
  16. </template>
  17. </el-option>
  18. </component>
  19. <component v-else-if="field.type === 'cascader'" is="el-cascader" v-model="formInline[field.model]"
  20. :options="field.options" :props="field.props" :placement="field.placement" filterable clearable
  21. @change="field.onChange" class="el-inputs" />
  22. <component v-else-if="field.type === 'date'" is="el-date-picker" v-model="formInline[field.model]"
  23. :type="field.dateType" range-separator="至" :start-placeholder="field.startplaceholder"
  24. :format="field.format" :value-format="field.valueFormat" :time-format="field.timeFormat"
  25. :end-placeholder="field.endplaceholder" clearable @change="field.onChange" />
  26. </ElFormItem>
  27. </ElCol>
  28. <ElCol :md="isOffset ? span : span / 2" :offset="btnOffset">
  29. <ElFormItem>
  30. <div class="flex j-end" style="width: 100%;">
  31. <el-button type="primary" @click="handleSearch">
  32. <template #icon>
  33. <SvgIcon name="i-ep:search" />
  34. </template>
  35. 查询
  36. </el-button>
  37. <el-button type="default" @click="handleReset">重置</el-button>
  38. </div>
  39. </ElFormItem>
  40. </ElCol>
  41. </ElRow>
  42. </ElForm>
  43. </template>
  44. <script lang="ts" setup>
  45. import { ref, computed, onMounted, onUnmounted } from 'vue'
  46. const props = withDefaults(defineProps<{
  47. fields: Array<{
  48. label: string; // 字段标签
  49. model: string; // v-model 绑定的字段名
  50. type: string; // 字段类型 ('input', 'select', 'cascader', 'date')
  51. filterable?: boolean; // 是否支持筛选
  52. placeholder?: string; // 占位符
  53. options?: Array<{ [key: string]: any }>; // 选项列表
  54. props?: any; // Cascader 组件的其他 props
  55. onChange?: (value: any) => void; // 变化事件
  56. dateType?: string; // 日期选择器类型
  57. format?: string; // 日期格式
  58. valueFormat?: string; // 日期值格式
  59. timeFormat?: string; // 时间格式
  60. startplaceholder?: string; // 开始日期占位符
  61. endplaceholder?: string; // 结束日期占位符
  62. clearable?: boolean; // 是否可清空
  63. [key: string]: any,
  64. }>; // 字段信息数组
  65. formInline: any; // 表单数据
  66. onSearch: () => void; // 查询事件
  67. onReset: () => void; // 重置事件
  68. span?: number;
  69. isOffset?: boolean; // 是否正规化按钮位置,而不是紧贴formItem后面
  70. }>(), {
  71. span: 8,
  72. isOffset: false,
  73. filterable: false
  74. });
  75. const windowWidth = ref(window.innerWidth);
  76. onMounted(() => {
  77. window.addEventListener('resize', updateWindowWidth)
  78. });
  79. onUnmounted(() => {
  80. window.removeEventListener('resize', updateWindowWidth)
  81. });
  82. // 定义更新窗口宽度的函数
  83. const updateWindowWidth = () => {
  84. windowWidth.value = window.innerWidth;
  85. };
  86. // 计算按钮组的左侧偏移量
  87. const btnOffset = computed(() => {
  88. // 为了保持原有页面的样子
  89. if (!props.isOffset) return 0;
  90. // 因为栅格布局,代码使用的md,所以监听宽度
  91. if (windowWidth.value < 992) {
  92. return 0;
  93. }
  94. // 计算逻辑
  95. const items = props.fields.length;
  96. const columns = 24 / props.span; // 列布局
  97. const spanPerItem = props.span; // 每列的span
  98. const itemsOnLastRow = items % columns;
  99. if (itemsOnLastRow === 0) {
  100. // 如果刚好占满最后一行,按钮就需要另起一行,并偏移3个位置
  101. return (columns - 1) * spanPerItem;
  102. } else {
  103. // 如果最后一行没占满,计算需要空出的位置数量
  104. const emptySlots = columns - 1 - itemsOnLastRow;
  105. return emptySlots * spanPerItem;
  106. }
  107. });
  108. const handleSearch = () => {
  109. props.onSearch(); // 调用父组件的查询事件
  110. };
  111. const handleReset = () => {
  112. props.onReset(); // 调用父组件的重置事件
  113. };
  114. </script>
  115. <style lang="scss">
  116. .el-inputs {
  117. width: 100% !important;
  118. }
  119. </style>