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:
parent
6805e8cf06
commit
f78286eaf5
|
@ -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())) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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; // 当前选中的数量
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 });
|
||||
});
|
||||
|
|
|
@ -112,6 +112,7 @@ export default {
|
|||
recycle: {
|
||||
recycleBin: 'Recycle',
|
||||
recover: 'Recover',
|
||||
recovering: 'Recovering',
|
||||
recoverSuccess: 'Recover success',
|
||||
recoverError: 'Recover failed',
|
||||
permanentlyDelete: 'Permanently delete',
|
||||
|
|
|
@ -111,6 +111,7 @@ export default {
|
|||
recycle: {
|
||||
recycleBin: '回收站',
|
||||
recover: '恢复',
|
||||
recovering: '正在恢复中',
|
||||
recoverSuccess: '恢复成功',
|
||||
recoverError: '恢复失败',
|
||||
permanentlyDelete: '彻底删除',
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue