vue3+arco design通过动态表单方式实现自定义筛选

目录

1.说明

2.示例

3.运行截图

[​编辑 4.总结](#编辑 4.总结)


1.说明

(1) 本文主要实现通过动态表单的方式实现自定义筛选的功能,用户可以自己添加筛选的项目,筛选条件及筛选内容。

(2) 每个项目的筛选包含筛选项目,筛选条件,筛选方式及筛选值。

筛选项目代表表中的字段,如姓名,年龄等

筛选条件包含等于,不等于,大于,小于,包含等

筛选类型包含手动输入值的方式,通过下拉进行选择的方式,和表中的其他字段进行比较的方式

筛选值可以手动输入,可以通过下拉进行选择,也可以选择某个日期。

2.示例

javascript 复制代码
<template>
  <a-modal :visible="visibleFlag" @ok="handleOk" @cancel="handleCancel" unmountOnClose width="auto">
    <div style="text-align: right">
      <a-button @click="handleAdd()">Add</a-button>
    </div>
    <a-form :model="form" layout='vertical' style="width: 1000px;height: 400px;" ref="formRef">
      <div v-for="(item,index) in form.data">
        <a-row :gutter="10">
          <a-col :span="5">
            <a-form-item :field="`data[${index}].filterColumn`" label="Column" :hide-label="index != 0"
                         :rules="[{required:true,message:'Column不能为空'}]"
                         validate-trigger="blur">
              <a-select v-model="item.filterColumn" :style="{width:'500px'}" placeholder="Please select ..."
                        @blur="columnBlur(form.data[index].filterColumn, form.data[index].filterType , index)" allow-clear>
                <a-option v-for="item of columns" :value="item.value" :label="item.label"/>
              </a-select>
            </a-form-item>
          </a-col>
          <a-col :span="6">
            <a-form-item :field="`data[${index}].filterOperation`" label="Operation" :hide-label="index != 0"
                         :rules="[{required:true,message:'Operation不能为空'}]"
                         validate-trigger="blur">
              <a-select v-model="item.filterOperation" :style="{width:'500px'}"
                        placeholder="Please select ..." allow-clear>
                <a-option v-for="item of filterOpeData[index]" :value="item.value" :label="item.label"/>
              </a-select>
            </a-form-item>
          </a-col>
          <a-col :span="5">
            <a-form-item :field="`data[${index}].filterType`" label="Type" :hide-label="index != 0"
                         :rules="[{required:true,message:'Type不能为空'}]"
                         validate-trigger="blur">
              <a-select v-model="item.filterType" :style="{width:'500px'}" placeholder="Please select ..."
                        @blur="typeBlur(form.data[index].filterColumn, form.data[index].filterType, index)" allow-clear>
                <a-option v-for="item of filterTypeData" :value="item.value" :label="item.label"/>
              </a-select>
            </a-form-item>
          </a-col>
          <a-col :span="5">
            <a-form-item :field="`data[${index}].filterValue`" label="Value" :hide-label="index != 0"
                         :rules="[{required:true,message:'Value不能为空'}]"
                         validate-trigger="blur">
              <a-input v-model="item.filterValue" :style="{width:'500px'}"
                       placeholder="Please enter something"
                       allow-clear v-if="valueFlag[index] == 0"/>
              <a-select v-model="item.filterValue" :style="{width:'400px'}" placeholder="Please select ..."
                        v-if="valueFlag[index] == 1" allow-clear>
                <a-option v-for="item of colValList[index]" :value="item.value" :label="item.label"/>
              </a-select>
              <a-date-picker v-model="item.filterValue" :style="{width:'400px'}"
                             v-if="valueFlag[index] == 2" />
            </a-form-item>
          </a-col>
          <a-col :span="3">
            <a-button v-show="index != 0" @click="handleDelete(index)" :style="{marginLeft:'10px'}">Delete</a-button>
          </a-col>
        </a-row>
      </div>
    </a-form>
  </a-modal>
</template>

<script lang="ts" setup>
import {onMounted, ref} from 'vue';
import {Message} from '@arco-design/web-vue';
import {FormInstance} from '@arco-design/web-vue/es/form';


const formRef = ref<FormInstance>();
const visibleFlag = ref(false)

// 0:输入框;1:下拉选择器;2:日期
const valueFlag = ref([] as any)
valueFlag.value.push(0)

const form = ref({
  data: [{
    filterColumn: '',
    filterOperation: '',
    filterType: '',
    filterValue: ''
  }]
})

const colValList = ref([] as any)

const filterOpeData = ref([] as any)

const filterTypeData = [{
  value: '0',
  label: 'Value',
}, {
  value: '1',
  label: 'Column',
}, {
  value: '2',
  label: 'User List',
}]

const operationData1 = [
  {value: '1', label: 'EQUAL'},
  {value: '2', label: 'GREATER_THAN'},
  {value: '3', label: 'GREATER_THAN_OR_EQUAL'},
  {value: '4', label: 'LESS_THAN'},
  {value: '5', label: 'LESS_THAN_OR_EQUAL'},
  {value: '6', label: 'NOT_EQUAL'}]
const operationData2 = [
  {value: '1', label: 'EQUAL'},
  {value: '2', label: 'GREATER_THAN'},
  {value: '3', label: 'GREATER_THAN_OR_EQUAL'},
  {value: '4', label: 'LESS_THAN'},
  {value: '5', label: 'LESS_THAN_OR_EQUAL'},
  {value: '6', label: 'NOT_EQUAL'},
  {value: '9', label: 'CONTAINS'},
  {value: '10', label: 'STARTS_WITH'},
  {value: '11', label: 'ENDS_WITH'},
  {value: '12', label: 'DOES_NOT_START_WITH'},
  {value: '13', label: 'DOES_NOT_END_WITH'},
  {value: '14', label: 'DOES_NOT_CONTAIN'},
  {value: '15', label: 'STARTS_OR_ENDS_WITH'}
]

const handleOk = async () => {
  const validRes = await formRef.value?.validate();
  if (!validRes) {
    visibleFlag.value = false;
    console.log(JSON.stringify(form.value.data))
  } else {
    console.log("校验失败")
  }
}

const handleCancel = () => {
  visibleFlag.value = false;
}

const handleDelete = (index: any) => {
  form.value.data.splice(index, 1);
  valueFlag.value.splice(index, 1);
  colValList.value.splice(index, 1);

}

const handleAdd = () => {
  form.value.data.push({
    filterColumn: '',
    filterOperation: '',
    filterType: '',
    filterValue: ''
  })
  valueFlag.value.push(0)
}

const columnBlur = (colVal: any, typeVal: any, index: any) => {
  if (!colVal) {
    return
  }
  const col = columns.value.find((item: { value: any; }) => item.value == colVal)
  if (col.type == 'text' || col.type == 'number') {
    filterOpeData.value[index] = operationData2
  } else {
    filterOpeData.value[index] = operationData1
  }
  if (col.type == 'list' && typeVal != '1') {
    valueFlag.value[index] = 1
    colValList.value[index] = [{
      value: '1',
      label: '处理提交'
    }, {
      value: '2',
      label: '处理中'
    }, {
      value: '3',
      label: '处理完成'
    }]
  }
  if (col.type == 'date') {
    valueFlag.value[index] = 2
  }
}

const typeBlur = (colVal: any, typeVal: any, index: any) => {
  if (typeVal == '1') {
    if (!colVal) {
      Message.error({content: 'column信息不能为空', position: 'top'});
      return
    } else {
      const col = columns.value.find((item: { value: any; }) => item.value == colVal)
      if (col.type == 'list') {
        Message.error({content: 'list类型的column不支持和其他列进行比较', position: 'top'});
        form.value.data[index].filterType = ''
        return
      }
      valueFlag.value[index] = 1
      if (col.type == 'date') {
        colValList.value[index] = columns.value.filter((item: { type: string; }) => item.type == 'date')
      } else if (col.type == 'text') {
        colValList.value[index] = columns.value.filter((item: { type: string; }) => item.type == 'text' || item.type == 'number')
      } else if (col.type == 'number') {
        colValList.value[index] = columns.value.filter((item: { type: string; }) => item.type == 'number')
      } else if (col.type == 'list') {
      } else {
        colValList.value[index] = []
      }
    }
  }

}

// 打开弹窗
const openDialog = () => {
  visibleFlag.value = true;
};
// 导出方法在父组件中进行使用
defineExpose({openDialog});

const columns = ref()
const getColumnList = () => {
  columns.value = [{
    value: '12',
    label: 'genoId',
    type: 'text'
  },
    {
      value: '13',
      label: 'status',
      type: 'list'
    },
    {
      value: '14',
      label: 'createTime',
      type: 'date'
    },
    {
      value: '15',
      label: 'qty',
      type: 'number'
    }
  ]
}

// 组件完成初始渲染并创建 DOM 节点后运行
onMounted(async () => {
  await getColumnList()
})

</script>

<script lang="ts">
export default {
  name: "dynamicFilter"
}
</script>

<style scoped>

</style>

说明:

①通过表单的方式进行实现,表单关联的数据源form是对象格式,如下:

javascript 复制代码
const form = ref({
  data: [{
    filterColumn: '',
    filterOperation: '',
    filterType: '',
    filterValue: ''
  }]
})

对象内是一个id为data的数组,数组内的每条数组则存储一个项目的筛选内容,包含筛选项目,筛选条件,筛选方式及筛选值。数据源设置为对象是为了进行校验处理,如果是数组格式,无法实现动态表单的的校验(不同的前端框架,可能会有所不同,但推荐使用对象格式)

②动态表单的实现
在form标签内嵌套div标签,并在div标签上进行循环form.data,如果动态的项目只有一个,可以直接在form-item上进行循环处理。具体的每个表单项可以通过form.data[index].表单项名的方式进行绑定,也可以通过item.表单项名的方式进行绑定。点击新增按钮,会向form.data中push一条新数据,添加的筛选项目后面有删除按钮,点击删除按钮,通过form.data.splice方法删除数组中的元素。

③动态表单的校验处理

在每个表单项中添加校验规则,不要在form上添加校验规则

在arco design框架中的动态表单校验如下:

javascript 复制代码
            <a-form-item :field="`data[${index}].filterColumn`" label="Column" :hide-label="index != 0"
                         :rules="[{required:true,message:'Column不能为空'}]"
                         validate-trigger="blur">
              <a-select v-model="item.filterColumn" :style="{width:'500px'}" placeholder="Please select ..."
                        @blur="columnBlur(form.data[index].filterColumn, form.data[index].filterType , index)" allow-clear>
                <a-option v-for="item of columns" :value="item.value" :label="item.label"/>
              </a-select>
            </a-form-item>

通过rules关联校验规则,注意field属性的设置,在arco design中field需要进行如上设置,结果例如:data[0].filterColumn。

在arco design中是设置field属性,在element ui中是设置prop属性,并且内容的设置方式也不一样,需要注意。

④提交时的校验

当前表单嵌套在对话框中,点击确定按钮时,需要先进行表单的校验处理,如下:

javascript 复制代码
const handleOk = async () => {
  const validRes = await formRef.value?.validate();
  if (!validRes) {
    visibleFlag.value = false;
    console.log(JSON.stringify(form.value.data))
  } else {
    console.log("校验失败")
  }
}

通过调用表单的ref对象的validate方法,如果校验失败则返回失败的项目的field属性内容及错误信息,如果成功则返回undefined,所以通过判断返回结果就可以判断校验成功与否。

校验失败的返回信息如下

{
	"data[0].filterColumn": {
		"label": "Column",
		"field": "data[0].filterColumn",
		"type": "string",
		"isRequiredError": true,
		"message": "Column不能为空"
	},
	"data[0].filterOperation": {
		"label": "Operation",
		"field": "data[0].filterOperation",
		"type": "string",
		"isRequiredError": true,
		"message": "Operation不能为空"
	},
	"data[0].filterType": {
		"label": "Type",
		"field": "data[0].filterType",
		"type": "string",
		"isRequiredError": true,
		"message": "Type不能为空"
	},
	"data[0].filterValue": {
		"label": "Value",
		"field": "data[0].filterValue",
		"type": "string",
		"isRequiredError": true,
		"message": "Value不能为空"
	},
	"data[1].filterColumn": {
		"label": "Column",
		"field": "data[1].filterColumn",
		"type": "string",
		"isRequiredError": true,
		"message": "Column不能为空"
	},
	"data[1].filterOperation": {
		"label": "Operation",
		"field": "data[1].filterOperation",
		"type": "string",
		"isRequiredError": true,
		"message": "Operation不能为空"
	},
	"data[1].filterType": {
		"label": "Type",
		"field": "data[1].filterType",
		"type": "string",
		"isRequiredError": true,
		"message": "Type不能为空"
	},
	"data[1].filterValue": {
		"label": "Value",
		"field": "data[1].filterValue",
		"type": "string",
		"isRequiredError": true,
		"message": "Value不能为空"
	},
	"data[2].filterColumn": {
		"label": "Column",
		"field": "data[2].filterColumn",
		"type": "string",
		"isRequiredError": true,
		"message": "Column不能为空"
	},
	"data[2].filterOperation": {
		"label": "Operation",
		"field": "data[2].filterOperation",
		"type": "string",
		"isRequiredError": true,
		"message": "Operation不能为空"
	},
	"data[2].filterType": {
		"label": "Type",
		"field": "data[2].filterType",
		"type": "string",
		"isRequiredError": true,
		"message": "Type不能为空"
	},
	"data[2].filterValue": {
		"label": "Value",
		"field": "data[2].filterValue",
		"type": "string",
		"isRequiredError": true,
		"message": "Value不能为空"
	}
}

校验通过后输出的form.data信息,如下:

[{
	"filterColumn": "12",
	"filterOperation": "1",
	"filterType": "0",
	"filterValue": "23"
}, {
	"filterColumn": "13",
	"filterOperation": "1",
	"filterType": "0",
	"filterValue": "1"
}, {
	"filterColumn": "14",
	"filterOperation": "2",
	"filterType": "0",
	"filterValue": "2024-05-14"
}]

3.运行截图

校验失败

输入内容后,校验通过

4.总结

①注意表单绑定的数据源的格式及不同前端框架的校验的方式

②为了使画面更加友好,可以将表单放在表格中

相关推荐
燃先生._.42 分钟前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖2 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235242 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240253 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar3 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人3 小时前
前端知识补充—CSS
前端·css
GISer_Jing3 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245523 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v4 小时前
webpack最基础的配置
前端·webpack·node.js