fix(系统设置): 修复系统的任务中心没有显示全部项目的缺陷

--bug=1040246 --user=王孝刚 【系统设置】系统/组织-任务中心-所属项目-下拉应该显示所有组织下的所有项目
https://www.tapd.cn/55049933/s/1511602
This commit is contained in:
wxg0103 2024-05-06 11:52:07 +08:00 committed by Craftsman
parent b7e8340a7c
commit 840acafe6f
12 changed files with 103 additions and 14 deletions

View File

@ -175,4 +175,12 @@ public class SystemProjectController {
systemProjectService.rename(request, SessionUtils.getUserId()); systemProjectService.rename(request, SessionUtils.getUserId());
} }
@GetMapping("/list")
@Operation(summary = "系统设置-系统-组织与项目-项目-获取所有项目")
@RequiresPermissions(PermissionConstants.SYSTEM_ORGANIZATION_PROJECT_READ)
public List<OptionDTO> getProjectList(@Schema(description = "查询关键字,根据项目名查询", requiredMode = Schema.RequiredMode.REQUIRED) @RequestParam(value = "keyword", required = false) String keyword) {
return systemProjectService.list(keyword);
}
} }

View File

@ -5,7 +5,7 @@
<select id="selectListProjectByOrg" resultType="io.metersphere.system.dto.sdk.OptionDTO"> <select id="selectListProjectByOrg" resultType="io.metersphere.system.dto.sdk.OptionDTO">
select id, name select id, name
from project from project
where deleted = 0 where enable = 1
and organization_id = #{organizationId} and organization_id = #{organizationId}
<if test="keyword != null and keyword != ''"> <if test="keyword != null and keyword != ''">
and name LIKE CONCAT('%', #{keyword}, '%') and name LIKE CONCAT('%', #{keyword}, '%')

View File

@ -6,6 +6,7 @@ import io.metersphere.system.dto.ProjectDTO;
import io.metersphere.system.dto.ProjectResourcePoolDTO; import io.metersphere.system.dto.ProjectResourcePoolDTO;
import io.metersphere.system.dto.request.ProjectMemberRequest; import io.metersphere.system.dto.request.ProjectMemberRequest;
import io.metersphere.system.dto.request.ProjectRequest; import io.metersphere.system.dto.request.ProjectRequest;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.dto.user.UserExtendDTO; import io.metersphere.system.dto.user.UserExtendDTO;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -34,4 +35,6 @@ public interface ExtSystemProjectMapper {
List<UserExtendDTO> getMemberByProjectId(@Param("projectId") String projectId, @Param("keyword") String keyword); List<UserExtendDTO> getMemberByProjectId(@Param("projectId") String projectId, @Param("keyword") String keyword);
List<User> getProjectMemberByUserId(@Param("projectId") String projectId, @Param("userIds") List<String> userIds); List<User> getProjectMemberByUserId(@Param("projectId") String projectId, @Param("userIds") List<String> userIds);
List<OptionDTO> getSystemProject(@Param("keyword") String keyword);
} }

View File

@ -224,4 +224,14 @@
</foreach> </foreach>
</if> </if>
</select> </select>
<select id="getSystemProject" resultType="io.metersphere.system.dto.sdk.OptionDTO">
select id, name
from project
where enable = 1
<if test="keyword != null and keyword != ''">
and name LIKE CONCAT('%', #{keyword}, '%')
</if>
order by update_time desc
limit 1000
</select>
</mapper> </mapper>

View File

@ -105,4 +105,9 @@ public class SystemProjectService {
public void rename(UpdateProjectNameRequest project, String userId) { public void rename(UpdateProjectNameRequest project, String userId) {
commonProjectService.rename(project, userId); commonProjectService.rename(project, userId);
} }
public List<OptionDTO> list(String keyword) {
return extSystemProjectMapper.getSystemProject(keyword);
}
} }

View File

@ -16,6 +16,7 @@ import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.domain.*; import io.metersphere.system.domain.*;
import io.metersphere.system.dto.*; import io.metersphere.system.dto.*;
import io.metersphere.system.dto.request.*; import io.metersphere.system.dto.request.*;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest; import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.system.dto.sdk.request.PosRequest; import io.metersphere.system.dto.sdk.request.PosRequest;
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest; import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
@ -1217,4 +1218,19 @@ public class SystemProjectControllerTests extends BaseTest {
apiTestCaseMapper.deleteByPrimaryKey(anotherCase.getId()); apiTestCaseMapper.deleteByPrimaryKey(anotherCase.getId());
} }
@Test
@Order(17)
public void getProjectListByOrgSuccess() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(prefix + "/list")
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString();
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
List<OptionDTO> projectList = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), OptionDTO.class);
Assertions.assertTrue(CollectionUtils.isNotEmpty(projectList));
}
} }

View File

@ -1,5 +1,6 @@
// 系统全局类的接口 // 系统全局类的接口
import MSR from '@/api/http/index'; import MSR from '@/api/http/index';
import { getSystemProjectListUrl } from '@/api/requrls/setting/member';
import { import {
GetVersionUrl, GetVersionUrl,
OrgOptionsUrl, OrgOptionsUrl,
@ -32,3 +33,7 @@ export function getPackageType() {
export function getUserHasProjectPermission(userId: string) { export function getUserHasProjectPermission(userId: string) {
return MSR.get({ url: `${userHasProjectPermissionUrl}/${userId}` }); return MSR.get({ url: `${userHasProjectPermissionUrl}/${userId}` });
} }
export function getSystemProjectList(keyword: string) {
return MSR.get({ url: getSystemProjectListUrl, params: { keyword } });
}

View File

@ -12,3 +12,4 @@ export const getUserGroupList = '/organization/user/role/list';
export const getUserList = '/organization/not-exist/user/list'; export const getUserList = '/organization/not-exist/user/list';
// 获取弹窗里边的穿梭项目列表 // 获取弹窗里边的穿梭项目列表
export const getProjectListUrl = '/organization/project/list'; export const getProjectListUrl = '/organization/project/list';
export const getSystemProjectListUrl = '/system/project/list'; // 获取系统项目列表

View File

@ -8,7 +8,7 @@ import {
getUserByProjectByOrg, getUserByProjectByOrg,
} from '@/api/modules/setting/organizationAndProject'; } from '@/api/modules/setting/organizationAndProject';
import { getOrgUserGroupOption, getSystemUserGroupOption } from '@/api/modules/setting/usergroup'; import { getOrgUserGroupOption, getSystemUserGroupOption } from '@/api/modules/setting/usergroup';
import { getOrgOptions } from '@/api/modules/system'; import { getOrgOptions, getSystemProjectList } from '@/api/modules/system';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
export enum UserRequestTypeEnum { export enum UserRequestTypeEnum {
SYSTEM_USER_GROUP = 'SYSTEM_USER_GROUP', SYSTEM_USER_GROUP = 'SYSTEM_USER_GROUP',
@ -25,6 +25,7 @@ export enum UserRequestTypeEnum {
PROJECT_PERMISSION_MEMBER = 'PROJECT_PERMISSION_MEMBER', PROJECT_PERMISSION_MEMBER = 'PROJECT_PERMISSION_MEMBER',
PROJECT_USER_GROUP = 'PROJECT_USER_GROUP', PROJECT_USER_GROUP = 'PROJECT_USER_GROUP',
SYSTEM_ORGANIZATION_LIST = 'SYSTEM_ORGANIZATION_LIST', SYSTEM_ORGANIZATION_LIST = 'SYSTEM_ORGANIZATION_LIST',
SYSTEM_PROJECT_LIST = 'SYSTEM_PROJECT_LIST',
} }
export default function initOptionsFunc(type: string, params: Record<string, any>) { export default function initOptionsFunc(type: string, params: Record<string, any>) {
if (type === UserRequestTypeEnum.SYSTEM_USER_GROUP) { if (type === UserRequestTypeEnum.SYSTEM_USER_GROUP) {
@ -71,4 +72,8 @@ export default function initOptionsFunc(type: string, params: Record<string, any
// 系统-组织 // 系统-组织
return getOrgOptions(); return getOrgOptions();
} }
if (type === UserRequestTypeEnum.SYSTEM_PROJECT_LIST) {
// 系统-项目 获取系统下所有的项目
return getSystemProjectList(params.keyword);
}
} }

View File

@ -107,7 +107,7 @@
// //
const initProjectMemberOptions = async () => { const initProjectMemberOptions = async () => {
try { try {
if (lastProjectId) { if (lastProjectId.value) {
const result = await getProjectMemberOptions(lastProjectId.value); const result = await getProjectMemberOptions(lastProjectId.value);
memberList.value = result; memberList.value = result;
} }

View File

@ -102,7 +102,7 @@
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template v-if="appStore.packageType === 'enterprise'" #orgFilterName="{ columnConfig }"> <template v-if="appStore.packageType === 'enterprise' && xPack" #orgFilterName="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="orgFilterVisible" v-model:visible="orgFilterVisible"
v-model:status-filters="orgFiltersMap[props.moduleType]" v-model:status-filters="orgFiltersMap[props.moduleType]"
@ -110,13 +110,26 @@
mode="remote" mode="remote"
value-key="id" value-key="id"
label-key="name" label-key="name"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_LIST" :type="
groupColumnsMap[props.group].key === TableKeyEnum.TASK_SCHEDULE_TASK_SYSTEM
? UserRequestTypeEnum.SYSTEM_PROJECT_LIST
: UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT
"
:placeholder-text="t('project.taskCenter.filterOrgPlaceholderText')" :placeholder-text="t('project.taskCenter.filterOrgPlaceholderText')"
@search="initData()" @search="initData()"
> >
</TableFilter> </TableFilter>
</template> </template>
<template #projectFilterName="{ columnConfig }"> <template
v-if="
hasAnyPermission(
groupColumnsMap[props.group].key === TableKeyEnum.TASK_API_CASE_SYSTEM
? ['SYSTEM_ORGANIZATION_PROJECT:READ']
: ['ORGANIZATION_PROJECT:READ']
)
"
#projectFilterName="{ columnConfig }"
>
<TableFilter <TableFilter
v-model:visible="projectFilterVisible" v-model:visible="projectFilterVisible"
v-model:status-filters="projectFiltersMap[props.moduleType]" v-model:status-filters="projectFiltersMap[props.moduleType]"
@ -124,7 +137,11 @@
mode="remote" mode="remote"
:load-option-params="{ organizationId: appStore.currentOrgId }" :load-option-params="{ organizationId: appStore.currentOrgId }"
:placeholder-text="t('project.taskCenter.filterProPlaceholderText')" :placeholder-text="t('project.taskCenter.filterProPlaceholderText')"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT" :type="
groupColumnsMap[props.group].key === TableKeyEnum.TASK_API_CASE_SYSTEM
? UserRequestTypeEnum.SYSTEM_PROJECT_LIST
: UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT
"
@search="initData()" @search="initData()"
> >
</TableFilter> </TableFilter>
@ -179,7 +196,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { computed, ref } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -208,6 +225,7 @@
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import useOpenNewPage from '@/hooks/useOpenNewPage'; import useOpenNewPage from '@/hooks/useOpenNewPage';
import { useAppStore, useTableStore } from '@/store'; import { useAppStore, useTableStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import { characterLimit } from '@/utils'; import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
@ -455,6 +473,8 @@
API_CASE: projectApiCaseFilter.value, API_CASE: projectApiCaseFilter.value,
API_SCENARIO: projectApiScenarioFilter.value, API_SCENARIO: projectApiScenarioFilter.value,
}); });
const licenseStore = useLicenseStore();
const xPack = computed(() => licenseStore.hasLicense());
function initData() { function initData() {
setLoadListParams({ setLoadListParams({
@ -469,7 +489,6 @@
}); });
loadList(); loadList();
} }
function handleFilterReset() { function handleFilterReset() {
statusFiltersMap.value[props.moduleType] = []; statusFiltersMap.value[props.moduleType] = [];
statusFilterVisible.value = false; statusFilterVisible.value = false;
@ -517,7 +536,7 @@
await loadRealMap.value[props.group].batchStop({ await loadRealMap.value[props.group].batchStop({
moduleType: props.moduleType, moduleType: props.moduleType,
selectIds: selectIds || [], selectIds: selectIds || [],
selectAll: !!selectAll, selectAll,
excludeIds: excludeIds || [], excludeIds: excludeIds || [],
condition: { condition: {
keyword: keyword.value, keyword: keyword.value,

View File

@ -65,7 +65,7 @@
</a-option> </a-option>
</a-select> </a-select>
</template> </template>
<template v-if="appStore.packageType === 'enterprise'" #orgFilterName="{ columnConfig }"> <template v-if="appStore.packageType === 'enterprise' && xPack" #orgFilterName="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="orgFilterVisible" v-model:visible="orgFilterVisible"
v-model:status-filters="orgFiltersMap[props.moduleType]" v-model:status-filters="orgFiltersMap[props.moduleType]"
@ -79,7 +79,16 @@
> >
</TableFilter> </TableFilter>
</template> </template>
<template #projectFilterName="{ columnConfig }"> <template
v-if="
hasAnyPermission(
groupColumnsMap[props.group].key === TableKeyEnum.TASK_SCHEDULE_TASK_SYSTEM
? ['SYSTEM_ORGANIZATION_PROJECT:READ']
: ['ORGANIZATION_PROJECT:READ']
)
"
#projectFilterName="{ columnConfig }"
>
<TableFilter <TableFilter
v-model:visible="projectFilterVisible" v-model:visible="projectFilterVisible"
v-model:status-filters="projectFiltersMap[props.moduleType]" v-model:status-filters="projectFiltersMap[props.moduleType]"
@ -87,7 +96,11 @@
mode="remote" mode="remote"
:load-option-params="{ organizationId: appStore.currentOrgId }" :load-option-params="{ organizationId: appStore.currentOrgId }"
:placeholder-text="t('project.taskCenter.filterProPlaceholderText')" :placeholder-text="t('project.taskCenter.filterProPlaceholderText')"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT" :type="
groupColumnsMap[props.group].key === TableKeyEnum.TASK_SCHEDULE_TASK_SYSTEM
? UserRequestTypeEnum.SYSTEM_PROJECT_LIST
: UserRequestTypeEnum.SYSTEM_ORGANIZATION_PROJECT
"
@search="initData()" @search="initData()"
> >
</TableFilter> </TableFilter>
@ -118,7 +131,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { computed, ref } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -154,6 +167,7 @@
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import useOpenNewPage from '@/hooks/useOpenNewPage'; import useOpenNewPage from '@/hooks/useOpenNewPage';
import { useAppStore, useTableStore } from '@/store'; import { useAppStore, useTableStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
import { BatchApiParams } from '@/models/common'; import { BatchApiParams } from '@/models/common';
@ -352,6 +366,9 @@
const hasJumpPermission = computed(() => hasAnyPermission(permissionsMap[props.group][props.moduleType].jump)); const hasJumpPermission = computed(() => hasAnyPermission(permissionsMap[props.group][props.moduleType].jump));
const licenseStore = useLicenseStore();
const xPack = computed(() => licenseStore.hasLicense());
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable( const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
loadRealMap.value[props.group].list, loadRealMap.value[props.group].list,
{ {