feat: 高级搜索组件部分逻辑修改
This commit is contained in:
parent
4910959a66
commit
15cd2c8bc4
|
@ -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(() => {
|
||||
|
|
|
@ -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>
|
|
@ -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],
|
||||
};
|
||||
|
|
|
@ -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>
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue