feat(测试用例): 测试用例支持高级搜索&修改高级搜索交互

This commit is contained in:
teukkk 2024-09-04 19:05:49 +08:00 committed by Craftsman
parent 73a011985c
commit 543909c358
16 changed files with 259 additions and 82 deletions

View File

@ -245,7 +245,7 @@ export function getDefaultLocale() {
// 视图列表 // 视图列表
export function getViewList(viewType: string, scopeId: string) { export function getViewList(viewType: string, scopeId: string) {
return MSR.get<ViewList>({ url: `/user-view/${viewType}/grouped/list`, params: scopeId }); return MSR.get<ViewList>({ url: `/user-view/${viewType}/grouped/list`, params: { scopeId } });
} }
// 视图详情 // 视图详情
export function getViewDetail(viewType: string, id: string) { export function getViewDetail(viewType: string, id: string) {
@ -261,5 +261,5 @@ export function addView(viewType: string, data: ViewParams) {
} }
// 删除视图 // 删除视图
export function deleteView(viewType: string, id: string) { export function deleteView(viewType: string, id: string) {
return MSR.post({ url: `/user-view/${viewType}/delete/${id}` }); return MSR.get({ url: `/user-view/${viewType}/delete/${id}` });
} }

View File

@ -302,7 +302,8 @@
.arco-input-tag-disabled, .arco-input-tag-disabled,
.arco-input-disabled, .arco-input-disabled,
.arco-textarea-disabled, .arco-textarea-disabled,
.arco-select-view-disabled .arco-select-view-disabled,
.arco-picker-disabled
):hover { ):hover {
border-color: rgb(var(--primary-5)) !important; border-color: rgb(var(--primary-5)) !important;
background-color: white; background-color: white;
@ -314,7 +315,8 @@
.arco-input-tag-disabled, .arco-input-tag-disabled,
.arco-select-view-disabled, .arco-select-view-disabled,
.arco-textarea-disabled, .arco-textarea-disabled,
.arco-input-disabled { .arco-input-disabled,
.arco-picker-disabled {
border-color: var(--color-text-n9) !important; border-color: var(--color-text-n9) !important;
background-color: var(--color-text-n9) !important; background-color: var(--color-text-n9) !important;
} }

View File

@ -4,7 +4,7 @@
class="hidden-item" class="hidden-item"
hide-asterisk hide-asterisk
field="name" field="name"
:validate-trigger="['blur', 'input']" :validate-trigger="['change', 'input']"
:rules="[{ required: true, message: t('advanceFilter.viewNameRequired') }, { validator: validateName }]" :rules="[{ required: true, message: t('advanceFilter.viewNameRequired') }, { validator: validateName }]"
> >
<a-input <a-input
@ -60,7 +60,16 @@
}); });
} }
function validateForm(cb: () => void) {
formRef.value?.validate(async (errors) => {
if (!errors) {
cb();
}
});
}
defineExpose({ defineExpose({
inputFocus, inputFocus,
validateForm,
}); });
</script> </script>

View File

@ -2,17 +2,18 @@
<MsDrawer v-model:visible="visible" :mask="false" :width="600"> <MsDrawer v-model:visible="visible" :mask="false" :width="600">
<template #title> <template #title>
<ViewNameInput <ViewNameInput
v-show="isShowNameInput" v-if="isShowNameInput"
ref="viewNameInputRef" ref="viewNameInputRef"
v-model:form="formModel" v-model:form="formModel"
:all-names="allViewNames" :all-names="allViewNames.filter((name) => name !== savedFormModel.name)"
@handle-submit="isShowNameInput = false" @handle-submit="isShowNameInput = false"
/> />
<div v-show="!isShowNameInput" class="flex flex-1 items-center gap-[8px] overflow-hidden"> <div v-else class="flex flex-1 items-center gap-[8px] overflow-hidden">
<a-tooltip :content="formModel.name"> <a-tooltip :content="formModel.name">
<div class="one-line-text"> {{ formModel.name }}</div> <div class="one-line-text"> {{ formModel.name }}</div>
</a-tooltip> </a-tooltip>
<MsIcon <MsIcon
v-if="formModel?.internalViewKey !== 'ALL_DATA'"
type="icon-icon_edit_outlined" type="icon-icon_edit_outlined"
class="min-w-[16px] cursor-pointer hover:text-[rgb(var(--primary-5))]" class="min-w-[16px] cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="showNameInput" @click="showNameInput"
@ -92,6 +93,18 @@
:max-length="255" :max-length="255"
:placeholder="t('common.pleaseInput')" :placeholder="t('common.pleaseInput')"
/> />
<MsSelect
v-else-if="item.type === FilterType.MEMBER"
v-model:model-value="item.value"
allow-clear
allow-search
:placeholder="t('common.pleaseSelect')"
:disabled="isValueDisabled(item)"
:options="props.memberOptions"
multiple
:search-keys="['label']"
:max-tag-count="1"
/>
<MsSelect <MsSelect
v-else-if="item.type === FilterType.SELECT" v-else-if="item.type === FilterType.SELECT"
v-model:model-value="item.value" v-model:model-value="item.value"
@ -180,12 +193,13 @@
{{ t('advanceFilter.addCondition') }} {{ t('advanceFilter.addCondition') }}
</MsButton> </MsButton>
<template #footer> <template #footer>
<div v-show="!isSaveAsView" class="flex items-center gap-[8px]"> <div v-if="!isSaveAsView" class="flex items-center gap-[8px]">
<a-button type="primary" @click="handleFilter">{{ t('common.filter') }}</a-button> <a-button type="primary" @click="handleFilter">{{ t('common.filter') }}</a-button>
<a-button class="mr-[16px]" @click="handleReset">{{ t('common.reset') }}</a-button> <a-button class="mr-[16px]" @click="handleReset">{{ t('common.reset') }}</a-button>
<MsButton <MsButton
v-if="formModel?.internalViewKey !== 'ALL_DATA'" v-if="formModel?.internalViewKey !== 'ALL_DATA'"
type="text" type="text"
:loading="saveLoading"
class="!text-[var(--color-text-1)]" class="!text-[var(--color-text-1)]"
@click="handleSaveView" @click="handleSaveView"
> >
@ -195,14 +209,14 @@
{{ t('advanceFilter.saveAsView') }} {{ t('advanceFilter.saveAsView') }}
</MsButton> </MsButton>
</div> </div>
<div v-show="isSaveAsView" class="flex items-center gap-[8px]"> <div v-else class="flex items-center gap-[8px]">
<ViewNameInput <ViewNameInput
ref="saveAsViewNameInputRef" ref="saveAsViewNameInputRef"
v-model:form="saveAsViewForm" v-model:form="saveAsViewForm"
class="w-[240px]" class="w-[240px]"
:all-names="allViewNames" :all-names="allViewNames"
/> />
<a-button type="primary" @click="handleAddView">{{ t('common.save') }}</a-button> <a-button type="primary" :loading="addLoading" @click="handleAddView">{{ t('common.save') }}</a-button>
<a-button @click="handleCancelSaveAsView">{{ t('common.cancel') }}</a-button> <a-button @click="handleCancelSaveAsView">{{ t('common.cancel') }}</a-button>
</div> </div>
</template> </template>
@ -221,6 +235,7 @@
import { addView, getViewDetail, updateView } from '@/api/modules/user/index'; import { addView, getViewDetail, updateView } from '@/api/modules/user/index';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { SelectValue } from '@/models/projectManagement/menuManagement'; import { SelectValue } from '@/models/projectManagement/menuManagement';
import { FilterType, OperatorEnum, ViewTypeEnum } from '@/enums/advancedFilterEnum'; import { FilterType, OperatorEnum, ViewTypeEnum } from '@/enums/advancedFilterEnum';
@ -234,6 +249,8 @@
viewType: ViewTypeEnum; viewType: ViewTypeEnum;
currentView: string; // currentView: string; //
allViewNames: string[]; allViewNames: string[];
canNotAddView: boolean;
memberOptions: { label: string; value: string }[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'handleFilter', value: FilterResult): void; (e: 'handleFilter', value: FilterResult): void;
@ -242,6 +259,7 @@
const visible = defineModel<boolean>('visible', { required: true }); const visible = defineModel<boolean>('visible', { required: true });
const { t } = useI18n(); const { t } = useI18n();
const appStore = useAppStore();
const defaultFormModel: FilterForm = { const defaultFormModel: FilterForm = {
name: '', name: '',
@ -361,6 +379,7 @@
// //
function handleReset() { function handleReset() {
formModel.value = cloneDeep(savedFormModel.value); formModel.value = cloneDeep(savedFormModel.value);
isShowNameInput.value = false;
} }
// //
function handleFilter() { function handleFilter() {
@ -378,29 +397,49 @@
handleFilter(); handleFilter();
} }
); );
watch(
() => visible.value,
async (val) => {
//
if (!val && formModel.value?.id !== props.currentView) {
await getUserViewDetail(props.currentView);
}
}
);
const isSaveAsView = ref(false);
const saveAsViewForm = ref({ name: '' });
const saveAsViewNameInputRef = ref<InstanceType<typeof ViewNameInput>>();
// //
function resetToNewViewForm() { function resetToNewViewForm() {
// TODO lmy //
let name = '';
for (let i = 1; i <= 10; i++) {
const defaultName = `${t('advanceFilter.unnamedView')}${String(i).padStart(3, '0')}`;
if (!props.allViewNames.includes(defaultName)) {
name = defaultName;
break;
}
}
formModel.value = { formModel.value = {
...cloneDeep(defaultFormModel), ...cloneDeep(defaultFormModel),
name: '未命名视图001', name,
}; };
savedFormModel.value = cloneDeep(formModel.value); savedFormModel.value = cloneDeep(formModel.value);
} }
// //
function handleSaveView() { const saveLoading = ref(false);
// TODO lmy function realSaveView() {
formRef.value?.validate(async (errors) => { formRef.value?.validate(async (errors) => {
if (!errors) { if (!errors) {
try { try {
saveLoading.value = true;
if (formModel.value.id) { if (formModel.value.id) {
await updateView(props.viewType, { ...getParams(), name: formModel.value.name, id: formModel.value.id }); await updateView(props.viewType, { ...getParams(), name: formModel.value.name, id: formModel.value.id });
} else { } else {
await addView(props.viewType, { ...getParams(), name: formModel.value.name, id: formModel.value.id }); await addView(props.viewType, {
...getParams(),
scopeId: appStore.currentProjectId,
name: formModel.value.name,
id: formModel.value.id,
});
} }
Message.success(t('common.saveSuccess')); Message.success(t('common.saveSuccess'));
savedFormModel.value = cloneDeep(formModel.value); savedFormModel.value = cloneDeep(formModel.value);
@ -408,15 +447,34 @@
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
} finally {
saveLoading.value = false;
} }
} }
}); });
} }
function handleSaveView() {
if (viewNameInputRef.value) {
viewNameInputRef.value?.validateForm(realSaveView);
} else {
realSaveView();
}
}
// //
const isSaveAsView = ref(false);
const saveAsViewForm = ref({ name: '' });
const saveAsViewNameInputRef = ref<InstanceType<typeof ViewNameInput>>();
function handleToSaveAs() { function handleToSaveAs() {
if (props.canNotAddView) {
Message.warning(t('advanceFilter.maxViewTip'));
return;
}
formRef.value?.validate((errors) => { formRef.value?.validate((errors) => {
if (!errors) { if (!errors) {
isSaveAsView.value = true; isSaveAsView.value = true;
nextTick(() => {
saveAsViewNameInputRef.value?.inputFocus();
});
} }
}); });
} }
@ -426,17 +484,29 @@
saveAsViewForm.value.name = ''; saveAsViewForm.value.name = '';
} }
// //
async function handleAddView() { const addLoading = ref(false);
// TODO lmy saveAsViewNameInputRef async function realAddView() {
try { try {
await addView(props.viewType, { ...getParams(), name: formModel.value.name, id: formModel.value.id }); addLoading.value = true;
await addView(props.viewType, {
...getParams(),
scopeId: appStore.currentProjectId,
name: saveAsViewForm.value.name,
id: formModel.value.id,
});
Message.success(t('common.saveSuccess')); Message.success(t('common.saveSuccess'));
emit('refreshViewList'); emit('refreshViewList');
handleCancelSaveAsView();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
} finally {
addLoading.value = false;
} }
} }
async function handleAddView() {
saveAsViewNameInputRef.value?.validateForm(realAddView);
}
defineExpose({ defineExpose({
resetToNewViewForm, resetToNewViewForm,

View File

@ -38,6 +38,7 @@ export const operatorOptionsMap: Record<string, { value: string; label: string }
[FilterType.RADIO]: COMMON_SELECTION_OPERATORS, [FilterType.RADIO]: COMMON_SELECTION_OPERATORS,
[FilterType.CHECKBOX]: COMMON_SELECTION_OPERATORS, [FilterType.CHECKBOX]: COMMON_SELECTION_OPERATORS,
[FilterType.SELECT]: COMMON_SELECTION_OPERATORS, [FilterType.SELECT]: COMMON_SELECTION_OPERATORS,
[FilterType.MEMBER]: COMMON_SELECTION_OPERATORS,
[FilterType.TAGS_INPUT]: [EMPTY, CONTAINS, NO_CONTAINS, COUNT_LT, COUNT_GT], [FilterType.TAGS_INPUT]: [EMPTY, CONTAINS, NO_CONTAINS, COUNT_LT, COUNT_GT],
[FilterType.TREE_SELECT]: [BELONG_TO, NOT_BELONG_TO], [FilterType.TREE_SELECT]: [BELONG_TO, NOT_BELONG_TO],
[FilterType.DATE_PICKER]: [BETWEEN, EQUAL, EMPTY, NOT_EMPTY], [FilterType.DATE_PICKER]: [BETWEEN, EQUAL, EMPTY, NOT_EMPTY],

View File

@ -34,8 +34,9 @@
<a-select <a-select
v-if="props.viewType" v-if="props.viewType"
v-model:model-value="currentView" v-model:model-value="currentView"
:loading="viewListLoading"
:trigger-props="{ contentClass: 'view-select-trigger' }" :trigger-props="{ contentClass: 'view-select-trigger' }"
class="w-[160px]" class="w-[180px]"
show-footer-on-empty show-footer-on-empty
> >
<template #prefix> {{ t('advanceFilter.view') }} </template> <template #prefix> {{ t('advanceFilter.view') }} </template>
@ -45,25 +46,34 @@
</a-option> </a-option>
</a-optgroup> </a-optgroup>
<a-optgroup :label="t('advanceFilter.myView')"> <a-optgroup :label="t('advanceFilter.myView')">
<a-option v-for="item in customViews" :key="item.id" :value="item.id"> <template v-for="item in customViews" :key="item.id">
{{ item.name }} <a-option v-show="!item.isShowNameInput" :value="item.id">
<div class="flex"> <div>{{ item.name }}</div>
<a-tooltip :content="t('common.rename')"> <div class="select-extra flex">
<MsButton type="text" status="secondary" class="!mr-[4px]" @click.stop="handleRenameView(item)"> <a-tooltip :content="t('common.rename')">
<MsIcon type="icon-icon_edit_outlined" class="hover:text-[rgb(var(--primary-4))]" size="12" /> <MsButton type="text" status="secondary" class="!mr-[4px]" @click="handleToRenameView(item)">
</MsButton> <MsIcon type="icon-icon_edit_outlined" class="hover:text-[rgb(var(--primary-4))]" size="12" />
</a-tooltip> </MsButton>
<a-tooltip :content="t('advanceFilter.deleteView')"> </a-tooltip>
<MsButton type="text" status="secondary" @click.stop="handleDeleteView(item)"> <a-tooltip :content="t('advanceFilter.deleteView')">
<MsIcon <MsButton type="text" :disabled="deleteLoading" status="secondary" @click="handleDeleteView(item)">
type="icon-icon_delete-trash_outlined1" <MsIcon
class="hover:text-[rgb(var(--primary-4))]" type="icon-icon_delete-trash_outlined1"
size="12" class="hover:text-[rgb(var(--primary-4))]"
/> size="12"
</MsButton> />
</a-tooltip> </MsButton>
</div> </a-tooltip>
</a-option> </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> </a-optgroup>
<template #footer> <template #footer>
<div class="flex cursor-pointer items-center gap-[8px]" @click="toNewView"> <div class="flex cursor-pointer items-center gap-[8px]" @click="toNewView">
@ -111,6 +121,8 @@
:all-view-names="allViewNames" :all-view-names="allViewNames"
:config-list="props.filterConfigList" :config-list="props.filterConfigList"
:custom-list="props.customFieldsConfigList" :custom-list="props.customFieldsConfigList"
:can-not-add-view="canNotAddView"
:member-options="memberOptions"
@handle-filter="handleFilter" @handle-filter="handleFilter"
@refresh-view-list="getUserViewList" @refresh-view-list="getUserViewList"
/> />
@ -122,9 +134,11 @@
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsTag from '../ms-tag/ms-tag.vue'; import MsTag from '../ms-tag/ms-tag.vue';
import ViewNameInput from './components/viewNameInput.vue';
import FilterDrawer from './filterDrawer.vue'; import FilterDrawer from './filterDrawer.vue';
import { deleteView, getViewList } from '@/api/modules/user/index'; import { getProjectOptions } from '@/api/modules/project-management/projectMember';
import { deleteView, getViewList, updateView } from '@/api/modules/user/index';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
@ -159,42 +173,94 @@
const currentView = ref(''); // const currentView = ref(''); //
const internalViews = ref<ViewItem[]>([]); const internalViews = ref<ViewItem[]>([]);
const customViews = ref<ViewItem[]>([]); const customViews = ref<ViewItem[]>([]);
const viewListLoading = ref(false);
const allViewNames = computed(() => [...internalViews.value, ...customViews.value].map((item) => item.name)); const allViewNames = computed(() => [...internalViews.value, ...customViews.value].map((item) => item.name));
const canNotAddView = computed(() => customViews.value.length >= 10);
async function getUserViewList() { async function getUserViewList() {
try { try {
viewListLoading.value = true;
const res = await getViewList(props.viewType as ViewTypeEnum, appStore.currentProjectId); const res = await getViewList(props.viewType as ViewTypeEnum, appStore.currentProjectId);
internalViews.value = res.internalViews; internalViews.value = res.internalViews;
customViews.value = res.customViews; customViews.value = res.customViews;
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
} finally {
viewListLoading.value = false;
} }
} }
const memberOptions = ref<{ label: string; value: string }[]>([]);
async function getMemberOptions() {
const res = await getProjectOptions(appStore.currentProjectId);
memberOptions.value = [{ name: t('common.currentUser'), id: 'CURRENT_USER' }, ...res].map((e: any) => ({
label: e.name,
value: e.id,
}));
}
onMounted(async () => { onMounted(async () => {
await getUserViewList(); if (props.viewType) {
currentView.value = internalViews.value[0].id; getMemberOptions();
await getUserViewList();
currentView.value = internalViews.value[0]?.id;
}
}); });
const filterDrawerRef = ref<InstanceType<typeof FilterDrawer>>(); const filterDrawerRef = ref<InstanceType<typeof FilterDrawer>>();
function toNewView() { function toNewView() {
if (canNotAddView.value) {
Message.warning(t('advanceFilter.maxViewTip'));
return;
}
visible.value = true; visible.value = true;
filterDrawerRef.value?.resetToNewViewForm(); filterDrawerRef.value?.resetToNewViewForm();
} }
function handleRenameView(item: ViewItem) {
// TODO lmy type refItem = Element | ComponentPublicInstance | null;
const viewNameInputRefMap: Record<string, any> = {};
function setNameInputRefMap(el: refItem, item: ViewItem) {
if (el) {
viewNameInputRefMap[`${item.id}`] = el;
}
} }
async function handleDeleteView(item: ViewItem) { const formModel = ref({ name: '', id: '' });
function handleToRenameView(item: ViewItem) {
formModel.value.id = item.id;
formModel.value.name = item.name;
item.isShowNameInput = true;
nextTick(() => {
viewNameInputRefMap[item.id]?.inputFocus();
});
}
async function handleRenameView() {
try { try {
await deleteView(item.viewType, item.id); await updateView(props.viewType as string, { name: formModel.value.name, id: formModel.value.id });
Message.success(t('common.deleteSuccess')); Message.success(t('common.saveSuccess'));
getUserViewList(); getUserViewList();
currentView.value = internalViews.value[0].id;
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
} }
} }
//
const deleteLoading = ref(false);
async function handleDeleteView(item: ViewItem) {
try {
deleteLoading.value = true;
await deleteView(props.viewType as string, item.id);
Message.success(t('common.deleteSuccess'));
await getUserViewList();
if (item.id === currentView.value) {
currentView.value = internalViews.value[0].id;
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
deleteLoading.value = false;
}
}
const isAdvancedSearchMode = ref(false); const isAdvancedSearchMode = ref(false);
const handleFilter = (filter: FilterResult) => { const handleFilter = (filter: FilterResult) => {
keyword.value = ''; keyword.value = '';
@ -242,6 +308,14 @@
.arco-select-option-content { .arco-select-option-content {
@apply flex w-full items-center justify-between; @apply flex w-full items-center justify-between;
} }
.select-extra {
visibility: hidden;
}
.arco-select-option:hover {
.select-extra {
visibility: visible;
}
}
.arco-select-dropdown-list-wrapper { .arco-select-dropdown-list-wrapper {
max-height: 255px; max-height: 255px;
} }

View File

@ -23,6 +23,7 @@ export default {
'advanceFilter.operator.length.le': 'Length less than or equal to', 'advanceFilter.operator.length.le': 'Length less than or equal to',
'advanceFilter.view': 'View', 'advanceFilter.view': 'View',
'advanceFilter.unnamedView': 'Unnamed View',
'advanceFilter.systemView': 'System view', 'advanceFilter.systemView': 'System view',
'advanceFilter.myView': 'My view', 'advanceFilter.myView': 'My view',
'advanceFilter.newView': 'New view', 'advanceFilter.newView': 'New view',
@ -40,4 +41,5 @@ export default {
'advanceFilter.conditionRequired': 'Query condition cannot be empty', 'advanceFilter.conditionRequired': 'Query condition cannot be empty',
'advanceFilter.filterContentRequired': 'Filter content cannot be empty', 'advanceFilter.filterContentRequired': 'Filter content cannot be empty',
'advanceFilter.filterTip': 'Filter mode, module filtering can only be operated in the current filter', 'advanceFilter.filterTip': 'Filter mode, module filtering can only be operated in the current filter',
'advanceFilter.maxViewTip': 'Up to 10 views can be added',
}; };

View File

@ -23,6 +23,7 @@ export default {
'advanceFilter.operator.length.le': '长度小于等于', 'advanceFilter.operator.length.le': '长度小于等于',
'advanceFilter.view': '视图', 'advanceFilter.view': '视图',
'advanceFilter.unnamedView': '未命名视图',
'advanceFilter.systemView': '系统视图', 'advanceFilter.systemView': '系统视图',
'advanceFilter.myView': '我的视图', 'advanceFilter.myView': '我的视图',
'advanceFilter.newView': '新建视图', 'advanceFilter.newView': '新建视图',
@ -40,4 +41,5 @@ export default {
'advanceFilter.conditionRequired': '查询条件不能为空', 'advanceFilter.conditionRequired': '查询条件不能为空',
'advanceFilter.filterContentRequired': '筛选内容不能为空', 'advanceFilter.filterContentRequired': '筛选内容不能为空',
'advanceFilter.filterTip': '筛选模式,模块过滤仅可在当前过滤器中操作', 'advanceFilter.filterTip': '筛选模式,模块过滤仅可在当前过滤器中操作',
'advanceFilter.maxViewTip': '最多可添加 10 个视图',
}; };

View File

@ -52,7 +52,7 @@ export interface ConditionsItem extends CombineItem {
export interface FilterResult { export interface FilterResult {
// 匹配模式 所有/任一 // 匹配模式 所有/任一
searchMode: AccordBelowType; searchMode?: AccordBelowType;
// 高级搜索 // 高级搜索
conditions?: ConditionsItem[]; conditions?: ConditionsItem[];
combine?: any; // TODO lmy 此为防报错占位 所有高级筛选都完成后 删除这一行 combine?: any; // TODO lmy 此为防报错占位 所有高级筛选都完成后 删除这一行
@ -69,6 +69,7 @@ export interface ViewItem {
pos?: number; // 自定义排序 pos?: number; // 自定义排序
createTime?: number; createTime?: number;
updateTime?: number; updateTime?: number;
isShowNameInput?: boolean;
} }
export interface ViewList { export interface ViewList {
internalViews: ViewItem[]; internalViews: ViewItem[];

View File

@ -75,6 +75,11 @@
background-color: var(--color-text-n8); background-color: var(--color-text-n8);
} }
} }
.ms-button-text {
@apply p-0;
color: rgb(var(--primary-5));
}
.ms-button--secondary { .ms-button--secondary {
color: var(--color-text-2); color: var(--color-text-2);
&:not(.ms-button-text, .ms-button--disabled):hover { &:not(.ms-button-text, .ms-button--disabled):hover {
@ -106,9 +111,4 @@
padding: 0 2px; padding: 0 2px;
font-size: 12px; font-size: 12px;
} }
.ms-button-text {
@apply p-0;
color: rgb(var(--primary-5));
}
</style> </style>

View File

@ -18,6 +18,7 @@ export enum FilterType {
INPUT = 'Input', INPUT = 'Input',
NUMBER = 'Number', NUMBER = 'Number',
SELECT = 'Select', SELECT = 'Select',
MEMBER = 'Member',
DATE_PICKER = 'DatePicker', DATE_PICKER = 'DatePicker',
TAGS_INPUT = 'TagsInput', TAGS_INPUT = 'TagsInput',
TREE_SELECT = 'TreeSelect', TREE_SELECT = 'TreeSelect',

View File

@ -208,4 +208,5 @@ export default {
'common.cutSuccess': 'Cut successfully', 'common.cutSuccess': 'Cut successfully',
'common.copySuccessToClipboard': 'Copied to clipboard', 'common.copySuccessToClipboard': 'Copied to clipboard',
'common.casePriority': 'Case Priority', 'common.casePriority': 'Case Priority',
'common.currentUser': 'Current user',
}; };

View File

@ -208,4 +208,5 @@ export default {
'common.cutSuccess': '剪切成功', 'common.cutSuccess': '剪切成功',
'common.copySuccessToClipboard': '已复制到剪切板', 'common.copySuccessToClipboard': '已复制到剪切板',
'common.casePriority': '用例等级', 'common.casePriority': '用例等级',
'common.currentUser': '当前用户',
}; };

View File

@ -412,7 +412,6 @@
} from '@/api/modules/case-management/featureCase'; } from '@/api/modules/case-management/featureCase';
import { getSocket } from '@/api/modules/project-management/commonScript'; import { getSocket } from '@/api/modules/project-management/commonScript';
import { getCaseRelatedInfo } from '@/api/modules/project-management/menuManagement'; import { getCaseRelatedInfo } from '@/api/modules/project-management/menuManagement';
import { getProjectOptions } from '@/api/modules/project-management/projectMember';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { useAppStore, useTableStore } from '@/store'; import { useAppStore, useTableStore } from '@/store';
@ -774,7 +773,6 @@
], ],
}; };
const memberOptions = ref<{ label: string; value: string }[]>([]);
const filterConfigList = computed<FilterFormItem[]>(() => [ const filterConfigList = computed<FilterFormItem[]>(() => [
{ {
title: 'caseManagement.featureCase.tableColumnID', title: 'caseManagement.featureCase.tableColumnID',
@ -787,7 +785,7 @@
type: FilterType.INPUT, type: FilterType.INPUT,
}, },
{ {
title: 'caseManagement.featureCase.tableColumnModule', title: 'common.belongModule',
dataIndex: 'moduleId', dataIndex: 'moduleId',
type: FilterType.TREE_SELECT, type: FilterType.TREE_SELECT,
treeSelectData: caseTreeData.value, treeSelectData: caseTreeData.value,
@ -800,44 +798,59 @@
multiple: true, multiple: true,
treeCheckable: true, treeCheckable: true,
treeCheckStrictly: true, treeCheckStrictly: true,
maxTagCount: 2, maxTagCount: 1,
}, },
}, },
{ {
title: 'caseManagement.featureCase.tableColumnVersion', title: 'caseManagement.featureCase.tableColumnReviewResult',
dataIndex: 'versionId', dataIndex: 'reviewStatus',
type: FilterType.SELECT,
selectProps: {
multiple: true,
options: reviewResultOptions.value,
},
},
{
title: 'caseManagement.featureCase.tableColumnExecutionResult',
dataIndex: 'lastExecuteResult',
type: FilterType.SELECT,
selectProps: {
multiple: true,
options: executeResultOptions.value,
},
},
{
title: 'caseManagement.featureCase.associatedDemand',
dataIndex: 'demand',
type: FilterType.INPUT, type: FilterType.INPUT,
}, },
{ {
title: 'caseManagement.featureCase.tableColumnCreateUser', title: 'caseManagement.featureCase.relatedAttachments',
dataIndex: 'createUserName', dataIndex: 'attachment',
type: FilterType.SELECT, type: FilterType.INPUT,
selectProps: {
mode: 'static',
options: memberOptions.value,
},
}, },
{ {
title: 'caseManagement.featureCase.tableColumnCreateTime', title: 'common.creator',
dataIndex: 'createUser',
type: FilterType.MEMBER,
},
{
title: 'common.createTime',
dataIndex: 'createTime', dataIndex: 'createTime',
type: FilterType.DATE_PICKER, type: FilterType.DATE_PICKER,
}, },
{ {
title: 'caseManagement.featureCase.tableColumnUpdateUser', title: 'common.updateUserName',
dataIndex: 'updateUserName', dataIndex: 'updateUser',
type: FilterType.SELECT, type: FilterType.MEMBER,
selectProps: {
mode: 'static',
options: memberOptions.value,
},
}, },
{ {
title: 'caseManagement.featureCase.tableColumnUpdateTime', title: 'common.updateTime',
dataIndex: 'updateTime', dataIndex: 'updateTime',
type: FilterType.DATE_PICKER, type: FilterType.DATE_PICKER,
}, },
{ {
title: 'caseManagement.featureCase.tableColumnTag', title: 'common.tag',
dataIndex: 'tags', dataIndex: 'tags',
type: FilterType.TAGS_INPUT, type: FilterType.TAGS_INPUT,
}, },
@ -846,8 +859,6 @@
async function initFilter() { async function initFilter() {
const result = await getCustomFieldsTable(currentProjectId.value); 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 }));
// //
searchCustomFields.value = result.map((item: any) => { searchCustomFields.value = result.map((item: any) => {
const FilterTypeKey: keyof typeof FilterType = CustomTypeMaps[item.type].type; const FilterTypeKey: keyof typeof FilterType = CustomTypeMaps[item.type].type;

View File

@ -67,6 +67,7 @@ export default {
'caseManagement.featureCase.moveTo': 'Move to', 'caseManagement.featureCase.moveTo': 'Move to',
'caseManagement.featureCase.copyTo': 'Copy to', 'caseManagement.featureCase.copyTo': 'Copy to',
'caseManagement.featureCase.associatedDemand': 'Associated demand', 'caseManagement.featureCase.associatedDemand': 'Associated demand',
'caseManagement.featureCase.relatedAttachments': 'Related attachments',
'caseManagement.featureCase.generatingDependencies': 'Generative dependency', 'caseManagement.featureCase.generatingDependencies': 'Generative dependency',
'caseManagement.featureCase.addToPublic': 'Add to public case', 'caseManagement.featureCase.addToPublic': 'Add to public case',
'caseManagement.featureCase.updateCase': 'Update Case', 'caseManagement.featureCase.updateCase': 'Update Case',

View File

@ -67,6 +67,7 @@ export default {
'caseManagement.featureCase.moveTo': '移动到', 'caseManagement.featureCase.moveTo': '移动到',
'caseManagement.featureCase.copyTo': '复制到', 'caseManagement.featureCase.copyTo': '复制到',
'caseManagement.featureCase.associatedDemand': '关联需求', 'caseManagement.featureCase.associatedDemand': '关联需求',
'caseManagement.featureCase.relatedAttachments': '关联附件',
'caseManagement.featureCase.generatingDependencies': '生成依赖关系', 'caseManagement.featureCase.generatingDependencies': '生成依赖关系',
'caseManagement.featureCase.addToPublic': '添加到公共用例库', 'caseManagement.featureCase.addToPublic': '添加到公共用例库',
'caseManagement.featureCase.updateCase': '更新用例', 'caseManagement.featureCase.updateCase': '更新用例',