mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-11-30 11:08:38 +08:00
feat: 高级搜索-点击搜索时校验
This commit is contained in:
parent
e759d15c15
commit
65c99f96fc
@ -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>
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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': '筛选内容不能为空',
|
||||
};
|
||||
|
@ -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[]; // 级联选择的选项
|
||||
|
17
frontend/src/enums/advancedFilterEnum.ts
Normal file
17
frontend/src/enums/advancedFilterEnum.ts
Normal 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 {};
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user