refactor: 高级筛选-重构视图下拉

This commit is contained in:
teukkk 2024-09-09 18:21:59 +08:00 committed by Craftsman
parent b39aa92da0
commit bf74d8d882
7 changed files with 168 additions and 97 deletions

View File

@ -77,22 +77,25 @@
:placeholder="t('advanceFilter.inputPlaceholder')"
:max-length="1000"
/>
<MsTagsInput
v-else-if="item.type === FilterType.TAGS_INPUT"
v-model:model-value="item.value"
:disabled="isValueDisabled(item)"
allow-clear
unique-value
retain-input-value
/>
<a-input-number
v-else-if="item.type === FilterType.NUMBER"
v-else-if="
item.type === FilterType.NUMBER ||
(item.type === FilterType.TAGS_INPUT && [OperatorEnum.COUNT_LT, OperatorEnum.COUNT_GT].includes(item.operator as OperatorEnum))
"
v-model:model-value="item.value"
allow-clear
:disabled="isValueDisabled(item)"
:max-length="255"
:placeholder="t('common.pleaseInput')"
/>
<MsTagsInput
v-else-if="item.type === FilterType.TAGS_INPUT&& ![OperatorEnum.COUNT_LT, OperatorEnum.COUNT_GT].includes(item.operator as OperatorEnum)"
v-model:model-value="item.value"
:disabled="isValueDisabled(item)"
allow-clear
unique-value
retain-input-value
/>
<MsSelect
v-else-if="item.type === FilterType.MEMBER"
v-model:model-value="item.value"
@ -343,7 +346,9 @@
function valueIsArray(listItem: FilterFormItem) {
return (
listItem.selectProps?.multiple ||
[FilterType.CHECKBOX, FilterType.TAGS_INPUT].includes(listItem.type) ||
[FilterType.CHECKBOX].includes(listItem.type) ||
(listItem.type === FilterType.TAGS_INPUT &&
![OperatorEnum.COUNT_LT, OperatorEnum.COUNT_GT].includes(listItem.operator as OperatorEnum)) ||
(listItem.type === FilterType.DATE_PICKER && listItem.operator === OperatorEnum.BETWEEN)
);
}
@ -376,6 +381,8 @@
formModel.value.list[index].operator = OperatorEnum.BELONG_TO;
} else if (optionsValueList.includes(OperatorEnum.EQUAL)) {
formModel.value.list[index].operator = OperatorEnum.EQUAL;
} else {
formModel.value.list[index].operator = OperatorEnum.BETWEEN; //
}
}
}
@ -401,22 +408,25 @@
}
function getParams() {
const conditions = formModel.value.list.map(({ type, value, operator, customField, dataIndex }) => {
let timeValue;
//
if (type === FilterType.DATE_PICKER && value?.[0] && value?.[1]) {
timeValue =
operator === OperatorEnum.BETWEEN
? [new Date(value[0]).getTime(), new Date(value[1]).getTime()]
: new Date(value).getTime();
const conditions = formModel.value.list.map(
({ customFieldType, type, value, operator, customField, dataIndex }) => {
let timeValue;
//
if (type === FilterType.DATE_PICKER && value?.[0] && value?.[1]) {
timeValue =
operator === OperatorEnum.BETWEEN
? [new Date(value[0]).getTime(), new Date(value[1]).getTime()]
: new Date(value).getTime();
}
return {
value: timeValue ?? value,
operator,
customField: customField ?? false,
name: dataIndex,
customFieldType: customFieldType ?? '',
};
}
return {
value: timeValue ?? value,
operator,
customField: customField ?? false,
name: dataIndex,
};
});
);
return { searchMode: formModel.value.searchMode, conditions };
}

View File

@ -11,8 +11,8 @@ export const LE = { label: 'advanceFilter.operator.le', value: 'LT_OR_EQUALS' };
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 COUNT_GT = { label: 'advanceFilter.operator.length.gt', value: OperatorEnum.COUNT_GT }; // 数量大下
export const COUNT_LT = { label: 'advanceFilter.operator.length.lt', value: OperatorEnum.COUNT_LT }; // 数量小于
export const COUNT_GT = { label: 'advanceFilter.operator.count.gt', value: OperatorEnum.COUNT_GT }; // 数量大下
export const COUNT_LT = { label: 'advanceFilter.operator.count.lt', value: OperatorEnum.COUNT_LT }; // 数量小于
export const EMPTY = { label: 'advanceFilter.operator.empty', value: OperatorEnum.EMPTY }; // 为空
export const NOT_EMPTY = { label: 'advanceFilter.operator.not_empty', value: OperatorEnum.NOT_EMPTY }; // 不为空
@ -41,7 +41,7 @@ export const operatorOptionsMap: Record<string, { value: string; label: string }
[FilterType.MEMBER]: COMMON_SELECTION_OPERATORS,
[FilterType.TAGS_INPUT]: [EMPTY, CONTAINS, NO_CONTAINS, COUNT_LT, COUNT_GT],
[FilterType.TREE_SELECT]: [BELONG_TO, NOT_BELONG_TO],
[FilterType.DATE_PICKER]: [BETWEEN, EQUAL, EMPTY, NOT_EMPTY],
[FilterType.DATE_PICKER]: [BETWEEN, GT, LT, EMPTY, NOT_EMPTY],
};
export const timeSelectOptions = [GE, LE];

View File

@ -31,57 +31,81 @@
@search="emit('keywordSearch', keyword)"
@clear="handleClear"
></a-input-search>
<a-select
v-if="props.viewType"
v-model:model-value="currentView"
:loading="viewListLoading"
:trigger-props="{ contentClass: 'view-select-trigger' }"
class="w-[180px]"
show-footer-on-empty
<!-- 在select的option里写input,鼠标点击和失焦不好使,故单独写了一个下拉trigger -->
<a-trigger
v-model:popup-visible="viewSelectOptionVisible"
trigger="click"
:popup-translate="[0, 4]"
content-class="arco-trigger-menu view-custom-trigger-content"
>
<template #prefix> {{ t('advanceFilter.view') }} </template>
<a-optgroup :label="t('advanceFilter.systemView')">
<a-option v-for="item in internalViews" :key="item.id" :value="item.id">
{{ item.name }}
</a-option>
</a-optgroup>
<a-optgroup :label="t('advanceFilter.myView')">
<template v-for="item in customViews" :key="item.id">
<a-option v-show="!item.isShowNameInput" :value="item.id">
<div>{{ item.name }}</div>
<div class="select-extra flex">
<a-tooltip :content="t('common.rename')">
<MsButton type="text" status="secondary" class="!mr-[4px]" @click="handleToRenameView(item)">
<MsIcon type="icon-icon_edit_outlined" class="hover:text-[rgb(var(--primary-4))]" size="12" />
</MsButton>
</a-tooltip>
<a-tooltip :content="t('advanceFilter.deleteView')">
<MsButton type="text" :disabled="deleteLoading" status="secondary" @click="handleDeleteView(item)">
<MsIcon
type="icon-icon_delete-trash_outlined1"
class="hover:text-[rgb(var(--primary-4))]"
size="12"
/>
</MsButton>
</a-tooltip>
<a-select
v-if="props.viewType"
v-model:model-value="currentView"
:loading="viewListLoading"
:options="[...internalViews, ...customViews].map((item) => ({ value: item.id, label: item.name }))"
:trigger-props="{ contentClass: 'view-select-trigger' }"
class="w-[180px]"
show-footer-on-empty
>
<template #prefix> {{ t('advanceFilter.view') }} </template>
</a-select>
<template #content>
<a-spin class="w-full" :loading="viewListLoading">
<div class="view-option-title">
<span>{{ t('advanceFilter.systemView') }}</span>
<a-divider></a-divider>
</div>
<div
v-for="item in internalViews"
:key="item.id"
:class="[`view-option-item ${item.id === currentView ? 'view-option-item-active' : ''}`]"
@click="changeView(item)"
>
{{ item.name }}
</div>
<div class="view-option-title">
<span>{{ t('advanceFilter.myView') }}</span>
<a-divider></a-divider>
</div>
<template v-for="item in customViews" :key="item.id">
<div
v-show="!item.isShowNameInput"
:class="[`view-option-item ${item.id === currentView ? 'view-option-item-active' : ''}`]"
@click="changeView(item)"
>
<div>{{ item.name }}</div>
<div class="select-extra flex">
<a-tooltip :content="t('common.rename')">
<MsButton type="text" status="secondary" class="!mr-[4px]" @click="handleToRenameView(item)">
<MsIcon type="icon-icon_edit_outlined" class="hover:text-[rgb(var(--primary-4))]" size="12" />
</MsButton>
</a-tooltip>
<a-tooltip :content="t('advanceFilter.deleteView')">
<MsButton type="text" :disabled="deleteLoading" status="secondary" @click="handleDeleteView(item)">
<MsIcon
type="icon-icon_delete-trash_outlined1"
class="hover:text-[rgb(var(--primary-4))]"
size="12"
/>
</MsButton>
</a-tooltip>
</div>
</div>
</a-option>
<ViewNameInput
v-if="item.isShowNameInput"
:ref="(el:refItem) => setNameInputRefMap(el, item)"
v-model:form="formModel"
:all-names="allViewNames.filter((name) => name !== item.name)"
@handle-submit="handleRenameView"
/>
</template>
</a-optgroup>
<template #footer>
<div class="flex cursor-pointer items-center gap-[8px]" @click="toNewView">
<MsIcon type="icon-icon_add_outlined" />
{{ t('advanceFilter.newView') }}
</div>
<ViewNameInput
v-if="item.isShowNameInput"
:ref="(el:refItem) => setNameInputRefMap(el, item)"
v-model:form="formModel"
:all-names="allViewNames.filter((name) => name !== item.name)"
@handle-submit="handleRenameView"
/>
</template>
<div class="flex cursor-pointer items-center gap-[8px] px-[8px] py-[3px]" @click="toNewView">
<MsIcon type="icon-icon_add_outlined" />
{{ t('advanceFilter.newView') }}
</div>
</a-spin>
</template>
</a-select>
</a-trigger>
<a-button
v-if="props.viewType"
type="outline"
@ -207,6 +231,17 @@
}
});
const viewSelectOptionVisible = ref(false);
function changeView(item: ViewItem) {
currentView.value = item.id;
viewSelectOptionVisible.value = false;
}
async function changeViewToFirstCustom() {
await getUserViewList();
currentView.value = customViews.value[0].id;
}
const filterDrawerRef = ref<InstanceType<typeof FilterDrawer>>();
function toNewView() {
if (canNotAddView.value) {
@ -301,40 +336,59 @@
}
}
async function changeViewToFirstCustom() {
await getUserViewList();
currentView.value = customViews.value[0].id;
}
defineExpose({
isAdvancedSearchMode,
});
</script>
<style lang="less">
.view-select-trigger .arco-select-dropdown {
.arco-select-option-content {
@apply flex w-full items-center justify-between;
.view-select-trigger {
display: none;
}
.view-custom-trigger-content {
width: 180px;
max-height: 300px;
.ms-scroll-bar();
.view-option-title {
display: flex;
align-items: center;
margin: 0 2px;
padding: 0 8px;
font-size: 12px;
color: var(--color-text-brand);
line-height: 20px;
.arco-divider-horizontal {
margin: 4px 0 4px 8px;
min-width: 0;
border-bottom-color: var(--color-text-n8);
flex: 1;
}
}
.select-extra {
visibility: hidden;
}
.arco-select-option:hover {
.select-extra {
visibility: visible;
.view-option-item {
padding: 3px 8px;
border-radius: 4px;
cursor: pointer;
@apply flex w-full items-center justify-between;
&:hover {
background-color: rgb(var(--primary-1));
.select-extra {
visibility: visible;
}
}
&-active {
color: rgb(var(--primary-5)) !important;
background-color: rgb(var(--primary-1)) !important;
}
}
.arco-select-dropdown-list-wrapper {
max-height: 255px;
.arco-form-item-content,
.arco-input-wrapper {
height: 28px;
}
.arco-select-group-title {
margin: 0 2px;
padding: 0 8px;
color: var(--color-text-brand);
}
.arco-select-dropdown-footer {
padding: 3px 8px;
border: none;
.arco-form-item-message {
margin: 0;
}
}
</style>

View File

@ -21,6 +21,8 @@ export default {
'advanceFilter.operator.length.ge': 'Length greater than or equal to',
'advanceFilter.operator.length.lt': 'Length less than',
'advanceFilter.operator.length.le': 'Length less than or equal to',
'advanceFilter.operator.count.lt': 'Quantity less than',
'advanceFilter.operator.count.gt': 'Quantity greater than',
'advanceFilter.view': 'View',
'advanceFilter.unnamedView': 'Unnamed View',

View File

@ -21,6 +21,8 @@ export default {
'advanceFilter.operator.length.ge': '长度大于等于',
'advanceFilter.operator.length.lt': '长度小于',
'advanceFilter.operator.length.le': '长度小于等于',
'advanceFilter.operator.count.lt': '数量小于',
'advanceFilter.operator.count.gt': '数量大于',
'advanceFilter.view': '视图',
'advanceFilter.unnamedView': '未命名视图',

View File

@ -34,6 +34,7 @@ export interface FilterFormItem {
type: FilterType; // 类型:判断第二列下拉数据和第三列显示形式
value?: any; // 第三列的值
customField?: boolean; // 是否是自定义字段
customFieldType?: string; // 自定义字段的类型
cascaderOptions?: CascaderOption[]; // 级联选择的选项
selectProps?: Partial<MsSearchSelectProps>; // select的props, 参考 MsSelect
cascaderProps?: Partial<MsCascaderProps>; // cascader的props, 参考 MsCascader

View File

@ -1108,12 +1108,14 @@
dataIndex: item.id,
type: formType,
customField: true,
customFieldType: item.type,
};
if (formObject.propsKey && formProps.options) {
formProps.options = item.options;
currentItem[formObject.propsKey] = {
...formProps,
customFieldType: item.type,
};
}
return currentItem;