feat: 高级搜索组件部分逻辑修改

This commit is contained in:
RubyLiu 2023-11-21 18:26:23 +08:00 committed by 刘瑞斌
parent 4910959a66
commit 15cd2c8bc4
7 changed files with 214 additions and 95 deletions

View File

@ -46,9 +46,19 @@
class="hidden-item"
:rules="[{ required: true, message: t('advanceFilter.plaseSelectOperator') }]"
>
<a-select v-model="item.operator" class="w-[100px]" :disabled="!item.dataIndex">
<a-option value="equal">{{ t('advanceFilter.operator.equal') }}</a-option>
<a-option value="notEqual">{{ t('advanceFilter.operator.notEqual') }}</a-option>
<a-select
v-model="item.operator"
class="w-[120px]"
:disabled="!item.dataIndex"
@change="(v) => operationChange(v, item.dataIndex as string, idx)"
>
<a-option
v-for="option in getOperationOption(item.type as FilterType, item.dataIndex as string)"
:key="option.value"
:value="option.value"
>
{{ t(option.label as string) }}
</a-option>
</a-select>
</a-form-item>
</div>
@ -67,18 +77,27 @@
:disabled="!item.dataIndex"
:max-length="60"
/>
<a-select
<a-input-number
v-if="item.type === FilterType.NUMBER"
v-model:model-value="item.value"
class="w-full"
allow-clear
:disabled="!item.dataIndex"
:max-length="60"
/>
<MsSelect
v-else-if="item.type === FilterType.SELECT"
v-model:model-value="item.value"
class="w-full"
allow-clear
allow-search
:option="item.options"
:placeholder="t('advanceFilter.pleaseSelect')"
:placeholder="t('common.pleaseSelect')"
:disabled="!item.dataIndex"
></a-select>
:options="item.selectProps?.options || []"
v-bind="item.selectProps"
></MsSelect>
<a-date-picker
v-else-if="item.type === FilterType.DATE_PICKER"
v-else-if="item.type === FilterType.DATE_PICKER && item.operator !== 'between'"
v-model:model-value="item.value"
class="w-full"
show-time
@ -86,7 +105,7 @@
:disabled="!item.dataIndex"
/>
<a-range-picker
v-else-if="item.type === FilterType.RANGE_PICKER"
v-else-if="item.type === FilterType.DATE_PICKER && item.operator === 'between'"
v-model:model-value="item.value"
class="w-full"
show-time
@ -124,10 +143,13 @@
<script lang="ts" setup>
import { FormInstance } from '@arco-design/web-vue';
import MsSelect from '@/components/business/ms-select';
import { useI18n } from '@/hooks/useI18n';
import { SelectValue } from '@/models/projectManagement/menuManagement';
import { OPERATOR_MAP } from './index';
import { AccordBelowType, BackEndEnum, FilterFormItem, FilterResult, FilterType } from './type';
const { t } = useI18n();
@ -136,13 +158,40 @@
const formModel = reactive<{ list: FilterFormItem[] }>({
list: [],
});
const props = defineProps<{ configList: FilterFormItem[]; visible: boolean; count: number }>();
const props = defineProps<{ configList: FilterFormItem[]; visible: boolean; count: number; rowCount: number }>();
const emit = defineEmits<{
(e: 'onSearch', value: FilterResult): void;
(e: 'dataIndexChange', value: string): void;
(e: 'update:count', value: number): void;
(e: 'update:count', value: number): void; // FilterIcon
(e: 'update:rowCount', value: number): void; // MsBaseTable
}>();
const isMutipleSelect = (dataIndex: string) => {
const tmpObj = props.configList.find((item) => item.dataIndex === dataIndex);
if (tmpObj) {
return tmpObj.selectProps?.multiple;
}
return false;
};
const getOperationOption = (type: FilterType, dataIndex: string) => {
let result = [];
switch (type) {
case FilterType.NUMBER:
result = OPERATOR_MAP.number;
break;
case FilterType.DATE_PICKER:
result = OPERATOR_MAP.date;
break;
case FilterType.SELECT:
result = isMutipleSelect(dataIndex) ? OPERATOR_MAP.array : OPERATOR_MAP.string;
break;
default:
result = OPERATOR_MAP.string;
}
return result;
};
//
const getCurrentOptionArr = () => {
const arr1 = props.configList;
@ -236,15 +285,17 @@
formModel.list[idx].operator = '';
formModel.list[idx].backendType = backendType;
formModel.list[idx].type = type;
if (
formModel.list[idx].type === FilterType.RANGE_PICKER ||
formModel.list[idx].type === FilterType.MUTIPLE_SELECT
) {
formModel.list[idx].value = isMutipleSelect(dataIndex as string) ? [] : '';
emit('dataIndexChange', dataIndex as string);
};
const operationChange = (v: SelectValue, dataIndex: string, idx: number) => {
if (v === 'between') {
formModel.list[idx].value = [];
} else {
formModel.list[idx].value = '';
formModel.list[idx].value = isMutipleSelect(dataIndex) ? [] : '';
}
emit('dataIndexChange', dataIndex as string);
};
onBeforeMount(() => {

View File

@ -1,36 +0,0 @@
<template>
<MsTag
:type="props.visible ? 'primary' : 'default'"
:theme="props.visible ? 'lightOutLine' : 'outline'"
size="large"
class="mt-[3px] min-w-[64px] cursor-pointer"
>
<span :class="!props.visible ? 'text-[var(--color-text-4)]' : ''" @click="handleOpenFilter">
<icon-filter class="text-[16px]" />
<span class="ml-[4px]">
<span v-if="props.count">{{ props.count }}</span>
{{ t('common.filter') }}
</span>
</span>
</MsTag>
</template>
<script setup lang="ts">
import MsTag from '../ms-tag/ms-tag.vue';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
count?: number;
}>();
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'update:count', value: number): void;
}>();
const handleOpenFilter = () => {
emit('update:visible', !props.visible);
};
</script>

View File

@ -1,2 +1,21 @@
export { default as FilterForm } from './FilterForm.vue';
export { default as FilterIcon } from './FilterIcon.vue';
export { default as MsAdvanceFilter } from './index.vue';
const IN = { label: 'advanceFilter.operator.in', value: 'in' };
const NOT_IN = { label: 'advanceFilter.operator.not_in', value: 'not_in' };
const LIKE = { label: 'advanceFilter.operator.like', value: 'like' };
const NOT_LIKE = { label: 'advanceFilter.operator.not_like', value: 'not_like' };
const GT = { label: 'advanceFilter.operator.gt', value: 'gt' };
const GE = { label: 'advanceFilter.operator.ge', value: 'ge' };
const LT = { label: 'advanceFilter.operator.lt', value: 'lt' };
const LE = { label: 'advanceFilter.operator.le', value: 'le' };
const EQUAL = { label: 'advanceFilter.operator.equal', value: 'equal' };
const NOT_EQUAL = { label: 'advanceFilter.operator.notEqual', value: 'notEqual' };
const BETWEEN = { label: 'advanceFilter.operator.between', value: 'between' };
export const OPERATOR_MAP = {
string: [LIKE, NOT_LIKE, IN, NOT_IN, EQUAL, NOT_EQUAL],
number: [GT, GE, LT, LE, EQUAL, NOT_EQUAL, BETWEEN],
date: [GT, GE, LT, LE, EQUAL, NOT_EQUAL, BETWEEN],
array: [IN, NOT_IN],
};

View File

@ -0,0 +1,86 @@
<template>
<div class="flex flex-row justify-between">
<slot name="left"></slot>
<div class="flex flex-row gap-[8px]">
<a-input-search
v-model="keyword"
size="small"
:placeholder="t('system.user.searchUser')"
class="w-[240px]"
allow-clear
@press-enter="emit('keywordSearch', keyword)"
@search="emit('keywordSearch', keyword)"
></a-input-search>
<MsTag
:type="visible ? 'primary' : 'default'"
:theme="visible ? 'lightOutLine' : 'outline'"
size="large"
class="min-w-[64px] cursor-pointer"
no-margin
>
<span :class="!visible ? 'text-[var(--color-text-4)]' : ''" @click="handleOpenFilter">
<icon-filter class="text-[16px]" />
<span class="ml-[4px]">
<span v-if="filterCount">{{ filterCount }}</span>
{{ t('common.filter') }}
</span>
</span>
</MsTag>
<slot name="right"></slot>
<MsTag no-margin size="large" class="cursor-pointer" theme="outline" @click="handleResetSearch">
<MsIcon class="text-[var(color-text-4)]" :size="16" type="icon-icon_reset_outlined" />
</MsTag>
</div>
</div>
<FilterForm
v-show="visible"
v-model:count="filterCount"
:row-count="props.rowCount"
:visible="visible"
:config-list="props.filterConfigList"
class="mt-[8px]"
@on-search="handleFilter"
@data-index-change="dataIndexChange"
/>
</template>
<script setup lang="ts">
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsTag from '../ms-tag/ms-tag.vue';
import FilterForm from './FilterForm.vue';
import { useI18n } from '@/hooks/useI18n';
import { FilterFormItem, FilterResult } from './type';
const { t } = useI18n();
const keyword = ref('');
const props = defineProps<{
rowCount: number;
filterConfigList: FilterFormItem[];
}>();
const visible = ref(false);
const filterCount = ref(0);
const emit = defineEmits<{
(e: 'keywordSearch', value: string): void; // keyword
(e: 'advSearch', value: FilterResult): void; //
(e: 'dataIndexChange', value: string): void; //
}>();
const handleResetSearch = () => {
keyword.value = '';
emit('keywordSearch', '');
};
const handleFilter = (filter: FilterResult) => {
emit('advSearch', filter);
};
const dataIndexChange = (dataIndex: string) => {
emit('dataIndexChange', dataIndex);
};
const handleOpenFilter = () => {
visible.value = !visible.value;
};
</script>

View File

@ -1,3 +1,5 @@
import { MsSearchSelectProps } from '@/components/business/ms-select';
/* eslint-disable no-shadow */
export enum BackEndEnum {
STRING = 'string',
@ -7,20 +9,20 @@ export enum BackEndEnum {
export enum FilterType {
INPUT = 'Input',
NUMBER = 'Number',
SELECT = 'Select',
DATE_PICKER = 'DatePicker',
RANGE_PICKER = 'RangePicker',
MUTIPLE_SELECT = 'MutiSelect',
}
export interface FilterFormItem {
dataIndex?: string; // 对应的row的数据key
title?: string; // 显示的label 国际化字符串定义在前端
type?: FilterType; // 类型Input,Select,DatePicker,RangePicker
type: FilterType; // 类型Input,Select,DatePicker,RangePicker
value?: any; // 值 字符串 和 数组
operator?: string; // 运算符号
options?: any[]; // 下拉框的选项
backendType?: BackEndEnum; // 后端类型 string array time
selectProps?: Partial<MsSearchSelectProps>; // select的props, 参考 MsSelect
}
export type AccordBelowType = 'all' | 'any';

View File

@ -6,7 +6,7 @@
:size="props.size"
:style="{
...typeStyle,
'margin-right': tagMargin,
'margin-right': noMargin ? 0 : tagMargin,
'min-width': props.width && `${props.width}ch`,
'max-width': '144px',
}"
@ -33,11 +33,13 @@
theme?: Theme; // tag
selfStyle?: any; //
width?: number; // tag,max-width
noMargin?: boolean; // tag
}>(),
{
type: 'default',
theme: 'dark',
size: 'medium',
noMargin: false,
}
);

View File

@ -1,35 +1,13 @@
<template>
<MsCard simple>
<div class="flex flex-row justify-between">
<div class="flex gap-[12px]">
<a-button type="primary" @click="handleCreate">
{{ t('bugManagement.createBug') }}
</a-button>
<a-button type="outline" @click="handleSync">
{{ t('bugManagement.syncBug') }}
</a-button>
</div>
<div class="flex flex-row gap-[8px]">
<a-input-search
v-model="keyword"
:placeholder="t('system.user.searchUser')"
class="w-[240px]"
allow-clear
@press-enter="fetchData"
@search="fetchData"
></a-input-search>
<FilterIcon v-model:visible="filterVisible" :count="filterCount" />
</div>
</div>
<FilterForm
v-show="filterVisible"
v-model:count="filterCount"
:visible="filterVisible"
:config-list="filterConfigList"
class="mt-[8px]"
@on-search="handleFilter"
@data-index-change="dataIndexChange"
/>
<MsAdvanceFilter :filter-config-list="filterConfigList" :row-count="filterRowCount">
<template #left>
<div class="flex gap-[12px]">
<a-button type="primary" @click="handleCreate">{{ t('bugManagement.createBug') }} </a-button>
<a-button type="primary" @click="handleSync">{{ t('bugManagement.syncBug') }} </a-button>
</div>
</template>
</MsAdvanceFilter>
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #numberOfCase="{ record }">
<span class="cursor-pointer text-[rgb(var(--primary-5))]" @click="jumpToTestPlan(record)">{{
@ -53,7 +31,7 @@
<script lang="ts" setup>
import { Message } from '@arco-design/web-vue';
import { FilterForm, FilterIcon } from '@/components/pure/ms-advance-filter';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCard from '@/components/pure/ms-card/index.vue';
@ -73,12 +51,11 @@
const { t } = useI18n();
const keyword = ref('');
const tableStore = useTableStore();
const appStore = useAppStore();
const projectId = computed(() => appStore.currentProjectId);
const filterVisible = ref(false);
const filterCount = ref(0);
const filterRowCount = ref(0);
const filterConfigList = reactive<FilterFormItem[]>([
{
title: 'bugManagement.ID',
@ -89,14 +66,28 @@
title: 'bugManagement.bugName',
dataIndex: 'name',
type: FilterType.SELECT,
selectProps: {
mode: 'static',
},
},
{
title: 'bugManagement.severity',
dataIndex: 'severity',
type: FilterType.MUTIPLE_SELECT,
type: FilterType.SELECT,
selectProps: {
mode: 'static',
multiple: true,
},
},
{
title: 'bugManagement.createTime',
dataIndex: 'createTime',
type: FilterType.DATE_PICKER,
},
]);
const heightUsed = computed(() => 286 + (filterVisible.value ? 160 + (filterRowCount.value - 1) * 60 : 0));
const columns: MsTableColumn = [
{
title: 'bugManagement.ID',
@ -184,7 +175,7 @@
}
};
const { propsRes, propsEvent, loadList, setKeyword, setLoadListParams } = useTable(
const { propsRes, propsEvent, loadList, setKeyword, setLoadListParams, setProps } = useTable(
postProjectTableByOrg,
{
tableKey: TableKeyEnum.BUG_MANAGEMENT,
@ -198,8 +189,12 @@
(record) => handleNameChange(record)
);
const fetchData = async () => {
setKeyword(keyword.value);
watchEffect(() => {
setProps({ heightUsed: heightUsed.value });
});
const fetchData = async (v = '') => {
setKeyword(v);
await loadList();
};