fix(缺陷管理): 缺陷全选批量操作参数有误

--bug=1039729 --user=宋昌昌 【缺陷管理】回收站-全选所有页-筛选删除人/处理人/状态/创建人/更新人/严重程度/搜索框搜索-批量操作-把所有缺陷都操作了 https://www.tapd.cn/55049933/s/1506497
--bug=1039725 --user=宋昌昌 【缺陷管理】缺陷-全选所有页-筛选状态/处理人/创建人/更新人/严重程度/搜索框搜索-批量操作-把所有的缺陷都操作了 https://www.tapd.cn/55049933/s/1506957
--bug=1039927 --user=宋昌昌 【项目管理】项目与权限-成员-批量添加至用户组-SQL报错 https://www.tapd.cn/55049933/s/1506849
This commit is contained in:
song-cc-rock 2024-04-23 18:06:36 +08:00 committed by 刘瑞斌
parent 6805e8cf06
commit f78286eaf5
8 changed files with 156 additions and 106 deletions

View File

@ -1511,6 +1511,12 @@ public class BugService {
BugPageRequest bugPageRequest = new BugPageRequest();
BeanUtils.copyBean(bugPageRequest, request);
bugPageRequest.setUseTrash(false);
if (request.getCondition() != null) {
bugPageRequest.setCombine(request.getCondition().getCombine());
bugPageRequest.setFilter(request.getCondition().getFilter());
bugPageRequest.setSearchMode(request.getCondition().getSearchMode());
bugPageRequest.setKeyword(request.getCondition().getKeyword());
}
List<BugDTO> allBugs = extBugMapper.list(bugPageRequest, request.getSort());
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
allBugs.removeIf(bug -> request.getExcludeIds().contains(bug.getId()));
@ -1538,6 +1544,8 @@ public class BugService {
if (request.getCondition() != null) {
bugPageRequest.setCombine(request.getCondition().getCombine());
bugPageRequest.setFilter(request.getCondition().getFilter());
bugPageRequest.setSearchMode(request.getCondition().getSearchMode());
bugPageRequest.setKeyword(request.getCondition().getKeyword());
}
List<String> ids = extBugMapper.getIdsByPageRequest(bugPageRequest);
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {

View File

@ -2,7 +2,7 @@
<MsDrawer
v-model:visible="visible"
:ok-text="t('common.export')"
:ok-loading="drawerLoading"
:ok-loading="exportLoading"
:width="800"
min-width="800px"
unmount-on-close
@ -36,8 +36,8 @@
class="mt-[8px] w-[95px] pl-[0px]"
:disabled="item.key === 'name'"
>
<a-tooltip :content="item.text">
<span class="one-line-text">{{ item.text }}</span>
<a-tooltip :content="item.text" position="top">
<div class="one-line-text max-w-[80px]">{{ item.text }}</div>
</a-tooltip>
</a-checkbox>
</div>
@ -51,8 +51,8 @@
:value="item.key"
class="mt-[8px] w-[95px] pl-[0px]"
>
<a-tooltip :content="item.text">
<span class="one-line-text">{{ item.text }}</span>
<a-tooltip :content="item.text" position="top">
<div class="one-line-text max-w-[80px]">{{ item.text }}</div>
</a-tooltip>
</a-checkbox>
</div>
@ -71,10 +71,8 @@
:value="item.key"
class="mt-[8px] w-[95px] pl-[0px]"
>
<a-tooltip :content="item.text">
<div class="one-line-text">
{{ item.text }}
</div>
<a-tooltip :content="item.text" position="top">
<div class="one-line-text max-w-[80px]">{{ item.text }}</div>
</a-tooltip>
</a-checkbox>
</div>
@ -146,6 +144,7 @@
defaultSelectedKeys?: string[];
isArrayColumn?: boolean;
arrayColumn?: EnvListItem[];
exportLoading: boolean;
titleProps?: {
selectableTitle: string; //
systemTitle: string; // |
@ -155,6 +154,7 @@
const props = withDefaults(defineProps<MsExportDrawerProps>(), {
visible: false,
exportLoading: false,
defaultSelectedKeys: () => ['name', 'id', 'title', 'status', 'handle_user', 'content'],
});
@ -163,6 +163,7 @@
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'update:exportLoading', value: boolean): void;
(e: 'confirm', value: MsExportDrawerOption[]): void;
}>();
@ -175,6 +176,15 @@
},
});
const exportLoading = computed({
get() {
return props.exportLoading;
},
set(value) {
emit('update:exportLoading', value);
},
});
const systemList = computed(() => {
if (props.isArrayColumn && props.arrayColumn) {
return props.arrayColumn.map((item) => {
@ -310,4 +320,9 @@
border: 1px dashed rgba(var(--primary-5));
background-color: rgba(var(--primary-1));
}
:deep(.arco-checkbox-group .arco-checkbox) {
margin-right: 20px;
margin-top: 10px;
}
</style>

View File

@ -150,7 +150,8 @@ export interface SetPaginationPrams {
export interface BatchActionQueryParams {
excludeIds?: string[]; // 排除的id
selectedIds?: string[]; // 选中的id
selectedIds?: string[];
selectIds?: string[]; // 选中的id
selectAll: boolean; // 是否跨页全选
params?: TableQueryParams; // 查询参数
currentSelectCount?: number; // 当前选中的数量

View File

@ -123,7 +123,6 @@
import { updateBatchBug } from '@/api/modules/bug-management';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import { tableParamsToRequestParams } from '@/utils';
import type { BugBatchUpdateFiledType } from '@/models/bug-management';
import { BugBatchUpdateFiledForm, BugEditCustomField } from '@/models/bug-management';
@ -221,7 +220,7 @@
try {
loading.value = true;
const tmpObj = {
...tableParamsToRequestParams(props.selectParam),
...props.selectParam,
projectId: appStore.currentProjectId,
[form.attribute]: form.value || form.inputValue,
append: form.append,

View File

@ -190,7 +190,12 @@
<a-date-picker v-model="syncObject.time" value-format="timestamp" show-time class="w-[304px]" />
</div>
</a-modal>
<MsExportDrawer v-model:visible="exportVisible" :all-data="exportOptionData" @confirm="exportConfirm">
<MsExportDrawer
v-model:visible="exportVisible"
:export-loading="exportLoading"
:all-data="exportOptionData"
@confirm="exportConfirm"
>
<template #title>
<span class="text-[var(--color-text-1)]">{{ t('bugManagement.exportBug') }}</span>
<span v-if="currentSelectParams.currentSelectCount" class="text-[var(--color-text-4)]"
@ -261,12 +266,7 @@
import router from '@/router';
import { useAppStore, useTableStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import {
customFieldDataToTableData,
customFieldToColumns,
downloadByteFile,
tableParamsToRequestParams,
} from '@/utils';
import { customFieldDataToTableData, customFieldToColumns, downloadByteFile } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { BugEditCustomField, BugListItem, BugOptionItem } from '@/models/bug-management';
@ -287,6 +287,7 @@
const syncVisible = ref(false);
const exportVisible = ref(false);
const exportOptionData = ref<MsExportDrawerMap>({});
const exportLoading = ref(false);
const currentPlatform = ref('Local');
const detailVisible = ref(false);
const activeDetailId = ref<string>('');
@ -545,9 +546,33 @@
],
};
function initTableParams() {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: keyword.value,
projectId: projectId.value,
filter: { ...filterParams },
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
},
};
}
function searchData() {
setLoadListParams(initTableParams());
loadList();
}
const fetchData = async (v = '') => {
setKeyword(v);
await loadList();
searchData();
};
const handleAdvSearch = (filter: FilterResult) => {
@ -568,14 +593,15 @@
const exportConfirm = async (option: MsExportDrawerOption[]) => {
try {
const params = tableParamsToRequestParams(currentSelectParams.value);
exportLoading.value = true;
const blob = await exportBug({
...params,
...currentSelectParams.value,
exportColumns: option.map((item) => item),
projectId: appStore.currentProjectId,
exportSort: sort.value,
});
downloadByteFile(blob, `${t('bugManagement.exportBug')}.zip`);
exportLoading.value = false;
exportVisible.value = false;
resetSelector();
} catch (error) {
@ -729,10 +755,10 @@
});
};
const handleBatchDelete = (params: BatchActionQueryParams) => {
let dataCount = params.selectedIds?.length;
if (params.selectAll) {
dataCount = params.currentSelectCount;
const handleBatchDelete = () => {
let dataCount = currentSelectParams.value.selectIds?.length;
if (currentSelectParams.value.selectAll) {
dataCount = currentSelectParams.value.currentSelectCount;
}
openDeleteModal({
title: t('bugManagement.deleteCount', { count: dataCount }),
@ -740,7 +766,7 @@
onBeforeOk: async () => {
try {
const tmpObj = {
...tableParamsToRequestParams(params),
...currentSelectParams.value,
projectId: projectId.value,
};
await deleteBatchBug(tmpObj);
@ -755,18 +781,8 @@
});
};
const handleBatchEdit = (params: BatchActionQueryParams) => {
const handleBatchEdit = () => {
batchEditVisible.value = true;
const condition = {
keyword: keyword.value,
searchMode: filterResult.value.accordBelow,
filter: propsRes.value.filter,
combine: filterResult.value.combine,
};
currentSelectParams.value = {
...params,
condition,
};
};
const handleExport = () => {
@ -810,17 +826,29 @@
} else {
params.condition = { filter: { ...filterParams } };
}
currentSelectParams.value = params;
const condition = {
keyword: keyword.value,
searchMode: filterResult.value.accordBelow,
filter: params.condition.filter,
combine: filterResult.value.combine,
};
currentSelectParams.value = {
excludeIds: params.excludeIds,
selectAll: params.selectAll,
selectIds: params.selectedIds,
currentSelectCount: params.currentSelectCount,
condition,
};
switch (event.eventTag) {
case 'export':
handleExport();
break;
case 'delete':
handleBatchDelete(params);
handleBatchDelete();
break;
case 'edit':
handleBatchEdit(params);
handleBatchEdit();
break;
default:
break;
@ -839,30 +867,6 @@
sort.value = sortObj;
}
function initTableParams() {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: keyword.value,
projectId: projectId.value,
filter: { ...filterParams },
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
},
};
}
function searchData() {
setLoadListParams(initTableParams());
loadList();
}
watchEffect(() => {
setProps({ heightUsed: heightUsed.value });
});

View File

@ -112,6 +112,7 @@ export default {
recycle: {
recycleBin: 'Recycle',
recover: 'Recover',
recovering: 'Recovering',
recoverSuccess: 'Recover success',
recoverError: 'Recover failed',
permanentlyDelete: 'Permanently delete',

View File

@ -111,6 +111,7 @@ export default {
recycle: {
recycleBin: '回收站',
recover: '恢复',
recovering: '正在恢复中',
recoverSuccess: '恢复成功',
recoverError: '恢复失败',
permanentlyDelete: '彻底删除',

View File

@ -7,7 +7,8 @@
:filter-config-list="filterConfigList"
:row-count="filterRowCount"
@keyword-search="fetchData"
@refresh="fetchData('')"
@adv-search="handleAdvSearch"
@refresh="handleAdvSearch"
>
<template #left>
<div></div>
@ -136,7 +137,7 @@
import dayjs from 'dayjs';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { BackEndEnum, FilterFormItem, FilterType } from '@/components/pure/ms-advance-filter/type';
import { BackEndEnum, 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';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
@ -156,12 +157,7 @@
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { useAppStore, useTableStore } from '@/store';
import {
characterLimit,
customFieldDataToTableData,
customFieldToColumns,
tableParamsToRequestParams,
} from '@/utils';
import { characterLimit, customFieldDataToTableData, customFieldToColumns } from '@/utils';
import { BugEditCustomField, BugListItem, BugOptionItem } from '@/models/bug-management';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -215,6 +211,8 @@
const severityFilterVisible = ref(false);
const severityFilterValue = ref<string[]>([]);
const severityColumnId = ref('');
const filterResult = ref<FilterResult>({ accordBelow: 'AND', combine: {} });
const currentSelectParams = ref<BatchActionQueryParams>({ selectAll: false, currentSelectCount: 0 });
const heightUsed = computed(() => 286 + (filterVisible.value ? 160 + (filterRowCount.value - 1) * 60 : 0));
@ -409,9 +407,39 @@
setProps({ heightUsed: heightUsed.value });
});
function initTableParams() {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
deleteUser: deleteUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: keyword.value,
projectId: projectId.value,
filter: { ...filterParams },
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
},
};
}
function searchData() {
setLoadListParams(initTableParams());
loadList();
}
const fetchData = async (v = '') => {
setKeyword(v);
await loadList();
searchData();
};
const handleAdvSearch = (filter: FilterResult) => {
filterResult.value = filter;
fetchData();
};
//
@ -427,11 +455,14 @@
};
//
const handleBatchRecover = async (params: BatchActionQueryParams) => {
const handleBatchRecover = async () => {
try {
const tmpObj = { ...tableParamsToRequestParams(params), projectId: projectId.value };
appStore.showLoading(t('bugManagement.recycle.recovering'));
const tmpObj = { ...currentSelectParams.value, projectId: projectId.value };
await recoverBatchByRecycle(tmpObj);
appStore.hideLoading();
Message.success(t('bugManagement.recycle.recoverSuccess'));
keyword.value = '';
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
@ -457,15 +488,16 @@
});
};
//
const handleBatchDelete = (params: BatchActionQueryParams) => {
const handleBatchDelete = () => {
openDeleteModal({
title: t('bugManagement.recycle.batchDelete', { count: params.currentSelectCount }),
title: t('bugManagement.recycle.batchDelete', { count: currentSelectParams.value.currentSelectCount }),
content: t('bugManagement.recycle.deleteContent'),
onBeforeOk: async () => {
try {
const tmpObj = { ...tableParamsToRequestParams(params), projectId: projectId.value };
const tmpObj = { ...currentSelectParams.value, projectId: projectId.value };
await deleteBatchByRecycle(tmpObj);
Message.success(t('common.deleteSuccess'));
keyword.value = '';
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
@ -490,43 +522,32 @@
} else {
params.condition = { filter: { ...filterParams } };
}
const condition = {
keyword: keyword.value,
searchMode: filterResult.value.accordBelow,
filter: params.condition.filter,
combine: filterResult.value.combine,
};
currentSelectParams.value = {
excludeIds: params.excludeIds,
selectAll: params.selectAll,
selectIds: params.selectedIds,
currentSelectCount: params.currentSelectCount,
condition,
};
switch (event.eventTag) {
case 'recover':
handleBatchRecover(params);
handleBatchRecover();
break;
case 'delete':
handleBatchDelete(params);
handleBatchDelete();
break;
default:
break;
}
}
function initTableParams() {
const filterParams: Record<string, any> = {
status: statusFilterValue.value,
handleUser: handleUserFilterValue.value,
updateUser: updateUserFilterValue.value,
createUser: createUserFilterValue.value,
deleteUser: deleteUserFilterValue.value,
};
filterParams[severityColumnId.value] = severityFilterValue.value;
return {
keyword: keyword.value,
projectId: projectId.value,
filter: { ...filterParams },
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
},
};
}
function searchData() {
setLoadListParams(initTableParams());
loadList();
}
async function initFilterOptions() {
const res = await getCustomOptionHeader(appStore.currentProjectId);
createUserFilterOptions.value = res.userOption;