feat: 高级搜索-点击搜索时校验

This commit is contained in:
teukkk 2024-08-30 12:38:13 +08:00 committed by 刘瑞斌
parent e759d15c15
commit 65c99f96fc
7 changed files with 177 additions and 88 deletions

View File

@ -22,15 +22,20 @@
</div>
</template>
<a-form ref="formRef" :model="formModel" layout="vertical">
<a-select v-model="formModel.andOrType" :options="andOrTypeOptions" class="w-[170px]">
<a-select v-model="formModel.andOrType" :options="andOrTypeOptions" class="mb-[12px] w-[170px]">
<template #prefix> {{ t('advanceFilter.meetTheFollowingConditions') }} </template>
</a-select>
<div
v-for="(item, listIndex) in formModel.list"
:key="item.dataIndex || `filter_item_${listIndex}`"
class="flex items-center gap-[8px]"
class="flex items-start gap-[8px]"
>
<a-form-item class="flex-1 overflow-hidden" :field="`list[${listIndex}].dataIndex`" hide-asterisk>
<a-form-item
class="flex-1 overflow-hidden"
:field="`list[${listIndex}].dataIndex`"
hide-asterisk
:rules="[{ required: true, message: t('advanceFilter.conditionRequired') }]"
>
<a-select
v-model="item.dataIndex"
allow-search
@ -57,7 +62,18 @@
</a-option>
</a-select>
</a-form-item>
<a-form-item class="flex-1 overflow-hidden" :field="`list[${listIndex}].value`" hide-asterisk>
<a-form-item
class="flex-1 overflow-hidden"
:field="`list[${listIndex}].value`"
hide-asterisk
:rules="[
{
validator: (value, callback) => {
validateFilterValue(item, value, callback);
},
},
]"
>
<a-input
v-if="item.type === FilterType.INPUT"
v-model:model-value="item.value"
@ -110,7 +126,13 @@
:data="item.treeSelectData"
:disabled="isValueDisabled(item)"
v-bind="item.treeSelectProps"
/>
>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tr">
<div class="one-line-text max-w-[170px]">{{ node.name }}</div>
</a-tooltip>
</template>
</a-tree-select>
<a-date-picker
v-else-if="item.type === FilterType.DATE_PICKER && item.operator !== 'between'"
v-model:model-value="item.value"
@ -202,6 +224,7 @@
import { useI18n } from '@/hooks/useI18n';
import { SelectValue } from '@/models/projectManagement/menuManagement';
import { OperatorEnum } from '@/enums/advancedFilterEnum';
import { defaultFormModelList, operatorOptionsMap } from './index';
import { AccordBelowType, BackEndEnum, FilterFormItem, FilterResult, FilterType } from './type';
@ -238,6 +261,17 @@
{ value: 'OR', label: t('advanceFilter.or') },
];
const formRef = ref<FormInstance>();
function validateFilterValue(item: FilterFormItem, value: string | undefined, callback: (error?: string) => void) {
if (
item.dataIndex?.length &&
item.operator?.length &&
!['EMPTY', 'NOT_EMPTY'].includes(item.operator as string) &&
!value?.length
) {
callback(t('advanceFilter.filterContentRequired'));
}
}
function getListItemByDataIndex(dataIndex: string) {
return [...props.configList, ...(props.customList || [])].find((item) => item.dataIndex === dataIndex);
}
@ -266,10 +300,27 @@
if (!listItem) return;
formModel.value.list[index] = { ...listItem };
formModel.value.list[index].value = valueIsArray(listItem) ? [] : '';
// //
if (!formModel.value.list[index].operator?.length) {
const optionsValueList = operatorOptionsMap[formModel.value.list[index].type].map(
(optionItem) => optionItem.value
);
if (optionsValueList.includes(OperatorEnum.LIKE)) {
formModel.value.list[index].operator = OperatorEnum.LIKE;
} else if (optionsValueList.includes(OperatorEnum.BELONG_TO)) {
formModel.value.list[index].operator = OperatorEnum.BELONG_TO;
} else if (optionsValueList.includes(OperatorEnum.EQUAL)) {
formModel.value.list[index].operator = OperatorEnum.EQUAL;
}
}
}
//
function operatorChange(item: FilterFormItem, index: number) {
formModel.value.list[index].value = valueIsArray(item) ? [] : '';
if (['EMPTY', 'NOT_EMPTY'].includes(formModel.value.list[index].operator as string)) {
formRef.value?.validate();
}
}
function isValueDisabled(item: FilterFormItem) {
return !item.dataIndex || ['EMPTY', 'NOT_EMPTY'].includes(item.operator as string);
@ -283,14 +334,12 @@
const item = {
dataIndex: '',
type: FilterType.INPUT,
operator: '',
value: '',
backendType: BackEndEnum.STRING,
};
formModel.value.list.push(item);
}
const formRef = ref<FormInstance>();
function handleFilter() {
formRef.value?.validate((errors) => {
if (!errors) {
@ -310,6 +359,11 @@
<style lang="less" scoped>
:deep(.arco-form-item) {
margin-bottom: 8px;
.arco-form-item-label-col {
display: none;
}
.arco-form-item-message {
margin-bottom: 2px;
}
}
</style>

View File

@ -1,23 +1,28 @@
import { OperatorEnum } from '@/enums/advancedFilterEnum';
import { BackEndEnum, FilterType } from './type';
export { default as MsAdvanceFilter } from './index.vue';
export const LIKE = { label: 'advanceFilter.operator.contains', value: 'like' }; // 包含
export const NOT_LIKE = { label: 'advanceFilter.operator.not_contains', value: 'not_like' }; // 不包含
export const GT = { label: 'advanceFilter.operator.gt', value: 'GT' }; // 大于
export const LIKE = { label: 'advanceFilter.operator.contains', value: OperatorEnum.LIKE }; // 包含
export const NOT_LIKE = { label: 'advanceFilter.operator.not_contains', value: OperatorEnum.NOT_LIKE }; // 不包含
export const BELONG_TO = { label: 'advanceFilter.operator.belongTo', value: OperatorEnum.BELONG_TO }; // 属于
export const NOT_BELONG_TO = { label: 'advanceFilter.operator.notBelongTo', value: OperatorEnum.NOT_BELONG_TO }; // 不属于
export const GT = { label: 'advanceFilter.operator.gt', value: OperatorEnum.GT }; // 大于
export const GE = { label: 'advanceFilter.operator.ge', value: 'GT_OR_EQUALS' }; // 大于等于
export const LT = { label: 'advanceFilter.operator.lt', value: 'LT' }; // 小于
export const LT = { label: 'advanceFilter.operator.lt', value: OperatorEnum.LT }; // 小于
export const LE = { label: 'advanceFilter.operator.le', value: 'LT_OR_EQUALS' }; // 小于等于
export const EQUAL = { label: 'advanceFilter.operator.equal', value: 'EQUALS' }; // 等于
export const NOT_EQUAL = { label: 'advanceFilter.operator.notEqual', value: 'NOT_EQUALS' }; // 不等于
export const BETWEEN = { label: 'advanceFilter.operator.between', value: 'between' }; // 介于
export const EQUAL = { label: 'advanceFilter.operator.equal', value: OperatorEnum.EQUAL }; // 等于
export const NOT_EQUAL = { label: 'advanceFilter.operator.notEqual', value: OperatorEnum.NOT_EQUAL }; // 不等于
export const BETWEEN = { label: 'advanceFilter.operator.between', value: OperatorEnum.BETWEEN }; // 介于
export const EMPTY = { label: 'advanceFilter.operator.empty', value: OperatorEnum.EMPTY }; // 为空
export const NOT_EMPTY = { label: 'advanceFilter.operator.not_empty', value: OperatorEnum.NOT_EMPTY }; // 不为空
export const NO_CHECK = { label: 'advanceFilter.operator.no_check', value: 'UNCHECK' }; // 不校验
export const CONTAINS = { label: 'advanceFilter.operator.contains', value: 'CONTAINS' }; // 包含
export const NO_CONTAINS = { label: 'advanceFilter.operator.not_contains', value: 'NOT_CONTAINS' }; // 不包含
export const START_WITH = { label: 'advanceFilter.operator.start_with', value: 'START_WITH' }; // 以...开始
export const END_WITH = { label: 'advanceFilter.operator.end_with', value: 'END_WITH' }; // 以...结束
export const EMPTY = { label: 'advanceFilter.operator.empty', value: 'EMPTY' }; // 为空
export const NOT_EMPTY = { label: 'advanceFilter.operator.not_empty', value: 'NOT_EMPTY' }; // 不为空
export const REGEX = { label: 'advanceFilter.operator.regexp', value: 'REGEX' }; // 正则匹配
export const LENGTH_EQUAL = { label: 'advanceFilter.operator.length.equal', value: 'LENGTH_EQUALS' }; // 长度等于
export const LENGTH_GT = { label: 'advanceFilter.operator.length.gt', value: 'LENGTH_GT' }; // 长度大于
@ -26,7 +31,7 @@ export const LENGTH_LT = { label: 'advanceFilter.operator.length.lt', value: 'LE
export const LENGTH_LE = { label: 'advanceFilter.operator.length.le', value: 'LENGTH_LT_OR_EQUALS' }; // 长度小于等于
const COMMON_TEXT_OPERATORS = [LIKE, NOT_LIKE, EMPTY, NOT_EMPTY, EQUAL, NOT_EQUAL];
const COMMON_SELECTION_OPERATORS = [LIKE, NOT_LIKE, EMPTY, NOT_EMPTY];
const COMMON_SELECTION_OPERATORS = [BELONG_TO, NOT_BELONG_TO, EMPTY, NOT_EMPTY];
export const operatorOptionsMap: Record<string, { value: string; label: string }[]> = {
[FilterType.INPUT]: COMMON_TEXT_OPERATORS,
@ -36,7 +41,7 @@ export const operatorOptionsMap: Record<string, { value: string; label: string }
[FilterType.CHECKBOX]: COMMON_SELECTION_OPERATORS,
[FilterType.SELECT]: COMMON_SELECTION_OPERATORS,
[FilterType.TAGS_INPUT]: [EMPTY, LIKE, NOT_LIKE, LENGTH_LT, LENGTH_GT],
[FilterType.TREE_SELECT]: [LIKE, NOT_LIKE],
[FilterType.TREE_SELECT]: [BELONG_TO, NOT_BELONG_TO],
[FilterType.DATE_PICKER]: [BETWEEN, EQUAL, EMPTY, NOT_EMPTY],
};
@ -168,7 +173,7 @@ export const defaultFormModelList = [
dataIndex: 'id',
title: 'caseManagement.featureCase.tableColumnID',
type: FilterType.INPUT,
operator: '',
operator: OperatorEnum.LIKE,
value: '',
backendType: BackEndEnum.STRING,
},
@ -176,7 +181,7 @@ export const defaultFormModelList = [
dataIndex: 'name',
label: 'common.name',
type: FilterType.INPUT,
operator: '',
operator: OperatorEnum.LIKE,
value: '',
backendType: BackEndEnum.STRING,
},
@ -184,7 +189,7 @@ export const defaultFormModelList = [
dataIndex: 'moduleId',
label: 'common.belongModule',
type: FilterType.TREE_SELECT,
operator: '',
operator: OperatorEnum.BELONG_TO,
value: '',
backendType: BackEndEnum.STRING,
},

View File

@ -8,6 +8,8 @@ export default {
'advanceFilter.operator.no_check': 'uncheck',
'advanceFilter.operator.contains': 'contain',
'advanceFilter.operator.not_contains': 'exclude',
'advanceFilter.operator.belongTo': 'belong to',
'advanceFilter.operator.notBelongTo': 'not belong to',
'advanceFilter.operator.start_with': 'with...start',
'advanceFilter.operator.end_with': 'with...end',
'advanceFilter.operator.empty': 'empty',
@ -26,4 +28,6 @@ export default {
'advanceFilter.or': 'Or',
'advanceFilter.inputPlaceholder': 'Separate keywords with spaces',
'advanceFilter.addCondition': 'Add conditions',
'advanceFilter.conditionRequired': 'The query condition cannot be empty',
'advanceFilter.filterContentRequired': 'The filtering content cannot be empty',
};

View File

@ -9,6 +9,8 @@ export default {
'advanceFilter.operator.no_check': '不校验',
'advanceFilter.operator.contains': '包含',
'advanceFilter.operator.not_contains': '不包含',
'advanceFilter.operator.belongTo': '属于',
'advanceFilter.operator.notBelongTo': '不属于',
'advanceFilter.operator.start_with': '以...开始',
'advanceFilter.operator.end_with': '以...结束',
'advanceFilter.operator.empty': '为空',
@ -27,4 +29,6 @@ export default {
'advanceFilter.or': '任一',
'advanceFilter.inputPlaceholder': '关键字之间以空格进行分隔',
'advanceFilter.addCondition': '添加条件',
'advanceFilter.conditionRequired': '查询条件不能为空',
'advanceFilter.filterContentRequired': '筛选内容不能为空',
};

View File

@ -1,5 +1,7 @@
import type { MsSearchSelectProps, RadioProps } from '@/components/business/ms-select';
import { OperatorEnum } from '@/enums/advancedFilterEnum';
import type { CascaderOption, TreeNodeData } from '@arco-design/web-vue';
import type { VirtualListProps } from '@arco-design/web-vue/es/_components/virtual-list-v2/interface';
import type { TreeSelectProps } from '@arco-design/web-vue/es/tree-select/interface';
@ -50,7 +52,7 @@ export enum FilterType {
export interface FilterFormItem {
dataIndex?: string; // 第一列下拉的value
title?: string; // 第一列下拉显示的label
operator?: string; // 第二列的值
operator?: OperatorEnum; // 第二列的值
type: FilterType; // 类型:判断第二列下拉数据和第三列显示形式
value?: any; // 第三列的值
cascaderOptions?: CascaderOption[]; // 级联选择的选项

View File

@ -0,0 +1,17 @@
export enum OperatorEnum {
LIKE = 'like',
NOT_LIKE = 'not_like',
BELONG_TO = 'BELONG_TO',
NOT_BELONG_TO = 'NOT_BELONG_TO',
GT = 'GT',
LT = 'LT',
EQUAL = 'EQUALS', // 有其他地方用到
NOT_EQUAL = 'NOT_EQUALS', // 有其他地方用到
EMPTY = 'EMPTY', // 有其他地方用到
NOT_EMPTY = 'NOT_EMPTY', // 有其他地方用到
BETWEEN = 'between',
LENGTH_LT = 'LENGTH_LT',
LENGTH_GT = 'LENGTH_GT',
}
export default {};

View File

@ -771,77 +771,80 @@
],
};
const filterConfigList = ref<FilterFormItem[]>([]);
const searchCustomFields = ref<FilterFormItem[]>([]);
const memberOptions = ref<{ label: string; value: string }[]>([]);
const filterConfigList = computed<FilterFormItem[]>(() => [
{
title: 'caseManagement.featureCase.tableColumnID',
dataIndex: 'id',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnName',
dataIndex: 'name',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnModule',
dataIndex: 'moduleId',
type: FilterType.TREE_SELECT,
treeSelectData: caseTreeData.value,
treeSelectProps: {
fieldNames: {
title: 'name',
key: 'id',
children: 'children',
},
multiple: true,
treeCheckable: true,
treeCheckStrictly: true,
maxTagCount: 2,
},
},
{
title: 'caseManagement.featureCase.tableColumnVersion',
dataIndex: 'versionId',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnCreateUser',
dataIndex: 'createUserName',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions.value,
},
},
{
title: 'caseManagement.featureCase.tableColumnCreateTime',
dataIndex: 'createTime',
type: FilterType.DATE_PICKER,
},
{
title: 'caseManagement.featureCase.tableColumnUpdateUser',
dataIndex: 'updateUserName',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions.value,
},
},
{
title: 'caseManagement.featureCase.tableColumnUpdateTime',
dataIndex: 'updateTime',
type: FilterType.DATE_PICKER,
},
{
title: 'caseManagement.featureCase.tableColumnTag',
dataIndex: 'tags',
type: FilterType.TAGS_INPUT,
},
]);
const searchCustomFields = ref<FilterFormItem[]>([]);
async function initFilter() {
const result = await getCustomFieldsTable(currentProjectId.value);
memberOptions.value = await getProjectOptions(appStore.currentProjectId, keyword.value);
memberOptions.value = memberOptions.value.map((e: any) => ({ label: e.name, value: e.id }));
filterConfigList.value = [
{
title: 'caseManagement.featureCase.tableColumnID',
dataIndex: 'id',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnName',
dataIndex: 'name',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnModule',
dataIndex: 'moduleId',
type: FilterType.TREE_SELECT,
treeSelectData: caseTreeData.value,
treeSelectProps: {
fieldNames: {
title: 'name',
key: 'id',
children: 'children',
},
},
},
{
title: 'caseManagement.featureCase.tableColumnVersion',
dataIndex: 'versionId',
type: FilterType.INPUT,
},
{
title: 'caseManagement.featureCase.tableColumnCreateUser',
dataIndex: 'createUserName',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions.value,
},
},
{
title: 'caseManagement.featureCase.tableColumnCreateTime',
dataIndex: 'createTime',
type: FilterType.DATE_PICKER,
},
{
title: 'caseManagement.featureCase.tableColumnUpdateUser',
dataIndex: 'updateUserName',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
options: memberOptions.value,
},
},
{
title: 'caseManagement.featureCase.tableColumnUpdateTime',
dataIndex: 'updateTime',
type: FilterType.DATE_PICKER,
},
{
title: 'caseManagement.featureCase.tableColumnTag',
dataIndex: 'tags',
type: FilterType.TAGS_INPUT,
},
];
//
searchCustomFields.value = result.map((item: any) => {
const FilterTypeKey: keyof typeof FilterType = CustomTypeMaps[item.type].type;