feat: 用户没有任何权限进入项目默认展示页面
This commit is contained in:
parent
c978deefc0
commit
e15e0611f7
|
@ -164,3 +164,17 @@ export function postUpdateEnableFake(data: FakeTableOperationParams) {
|
|||
export function getDeleteFake(data: FakeTableOperationParams) {
|
||||
return MSR.post<FakeTableListItem[]>({ url: Url.getFakeTableDeleteUrl, data });
|
||||
}
|
||||
|
||||
// JIRA插件key校验
|
||||
export function validateJIRAKey(data: object, pluginId: string) {
|
||||
return MSR.post<FakeTableListItem[]>({ url: `${Url.postValidateJiraKeyUrl}${pluginId}`, data });
|
||||
}
|
||||
// 缺陷管理-获取同步信息
|
||||
export function getBugSyncInfo(projectId: string) {
|
||||
return MSR.get<FakeTableListItem[]>({ url: `${Url.getBugSyncInfoUrl}${projectId}` });
|
||||
}
|
||||
|
||||
// 用例管理-获取关联需求信息
|
||||
export function getCaseRelatedInfo(projectId: string) {
|
||||
return MSR.get<FakeTableListItem[]>({ url: `${Url.getCaseRelatedInfoUrl}${projectId}` });
|
||||
}
|
||||
|
|
|
@ -25,3 +25,9 @@ export const postFakeTableAddUrl = '/fake/error/add';
|
|||
export const postFakeTableUpdateUrl = '/fake/error/update';
|
||||
// 误报规则列表启用或禁用
|
||||
export const postFakeTableEnableUrl = '/fake/error/update/enable';
|
||||
// JIRAKEY 校验
|
||||
export const postValidateJiraKeyUrl = '/project/application/validate/';
|
||||
// 缺陷管理-获取同步信息
|
||||
export const getBugSyncInfoUrl = '/project/application/bug/sync/info/';
|
||||
// 用例管理-获取关联需求信息
|
||||
export const getCaseRelatedInfoUrl = '/project/application/case/related/info/';
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
:placeholder="t('project.menu.pleaseInputJiraKey')"
|
||||
v-bind="attrs"
|
||||
@change="(v: string) => emit('update:modelValue', v)"
|
||||
@blur="handleBlur"
|
||||
/>
|
||||
<div class="flex flex-row items-center gap-[10px] text-[12px] leading-[16px]">
|
||||
<span class="text-[var(--color-text-4)]">{{ t('project.menu.howGetJiraKey') }}</span>
|
||||
|
@ -20,6 +21,9 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import { validateJIRAKey } from '@/api/modules/project-management/menuManagement';
|
||||
import { getLogo } from '@/api/modules/setting/serviceIntegration';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
|
@ -44,6 +48,17 @@
|
|||
previewIcon.value = URL.createObjectURL(new Blob([data]));
|
||||
});
|
||||
});
|
||||
const handleBlur = async () => {
|
||||
const pluginId = sessionStorage.getItem('platformKey') || '';
|
||||
if (pluginId) {
|
||||
try {
|
||||
await validateJIRAKey({ name: '1231' }, pluginId);
|
||||
Message.success('common.validateSuccess');
|
||||
} catch {
|
||||
Message.error('common.validateFaild');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
</template>
|
||||
<TopMenu />
|
||||
</div>
|
||||
<ul v-if="!props.isPreview" class="right-side">
|
||||
<ul v-if="!props.isPreview && !props.hideRight" class="right-side">
|
||||
<li>
|
||||
<a-tooltip :content="t('settings.navbar.search')">
|
||||
<a-button type="secondary">
|
||||
|
@ -142,6 +142,7 @@
|
|||
isPreview?: boolean;
|
||||
logo?: string;
|
||||
name?: string;
|
||||
hideRight?: boolean;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
<template>
|
||||
<a-layout class="layout" :class="{ mobile: appStore.hideMenu }">
|
||||
<div v-if="navbar" class="layout-navbar z-[100]">
|
||||
<NavBar :is-preview="innerProps.isPreview" :logo="innerLogo" :name="innerName" />
|
||||
<NavBar
|
||||
:is-preview="innerProps.isPreview"
|
||||
:hide-right="innerProps.hideRight"
|
||||
:logo="innerLogo"
|
||||
:name="innerName"
|
||||
/>
|
||||
</div>
|
||||
<slot name="body">
|
||||
<a-layout>
|
||||
|
@ -44,7 +49,9 @@
|
|||
>
|
||||
<MsBreadCrumb />
|
||||
<a-layout-content>
|
||||
<PageLayout v-if="!props.isPreview" />
|
||||
<slot name="page">
|
||||
<PageLayout v-if="!props.isPreview" />
|
||||
</slot>
|
||||
<slot></slot>
|
||||
</a-layout-content>
|
||||
<Footer v-if="footer" />
|
||||
|
@ -76,6 +83,7 @@
|
|||
logo?: string;
|
||||
name?: string;
|
||||
singleLogo?: boolean;
|
||||
hideRight?: boolean;
|
||||
}
|
||||
const props = defineProps<Props>();
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<div class="end-item">
|
||||
<DefaultLayout
|
||||
:logo="pageConfig.logoPlatform[0]?.url || defaultPlatformLogo"
|
||||
:name="pageConfig.platformName"
|
||||
class="overflow-hidden"
|
||||
hide-right
|
||||
>
|
||||
<template #page>
|
||||
<div class="page">
|
||||
<div class="content-wrapper">
|
||||
<div class="content">
|
||||
<div class="icon">
|
||||
<div class="icon-svg">
|
||||
<svg-icon width="232px" height="184px" name="no_resource" />
|
||||
</div>
|
||||
<div class="radius-box"></div>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{ t('common.noProject') }}</span>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DefaultLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DefaultLayout from './default-layout.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
const defaultPlatformLogo = `${import.meta.env.BASE_URL}images/MS-full-logo.svg`;
|
||||
const appStore = useAppStore();
|
||||
const pageConfig = ref({ ...appStore.pageConfig });
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.end-item {
|
||||
:deep(.arco-menu-vertical) {
|
||||
.arco-menu-inner {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
}
|
||||
.page {
|
||||
height: 100vh;
|
||||
background-color: var(--color-text-fff);
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-color: var(--color-text-fff);
|
||||
.content {
|
||||
.icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
height: 218px;
|
||||
flex-direction: column;
|
||||
.icon-svg {
|
||||
z-index: 100;
|
||||
}
|
||||
.radius-box {
|
||||
position: relative;
|
||||
bottom: 83px;
|
||||
width: 355px;
|
||||
height: 117px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 355px;
|
||||
background: linear-gradient(180deg, #ededf1 0%, rgb(255 255 255 / 0%) 100%);
|
||||
}
|
||||
}
|
||||
.title {
|
||||
font-size: 16px;
|
||||
color: var(--color-text-1);
|
||||
.user {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -18,9 +18,6 @@
|
|||
</div>
|
||||
<div class="title">
|
||||
<span>{{ props.isProject ? t('common.noProject') : t('common.noResource') }}</span>
|
||||
<span class="user">
|
||||
{{ adminStr }}
|
||||
</span>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -34,32 +31,18 @@
|
|||
<script setup lang="ts">
|
||||
import DefaultLayout from './default-layout.vue';
|
||||
|
||||
import { getAdminByProjectByOrg } from '@/api/modules/setting/organizationAndProject';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
const defaultPlatformLogo = `${import.meta.env.BASE_URL}images/MS-full-logo.svg`;
|
||||
const appStore = useAppStore();
|
||||
const pageConfig = ref({ ...appStore.pageConfig });
|
||||
const adminStr = ref<string>('');
|
||||
|
||||
const props = defineProps<{
|
||||
isProject?: boolean;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const initData = async () => {
|
||||
try {
|
||||
const res = (await getAdminByProjectByOrg(appStore.currentOrgId, '')) || [];
|
||||
adminStr.value = res.map((item) => item.name).join(';');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -4,11 +4,12 @@ import type { RouteRecordRaw } from 'vue-router';
|
|||
|
||||
export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue');
|
||||
export const PAGE_LAYOUT = () => import('@/layout/page-layout.vue');
|
||||
export const NO_PERMISSION_LAYOUT = () => import('@/layout/no-permission-layout.vue');
|
||||
|
||||
export const INDEX_ROUTE: RouteRecordRaw = {
|
||||
path: '/index',
|
||||
name: 'metersphereIndex',
|
||||
component: DEFAULT_LAYOUT,
|
||||
component: NO_PERMISSION_LAYOUT,
|
||||
meta: {
|
||||
hideInMenu: true,
|
||||
roles: ['*'],
|
||||
|
|
|
@ -49,7 +49,7 @@ export function hasAnyPermission(permissions: string[], typeList = ['PROJECT', '
|
|||
}
|
||||
|
||||
function filterProject(role: UserRole, id: string) {
|
||||
return role && role.type === 'PROJECT' && role.scopeId === id;
|
||||
return role && role.type === 'PROJECT' && (role.scopeId === id || role.scopeId === 'global');
|
||||
}
|
||||
function filterOrganization(role: UserRole, id: string) {
|
||||
return role && role.type === 'ORGANIZATION' && (role.scopeId === id || role.scopeId === 'global');
|
||||
|
|
|
@ -121,6 +121,10 @@
|
|||
import { PoolOption, SelectValue } from '@/models/projectManagement/menuManagement';
|
||||
import { MenuEnum } from '@/enums/commonEnum';
|
||||
|
||||
defineOptions({
|
||||
name: 'DefectSync',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
|
|
|
@ -799,7 +799,7 @@
|
|||
};
|
||||
|
||||
// 点击展开
|
||||
const handleRowClick = (record: TableData, ev: Event) => {
|
||||
const handleRowClick = (record: TableData) => {
|
||||
if (record.module) {
|
||||
expandChange(record);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
'organization.member.addMember': '添加成员',
|
||||
'organization.member.updateMember': '更新成员({name})',
|
||||
'organization.member.searchMember': '通过名称或邮箱搜索搜索',
|
||||
'organization.member.searchMember': '通过名称或邮箱搜索',
|
||||
'organization.member.remove': '移除',
|
||||
'organization.member.edit': '编辑',
|
||||
'organization.member.batchActionAddProject': '添加至项目',
|
||||
|
|
|
@ -142,6 +142,7 @@
|
|||
}
|
||||
Message.success(t('common.removeSuccess'));
|
||||
fetchData();
|
||||
emit('requestFetchData');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<MsCard simple>
|
||||
<div class="mb-[16px] flex items-center justify-between">
|
||||
<div
|
||||
class="mb-[16px] flex items-center"
|
||||
:class="{ 'justify-between': hasAddPermission, 'justify-end': !hasAddPermission }"
|
||||
>
|
||||
<a-button v-permission="['ORGANIZATION_PROJECT:READ+ADD']" type="primary" @click="showAddProject">{{
|
||||
t('system.organization.createProject')
|
||||
}}</a-button>
|
||||
|
@ -124,6 +127,7 @@
|
|||
const { openDeleteModal, openModal } = useModal();
|
||||
const appStore = useAppStore();
|
||||
const currentOrgId = computed(() => appStore.currentOrgId);
|
||||
const hasAddPermission = computed(() => hasAnyPermission(['ORGANIZATION_PROJECT:READ+ADD']));
|
||||
const hasOperationPermission = computed(() =>
|
||||
hasAnyPermission([
|
||||
'ORGANIZATION_PROJECT:READ+RECOVER',
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<template #operation="{ record }">
|
||||
<MsRemoveButton
|
||||
:title="t('system.organization.removeName', { name: record.name })"
|
||||
:sub-title-tip="t('system.organization.removeTip')"
|
||||
:sub-title-tip="props.organizationId ? t('system.organization.removeTip') : t('system.project.removeTip')"
|
||||
@ok="handleRemove(record)"
|
||||
/>
|
||||
</template>
|
||||
|
@ -160,10 +160,11 @@
|
|||
await deleteUserFromOrgOrProject(props.organizationId, record.id);
|
||||
}
|
||||
if (props.projectId) {
|
||||
await deleteUserFromOrgOrProject(props.projectId, record.id, true);
|
||||
await deleteUserFromOrgOrProject(props.projectId, record.id, false);
|
||||
}
|
||||
Message.success(t('common.removeSuccess'));
|
||||
fetchData();
|
||||
emit('requestFetchData');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
|
|
|
@ -73,4 +73,5 @@ export default {
|
|||
'system.project.createTip': 'After the project is enabled, it will be displayed in the project switching list',
|
||||
'system.project.affiliatedOrgRequired': 'Affiliated organization cannot be empty',
|
||||
'system.project.revokeDeleteToolTip': 'The project will be deleted automatically after 30 days',
|
||||
'system.project.removeTip': "Remove it, and you'll lose access to the project.",
|
||||
};
|
||||
|
|
|
@ -68,4 +68,5 @@ export default {
|
|||
'system.project.createTip': '项目启用后,将展示在项目切换列表',
|
||||
'system.project.affiliatedOrgRequired': '所属组织不能为空',
|
||||
'system.project.revokeDeleteToolTip': '该项目将于30 天后自动删除',
|
||||
'system.project.removeTip': '移除后,将失去项目权限',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue