feat(功能用例): 用例导入联调&bug修改&部分权限&非路由xpack入口控制

This commit is contained in:
xinxin.wu 2024-01-25 11:26:52 +08:00 committed by 刘瑞斌
parent 9117526096
commit 4305aa9add
48 changed files with 468 additions and 194 deletions

View File

@ -53,8 +53,10 @@
onBeforeMount(async () => {
try {
appStore.initSystemVersion(); //
appStore.initPageConfig(); //
licenseStore.getValidateLicense(); // license
if (licenseStore.hasLicense()) {
appStore.initPageConfig(); //
}
// url url
const isInitUrl = getLocalStorage('isInitUrl'); // url
if (isInitUrl === 'true') return;

View File

@ -55,6 +55,7 @@ import {
GetSearchCustomFieldsUrl,
getTransferTreeUrl,
GetTrashCaseModuleTreeUrl,
importExcelCaseUrl,
MoveCaseModuleTreeUrl,
PreviewEditorImageUrl,
PreviewFileUrl,
@ -395,5 +396,9 @@ export function editorUploadFile(data: { fileList: File[] }) {
export function editorPreviewImages(data: PreviewImages) {
return MSR.post({ url: PreviewEditorImageUrl, data });
}
// 导入excel
export function importExcelCase(data: { request: ImportExcelType; fileList: File[] }) {
return MSR.uploadFile({ url: importExcelCaseUrl }, { request: data.request, fileList: data.fileList }, '');
}
export default {};

View File

@ -141,3 +141,5 @@ export const EditorUploadFileUrl = '/attachment/upload/temp/file';
export const PreviewEditorImageUrl = '/attachment/download/file';
// 导入excel文件检查
export const exportExcelCheckUrl = '/functional/case/pre-check/excel';
// 导入excel文件
export const importExcelCaseUrl = '/functional/case/import/excel';

View File

@ -10,7 +10,7 @@
<template #content>
<a-upload
ref="uploadRef"
v-model:file-list="fileList"
v-model:file-list="innerFileList"
:auto-upload="false"
:show-file-list="false"
:before-upload="beforeUpload"
@ -38,7 +38,7 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useVModel } from '@vueuse/core';
import type { MsFileItem } from '@/components/pure/ms-upload/types';
@ -46,19 +46,26 @@
const { t } = useI18n();
const props = defineProps<{
fileList: MsFileItem[];
}>();
const emit = defineEmits<{
(e: 'upload', file: File): void;
(e: 'change', _fileList: MsFileItem[], fileItem: MsFileItem): void;
(e: 'linkFile'): void;
(e: 'update:fileList', fileList: MsFileItem[]): void;
}>();
const fileList = ref<MsFileItem[]>([]);
// const innerFileList = ref<MsFileItem[]>([]);
const innerFileList = useVModel(props, 'fileList', emit);
function beforeUpload(file: File) {
emit('upload', file);
}
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
innerFileList.value = _fileList;
emit('change', _fileList, fileItem);
}

View File

@ -55,9 +55,13 @@
<a-radio value="commonScript">{{ t('project.commonScript.commonScript') }}</a-radio>
<a-radio value="executionResult">{{ t('project.commonScript.executionResult') }}</a-radio>
</a-radio-group>
<a-button type="outline" :loading="loading" @click="testScript">{{
t('project.commonScript.scriptTest')
}}</a-button>
<a-button
v-permission="['PROJECT_CUSTOM_FUNCTION:READ+EXECUTE']"
type="outline"
:loading="loading"
@click="testScript"
>{{ t('project.commonScript.scriptTest') }}</a-button
>
</div>
<ScriptDefined
v-model:language="form.type"

View File

@ -13,6 +13,7 @@
import useUser from '@/hooks/useUser';
import { BOTTOM_MENU_LIST } from '@/router/constants';
import { useAppStore, useUserStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import { openWindow, regexUrl } from '@/utils';
import { listenerRouteChange } from '@/utils/route-listener';
@ -138,16 +139,6 @@
}
}
onBeforeMount(async () => {
try {
const res = await getOrgOptions();
originOrgList.value = res || [];
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
});
const isActiveSwitchOrg = ref(false);
const personalMenus = [
{
@ -181,6 +172,21 @@
},
];
const licenseStore = useLicenseStore();
onBeforeMount(async () => {
if (!licenseStore.hasLicense()) {
personalMenus.splice(1, 1);
return;
}
try {
const res = await getOrgOptions();
originOrgList.value = res || [];
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
});
watch(
() => personalMenusVisible.value,
(val) => {

View File

@ -1,16 +1,18 @@
<template>
<MsPopconfirm
type="error"
:title="props.title"
:sub-title-tip="props.subTitleTip"
:loading="props.loading"
:visible="currentVisible"
:ok-text="props.okText"
@confirm="handleOk"
@cancel="handleCancel"
>
<MsButton @click="showPopover">{{ t(props.removeText) }}</MsButton>
</MsPopconfirm>
<span>
<MsPopconfirm
type="error"
:title="props.title"
:sub-title-tip="props.subTitleTip"
:loading="props.loading"
:visible="currentVisible"
:ok-text="props.okText"
@confirm="handleOk"
@cancel="handleCancel"
>
<MsButton @click="showPopover">{{ t(props.removeText) }}</MsButton>
</MsPopconfirm>
</span>
</template>
<script setup lang="ts">

View File

@ -22,7 +22,7 @@ const CaseManagement: AppRouteRecordRaw = {
component: () => import('@/views/case-management/caseManagementFeature/index.vue'),
meta: {
locale: 'menu.caseManagement.featureCase',
roles: ['*'],
roles: ['FUNCTIONAL_CASE:READ'],
isTopMenu: true,
},
},

View File

@ -23,7 +23,7 @@ const ProjectManagement: AppRouteRecordRaw = {
redirect: '/project-management/permission/basicInfo',
meta: {
locale: 'menu.projectManagement.projectPermission',
roles: ['*'],
roles: ['PROJECT_USER:READ'],
isTopMenu: true,
},
children: [
@ -34,7 +34,7 @@ const ProjectManagement: AppRouteRecordRaw = {
component: () => import('@/views/project-management/projectAndPermission/basicInfos/index.vue'),
meta: {
locale: 'project.permission.basicInfo',
roles: ['*'],
roles: ['SYSTEM_PARAMETER_SETTING_BASE:READ'],
},
},
// 菜单管理
@ -86,7 +86,7 @@ const ProjectManagement: AppRouteRecordRaw = {
component: () => import('@/views/project-management/template/index.vue'),
meta: {
locale: 'menu.projectManagement.templateManager',
roles: ['*'],
roles: ['PROJECT_TEMPLATE:READ'],
isTopMenu: true,
},
},
@ -227,7 +227,7 @@ const ProjectManagement: AppRouteRecordRaw = {
component: () => import('@/views/project-management/commonScript/index.vue'),
meta: {
locale: 'menu.projectManagement.commonScript',
roles: ['*'],
roles: ['PROJECT_CUSTOM_FUNCTION:READ'],
isTopMenu: true,
},
},

View File

@ -1,8 +1,12 @@
// import useLicenseStore from '@/store/modules/setting/license';
import { SettingRouteEnum } from '@/enums/routeEnum';
import { DEFAULT_LAYOUT } from '../base';
import type { AppRouteRecordRaw } from '../types';
// const licenseStore = useLicenseStore();
const Setting: AppRouteRecordRaw = {
path: '/setting',
name: SettingRouteEnum.SETTING,
@ -101,7 +105,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/system/authorizedManagement/index.vue'),
meta: {
locale: 'menu.settings.system.authorizedManagement',
roles: ['*'],
roles: ['SYSTEM_AUTH:READ'],
isTopMenu: true,
},
},
@ -121,7 +125,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/system/pluginManager/index.vue'),
meta: {
locale: 'menu.settings.system.pluginManager',
roles: ['*'],
roles: ['SYSTEM_PLUGIN:READ'],
isTopMenu: true,
},
},
@ -144,7 +148,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/organization/member/index.vue'),
meta: {
locale: 'menu.settings.organization.member',
roles: ['*'],
roles: ['ORGANIZATION_MEMBER:READ'],
isTopMenu: true,
},
},
@ -174,7 +178,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/organization/serviceIntegration/index.vue'),
meta: {
locale: 'menu.settings.organization.serviceIntegration',
roles: ['*'],
roles: ['SYSTEM_SERVICE_INTEGRATION:READ'],
isTopMenu: true,
},
},
@ -184,7 +188,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/organization/template/index.vue'),
meta: {
locale: 'menu.settings.organization.template',
roles: ['*'],
roles: ['ORGANIZATION_TEMPLATE:READ'],
isTopMenu: true,
},
},

View File

@ -22,7 +22,7 @@ const TestPlan: AppRouteRecordRaw = {
component: () => import('@/views/test-plan/testPlan/index.vue'),
meta: {
locale: 'menu.testPlan',
roles: ['*'],
roles: ['PROJECT_TEST_PLAN:READ'],
isTopMenu: true,
},
},

View File

@ -9,7 +9,6 @@ const useFeatureCaseStore = defineStore('featureCase', {
persist: true,
state: (): {
moduleId: string[]; // 当前选中模块
allModuleId: string[]; // 所有模块
caseTree: ModuleTreeNode[]; // 用例树
modulesCount: Record<string, any>; // 用例树模块数量
recycleModulesCount: Record<string, any>; // 回收站模块数量
@ -17,7 +16,6 @@ const useFeatureCaseStore = defineStore('featureCase', {
tabSettingList: TabItemType[]; // 详情tab
} => ({
moduleId: [],
allModuleId: [],
caseTree: [],
modulesCount: {},
recycleModulesCount: {},
@ -26,10 +24,11 @@ const useFeatureCaseStore = defineStore('featureCase', {
}),
actions: {
// 设置选择moduleId
setModuleId(currentModuleId: string[], offspringIds: string[]) {
this.moduleId = currentModuleId;
if (offspringIds.length > 0) {
this.allModuleId = offspringIds;
setModuleId(currentModuleId: string[]) {
if (['all', 'recycle'].includes(currentModuleId[0])) {
this.moduleId = ['root'];
} else {
this.moduleId = currentModuleId;
}
},
// 设置用例树

View File

@ -24,7 +24,8 @@ const useLicenseStore = defineStore('license', {
if (!result || !result.status || !result.license || !result.license.count) {
return;
}
this.setLicenseStatus(result.status);
// this.setLicenseStatus(result.status);
this.setLicenseStatus('fail');
} catch (error) {
console.log(error);
}

View File

@ -136,7 +136,7 @@
<span class="w-[calc(100%-36%)]">
<a-tree-select
v-model="detailInfo.moduleId"
:data="featureCaseStore.caseTree"
:data="caseTree"
class="w-full"
:allow-search="true"
:field-names="{
@ -179,6 +179,7 @@
</div>
<inputComment
v-model:content="content"
v-permission="['FUNCTIONAL_CASE:READ+COMMENT']"
:is-active="isActive"
is-show-avatar
is-use-bottom
@ -223,15 +224,17 @@
deleteCaseRequest,
followerCaseRequest,
getCaseDetail,
getCaseModuleTree,
} from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useUserStore from '@/store/modules/user';
import { characterLimit, findNodeByKey } from '@/utils';
import { characterLimit } from '@/utils';
import type { CustomAttributes, DetailCase, TabItemType } from '@/models/caseManagement/featureCase';
import { ModuleTreeNode } from '@/models/projectManagement/file';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { getCaseLevels } from './utils';
@ -310,20 +313,26 @@
reviewStatus: 'UN_REVIEWED',
};
const caseTree = ref<ModuleTreeNode[]>([]);
async function getCaseTree() {
try {
caseTree.value = await getCaseModuleTree({ projectId: currentProjectId.value });
} catch (error) {
console.log(error);
}
}
const detailInfo = ref<DetailCase>({ ...initDetail });
const customFields = ref<CustomAttributes[]>([]);
const caseLevels = ref<CaseLevel>('P0');
function loadedCase(detail: DetailCase) {
getCaseTree();
detailInfo.value = { ...detail };
customFields.value = detailInfo.value.customFields;
caseLevels.value = getCaseLevels(customFields.value) as CaseLevel;
}
const moduleName = computed(() => {
return findNodeByKey<Record<string, any>>(featureCaseStore.caseTree, detailInfo.value?.moduleId as string, 'id')
?.name;
});
const editLoading = ref<boolean>(false);
function updateSuccess() {

View File

@ -82,26 +82,32 @@
</template> -->
<!-- 渲染自定义字段结束 -->
<template #operation="{ record }">
<MsButton @click="operateCase(record, 'edit')">{{ t('common.edit') }}</MsButton>
<a-divider direction="vertical" :margin="8"></a-divider>
<MsButton @click="operateCase(record, 'copy')">{{ t('caseManagement.featureCase.copy') }}</MsButton>
<a-divider direction="vertical" :margin="8"></a-divider>
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" @click="operateCase(record, 'edit')">{{
t('common.edit')
}}</MsButton>
<a-divider v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" direction="vertical" :margin="8"></a-divider>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" @click="operateCase(record, 'copy')">{{
t('caseManagement.featureCase.copy')
}}</MsButton>
<a-divider v-permission="['FUNCTIONAL_CASE:READ+ADD']" direction="vertical" :margin="8"></a-divider>
<span v-permission="['FUNCTIONAL_CASE:READ+DELETE']">
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
</span>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex items-center justify-center p-[8px] text-[var(--color-text-4)]">
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton class="ml-[8px]" @click="createCase">
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="ml-[8px]" @click="createCase">
{{ t('caseManagement.featureCase.creatingCase') }}
</MsButton>
{{ t('caseManagement.featureCase.or') }}
<MsButton class="ml-[8px]" @click="emit('import', 'Excel')">
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="ml-[8px]" @click="emit('import', 'Excel')">
{{ t('caseManagement.featureCase.importExcel') }}
</MsButton>
<MsButton class="ml-[4px]" @click="emit('import', 'Xmind')">
<!-- <MsButton class="ml-[4px]" @click="emit('import', 'Xmind')">
{{ t('caseManagement.featureCase.importXmind') }}
</MsButton>
</MsButton> -->
</div>
</template>
</ms-base-table>
@ -1098,7 +1104,7 @@
initData();
};
onMounted(() => {
onBeforeMount(() => {
if (route.query.id) {
showCaseDetail(route.query.id as string, 0);
}
@ -1116,10 +1122,12 @@
watch(
() => props.activeFolder,
() => {
(val) => {
keyword.value = '';
initData();
resetSelector();
if (props.activeFolder !== 'recycle' && val) {
initData();
resetSelector();
}
}
);
</script>

View File

@ -77,7 +77,12 @@
:upload-image="handleUploadImage"
/>
</a-form-item>
<AddAttachment @change="handleChange" @link-file="associatedFile" @upload="beforeUpload" />
<AddAttachment
v-model:file-list="fileList"
@change="handleChange"
@link-file="associatedFile"
@upload="beforeUpload"
/>
</a-form>
<!-- 文件列表开始 -->
<div class="w-[90%]">
@ -260,6 +265,7 @@
getAssociatedFileListUrl,
getCaseDefaultFields,
getCaseDetail,
getCaseModuleTree,
previewFile,
transferFileRequest,
updateFile,
@ -311,14 +317,6 @@
const featureCaseStore = useFeatureCaseStore();
const modelId = computed(() => featureCaseStore.moduleId[0]);
const caseTree = computed(() => {
return mapTree<ModuleTreeNode>(featureCaseStore.caseTree, (e) => {
return {
...e,
draggable: false,
};
});
});
const initForm: DetailCase = {
id: '',
@ -560,7 +558,6 @@
async function getCaseInfo() {
try {
isLoading.value = true;
// await getAllCaseFields();
const detailResult: DetailCase = await getCaseDetail(props.caseId);
const fileIds = (detailResult.attachments || []).map((item: any) => item.id);
if (fileIds.length) {
@ -574,12 +571,23 @@
}
}
watchEffect(() => {
const caseTree = ref<ModuleTreeNode[]>([]);
async function initSelectTree() {
try {
caseTree.value = await getCaseModuleTree({ projectId: currentProjectId.value });
} catch (error) {
console.log(error);
}
}
onMounted(() => {
if (route.params.mode === 'edit' || route.params.mode === 'copy') {
getCaseInfo();
} else {
initDefaultFields();
}
initSelectTree();
});
//
function getFilesParams() {
@ -619,7 +627,7 @@
if (val) {
params.value.request = { ...form.value };
emit('update:formModeValue', params.value);
featureCaseStore.setModuleId([form.value.moduleId], []);
featureCaseStore.setModuleId([form.value.moduleId]);
}
}
},

View File

@ -96,6 +96,8 @@
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
import { ModuleTreeNode } from '@/models/projectManagement/file';
const featureCaseStore = useFeatureCaseStore();
const { t } = useI18n();
const { openModal } = useModal();
const appStore = useAppStore();
@ -141,6 +143,7 @@
() => props.selectedKeys,
(val) => {
selectedNodeKeys.value = val || [];
featureCaseStore.setModuleId(val as string[]);
}
);
@ -150,7 +153,6 @@
emits('update:selectedKeys', val);
}
);
const featureCaseStore = useFeatureCaseStore();
/**
* 初始化模块树
* @param isSetDefaultKey 是否设置第一个节点为选中节点
@ -169,6 +171,7 @@
};
});
featureCaseStore.setModulesTree(caseTree.value);
featureCaseStore.setModuleId(['all']);
if (isSetDefaultKey) {
selectedNodeKeys.value = [caseTree.value[0].id];
}

View File

@ -43,11 +43,12 @@
:auto-upload="false"
:disabled="confirmLoading"
></MsUpload>
<a-form-item field="post" :label="t('caseManagement.featureCase.selectVersion')">
<!-- 版本暂时不上 -->
<!-- <a-form-item field="post" :label="t('caseManagement.featureCase.selectVersion')">
<a-select class="max-w-[240px]" :placeholder="t('caseManagement.featureCase.defaultSelectNewVersion')">
<a-option v-for="item of versionOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select>
</a-form-item>
</a-form-item> -->
</div>
<template #footer>
<div class="flex items-center justify-between">
@ -69,9 +70,14 @@
></a-checkbox>
<div>
<a-button type="secondary" @click="handleCancel">{{ t('system.plugin.pluginCancel') }}</a-button>
<a-button class="ml-3" type="primary" :disabled="isDisabled" :loading="confirmLoading" @click="saveConfirm">{{
t('caseManagement.featureCase.checkTemplate')
}}</a-button>
<a-button
class="ml-3"
type="primary"
:disabled="isDisabled"
:loading="props.confirmLoading"
@click="saveConfirm"
>{{ t('caseManagement.featureCase.checkTemplate') }}</a-button
>
</div>
</div>
</template>
@ -95,6 +101,7 @@
const props = defineProps<{
visible: boolean;
validateType: 'Excel' | 'Xmind';
confirmLoading: boolean;
}>();
const emit = defineEmits<{
@ -112,19 +119,17 @@
set: (val) => emit('update:visible', val),
});
const confirmLoading = ref<boolean>(false);
const handleCancel = () => {
fileList.value = [];
emit('close');
};
const versionOptions = ref([
{
id: '1001',
name: 'V1.0',
},
]);
// const versionOptions = ref([
// {
// id: '1001',
// name: 'V1.0',
// },
// ]);
const isRecover = ref<boolean>(false);

View File

@ -119,19 +119,18 @@
import { ValidateInfo } from '@/models/caseManagement/featureCase';
import type { FileItem } from '@arco-design/web-vue';
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
validateType: 'Excel' | 'Xmind';
validateInfo: ValidateInfo;
importLoading: boolean;
}>();
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void;
(e: 'save', files: FileItem[]): void;
(e: 'close'): void;
(e: 'save'): void;
}>();
const validateResultModal = computed({
@ -175,7 +174,9 @@
}
//
function confirmImport() {}
function confirmImport() {
emit('save');
}
watchEffect(() => {
validateResultInfo.value = { ...props.validateInfo };

View File

@ -124,10 +124,15 @@
</div>
</template> -->
<template #operation="{ record }">
<MsButton @click="recoverCase(record.id)">{{ t('caseManagement.featureCase.batchRecover') }}</MsButton>
<MsButton class="!mr-0" @click="handleBatchCleanOut(record)">{{
t('caseManagement.featureCase.batchCleanOut')
<MsButton v-permission="['FUNCTIONAL_CASE:READ+DELETE']" @click="recoverCase(record.id)">{{
t('caseManagement.featureCase.batchRecover')
}}</MsButton>
<MsButton
v-permission="['FUNCTIONAL_CASE:READ+DELETE']"
class="!mr-0"
@click="handleBatchCleanOut(record)"
>{{ t('caseManagement.featureCase.batchCleanOut') }}</MsButton
>
</template>
</ms-base-table>
</div>
@ -810,10 +815,6 @@
}
}
function getCaseLevel(record: CaseManagementTable): CaseLevel {
const caseLevelItem = record.customFields.find((it: any) => it.fieldName === '用例等级');
return caseLevelItem?.options.find((it: any) => it.value === caseLevelItem.defaultValue).text;
}
onMounted(() => {
getDefaultFields();
initFilter();

View File

@ -53,9 +53,11 @@
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useLicenseStore from '@/store/modules/setting/license';
import type { TabItemType } from '@/models/caseManagement/featureCase';
const licenseStore = useLicenseStore();
const { t } = useI18n();
const featureCaseStore = useFeatureCaseStore();
@ -98,6 +100,19 @@
let buggerTab: TabItemType[] = [];
let testPlanTab: TabItemType[] = [];
function getTabList() {
if (licenseStore.hasLicense()) {
return [
{
key: 'changeHistory',
title: 'caseManagement.featureCase.changeHistory',
enable: true,
},
];
}
return [];
}
const tabDefaultSettingList = ref<TabItemType[]>([
{
key: 'case',
@ -119,11 +134,7 @@
title: 'caseManagement.featureCase.comments',
enable: true,
},
{
key: 'changeHistory',
title: 'caseManagement.featureCase.changeHistory',
enable: true,
},
...getTabList(),
]);
async function getTabModule() {
buggerTab = [];

View File

@ -49,7 +49,7 @@
<MsButton @click="cancelLink(record.id)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex items-center justify-center">
<div class="flex w-full items-center justify-center">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton class="ml-[8px]" @click="linkDefect">
{{ t('caseManagement.featureCase.linkDefect') }}
@ -77,7 +77,7 @@
<MsButton @click="cancelLink(record.id)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex items-center justify-center">
<div class="flex w-full items-center justify-center">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton class="ml-[8px]" @click="createDefect">
{{ t('caseManagement.featureCase.createDefect') }}

View File

@ -26,6 +26,22 @@
<template #operation="{ record }">
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex w-full items-center justify-center">
{{ t('caseManagement.caseReview.tableNoData') }}
<a-dropdown @select="handleSelect">
<MsButton class="ml-[8px]">
{{ t('caseManagement.featureCase.linkCase') }}
</MsButton>
<template #content>
<a-doption v-for="item of caseTypeOptions" :key="item.value" :value="item.value">{{
t(item.label)
}}</a-doption>
</template>
</a-dropdown>
</div>
</template>
</ms-base-table>
<MsCaseAssociate
v-model:visible="innerVisible"

View File

@ -15,7 +15,7 @@
}}</MsButton>
</template>
<template v-if="(props.funParams.keyword || '').trim() === ''" #empty>
<div class="flex items-center justify-center">
<div class="flex w-full w-full items-center justify-center">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton class="ml-[8px]" @click="emit('create')">
{{ t('caseManagement.featureCase.addDemand') }}

View File

@ -84,7 +84,15 @@
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #caseLevel="{ record }">
<caseLevel :case-level="getCaseLevel(record)" />
<caseLevel :case-level="getCaseLevels(record.customFields)" />
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="ml-[8px]" @click="createCase">
{{ t('caseManagement.featureCase.creatingCase') }}
</MsButton>
</div>
</template>
</ms-base-table>
<div class="footer">
@ -114,8 +122,10 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
@ -139,6 +149,9 @@
import type { CaseManagementTable, CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
import type { TableQueryParams } from '@/models/common';
import { ModuleTreeNode } from '@/models/projectManagement/file';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { getCaseLevels } from '../../utils';
const appStore = useAppStore();
const currentProjectId = computed(() => appStore.currentProjectId);
@ -451,6 +464,13 @@
}
);
const router = useRouter();
function createCase() {
router.push({
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
});
}
onMounted(() => {
resetSelector();
});

View File

@ -39,7 +39,7 @@
/>
</template>
<template v-if="(keyword || '').trim() === ''" #empty>
<div class="flex items-center justify-center">
<div class="flex w-full items-center justify-center">
{{ t('caseManagement.caseReview.tableNoData') }}
<MsButton class="ml-[8px]" @click="addCase">
{{

View File

@ -9,7 +9,13 @@
asterisk-position="end"
>
<span class="absolute right-[6px] top-0">
<a-button v-if="props.allowEdit" type="text" class="px-0" @click="prepositionEdit">
<a-button
v-if="props.allowEdit"
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
type="text"
class="px-0"
@click="prepositionEdit"
>
<MsIcon type="icon-icon_edit_outlined" class="mr-1 font-[16px] text-[rgb(var(--primary-5))]" />{{
t('caseManagement.featureCase.contentEdit')
}}</a-button
@ -99,7 +105,7 @@
<div v-if="props.allowEdit" class="flex flex-col">
<div class="mb-1">
<a-dropdown position="tr" trigger="hover">
<a-button type="outline">
<a-button v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" type="outline">
<template #icon> <icon-plus class="text-[14px]" /> </template
>{{ t('system.orgTemplate.addAttachment') }}</a-button
>
@ -261,7 +267,7 @@
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { downloadByteFile, getGenerateId } from '@/utils';
import { downloadByteFile, encrypted, getGenerateId, sleep } from '@/utils';
import { scrollIntoView } from '@/utils/dom';
import type { AssociatedList, DetailCase, StepList } from '@/models/caseManagement/featureCase';
@ -479,10 +485,6 @@
}
const fileListRef = ref<InstanceType<typeof MsFileList>>();
async function startUpload() {
await fileListRef.value?.startUpload();
emit('updateSuccess');
}
function beforeUpload(file: File) {
const _maxSize = 50 * 1024 * 1024;
@ -600,16 +602,18 @@
}
);
async function startUpload() {
await sleep(300);
await fileListRef.value?.startUpload();
emit('updateSuccess');
}
//
watch(
() => fileList.value,
(val) => {
if (val) {
if (val.filter((item) => item.status === 'init').length) {
setTimeout(() => {
startUpload();
}, 30);
}
async (val) => {
const isNewFiles = val.filter((item) => item.status === 'init').length;
if (val && isNewFiles) {
startUpload();
}
}
);

View File

@ -1,15 +1,15 @@
<template>
<div class="rounded-2xl bg-white">
<div class="p-[24px] pb-[16px]">
<a-button type="primary" @click="caseDetail">
<a-button v-permission="['FUNCTIONAL_CASE:READ+ADD']" type="primary" @click="caseDetail">
{{ t('caseManagement.featureCase.creatingCase') }}
</a-button>
<a-button class="mx-3" type="outline" @click="importCase('Excel')">
<a-button v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="mx-3" type="outline" @click="importCase('Excel')">
{{ t('caseManagement.featureCase.importExcel') }}
</a-button>
<a-button type="outline" @click="importCase('Xmind')">
<!-- <a-button type="outline" @click="importCase('Xmind')">
{{ t('caseManagement.featureCase.importXmind') }}
</a-button>
</a-button> -->
</div>
<a-divider class="!my-0" />
<div class="pageWrap">
@ -98,6 +98,7 @@
<ExportExcelModal
v-model:visible="showExcelModal"
:validate-type="validateType"
:confirm-loading="validateLoading"
@save="validateTemplate"
@close="showExcelModal = false"
/>
@ -112,7 +113,9 @@
v-model:visible="validateResultModal"
:validate-type="validateType"
:validate-info="validateInfo"
:import-loading="importLoading"
@close="closeHandler"
@save="conFirmImport"
/>
</template>
@ -133,7 +136,7 @@
import ValidateModal from './components/export/validateModal.vue';
import ValidateResult from './components/export/validateResult.vue';
import { createCaseModuleTree, importExcelChecked } from '@/api/modules/case-management/featureCase';
import { createCaseModuleTree, importExcelCase, importExcelChecked } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
@ -193,7 +196,7 @@
[activeFolder.value] = keys;
activeCaseType.value = 'module';
offspringIds.value = [..._offspringIds];
featureCaseStore.setModuleId(keys, offspringIds.value);
featureCaseStore.setModuleId(keys);
}
const confirmLoading = ref(false);
@ -322,8 +325,15 @@
}, 100);
}
const fileList = ref<FileItem[]>([]);
const isCover = ref<boolean>(false);
const validateLoading = ref<boolean>(false);
//
async function validateTemplate(files: FileItem[], cover: boolean) {
fileList.value = files;
isCover.value = cover;
validateLoading.value = true;
try {
start();
validateModal.value = true;
@ -339,6 +349,8 @@
}
} catch (error) {
console.log(error);
} finally {
validateLoading.value = false;
}
}
@ -353,6 +365,29 @@
function closeHandler() {
showExcelModal.value = false;
validateResultModal.value = false;
caseTreeRef.value.initModules();
}
const importLoading = ref<boolean>(false);
//
async function conFirmImport() {
importLoading.value = true;
try {
const params = {
projectId: appStore.currentProjectId,
versionId: '',
cover: isCover.value,
};
await importExcelCase({ request: params, fileList: fileList.value.map((item: any) => item.file) });
Message.success(t('caseManagement.featureCase.importSuccess'));
validateResultModal.value = false;
showExcelModal.value = false;
caseTreeRef.value.initModules();
} catch (error) {
console.log(error);
} finally {
importLoading.value = false;
}
}
onMounted(() => {

View File

@ -3,6 +3,7 @@ export default {
'caseManagement.featureCase.importCase': 'Import Case',
'caseManagement.featureCase.importExcel': 'Import Excel',
'caseManagement.featureCase.importXmind': 'Import Xmind',
'caseManagement.featureCase.importSuccess': 'Import successfully',
'caseManagement.featureCase.publicCase': 'Public of Cases',
'caseManagement.featureCase.allCase': 'All of Cases',
'caseManagement.featureCase.searchTip': 'Please enter a group name',

View File

@ -3,6 +3,7 @@ export default {
'caseManagement.featureCase.importCase': '导入用例',
'caseManagement.featureCase.importExcel': 'Excel导入',
'caseManagement.featureCase.importXmind': 'Xmind导入',
'caseManagement.featureCase.importSuccess': '导入成功',
'caseManagement.featureCase.publicCase': '公共用例库',
'caseManagement.featureCase.allCase': '全部用例',
'caseManagement.featureCase.searchTip': '请输入分组名称',

View File

@ -13,7 +13,7 @@
<a-form-item class="login-form-item" field="radio" hide-label>
<a-radio-group v-model="userInfo.authenticate" type="button">
<a-radio value="LOCAL">普通登陆</a-radio>
<a-radio value="LDAP">LDAP</a-radio>
<a-radio v-xpack value="LDAP">LDAP</a-radio>
<a-radio value="OAuth2">OAuth2 测试</a-radio>
<a-radio value="OIDC 90">OIDC 90</a-radio>
</a-radio-group>

View File

@ -1,7 +1,9 @@
<template>
<MsCard simple>
<div class="mb-4 flex items-center justify-between">
<a-button type="outline" @click="addCommonScript"> {{ t('project.commonScript.addPublicScript') }} </a-button>
<a-button v-permission="['PROJECT_CUSTOM_FUNCTION:READ+ADD']" type="outline" @click="addCommonScript">
{{ t('project.commonScript.addPublicScript') }}
</a-button>
<a-input-search
v-model:model-value="keyword"
:placeholder="t('project.commonScript.searchByNameAndId')"
@ -43,11 +45,12 @@
<MsTag v-else>{{ t('project.commonScript.draft') }}</MsTag>
</template>
<template #operation="{ record }">
<MsButton status="primary" @click="editHandler(record)">
<MsButton v-permission="['PROJECT_CUSTOM_FUNCTION:READ+UPDATE']" status="primary" @click="editHandler(record)">
{{ t('common.edit') }}
</MsButton>
<MsTableMoreAction
v-if="!record.internal"
v-permission="['PROJECT_CUSTOM_FUNCTION:READ+DELETE']"
:list="actions"
@select="(item:ActionsItem) => handleMoreActionSelect(item,record)"
/></template>

View File

@ -4,9 +4,13 @@
</div>
<div class="wrapper mb-6 flex justify-between">
<span class="font-medium text-[var(--color-text-000)]">{{ t('project.basicInfo.basicInfo') }}</span>
<a-button v-if="!projectDetail?.deleted" type="outline" @click="editHandler">{{
t('project.basicInfo.edit')
}}</a-button>
<a-button
v-if="!projectDetail?.deleted"
v-permission="['SYSTEM_PARAMETER_SETTING_BASE:READ+UPDATE']"
type="outline"
@click="editHandler"
>{{ t('project.basicInfo.edit') }}</a-button
>
</div>
<div class="project-info mb-6 h-[112px] bg-white p-1">
<div class="inner-wrapper rounded-md p-4">

View File

@ -24,6 +24,7 @@
import MsMenuPanel from '@/components/pure/ms-menu-panel/index.vue';
import { useI18n } from '@/hooks/useI18n';
import useLicenseStore from '@/store/modules/setting/license';
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
@ -31,6 +32,21 @@
const router = useRouter();
const route = useRoute();
const licenseStore = useLicenseStore();
function getProjectVersion() {
if (licenseStore.hasLicense()) {
return [
{
key: 'projectVersion',
title: t('project.permission.projectVersion'),
level: 2,
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_VERSION,
},
];
}
return [];
}
const menuList = ref([
{
key: 'project',
@ -50,12 +66,7 @@
level: 2,
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_MENU_MANAGEMENT,
},
{
key: 'projectVersion',
title: t('project.permission.projectVersion'),
level: 2,
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_VERSION,
},
...getProjectVersion(),
{
key: 'memberPermission',
title: t('project.permission.memberPermission'),

View File

@ -1,7 +1,9 @@
<template>
<div class="mb-4 grid grid-cols-4 gap-2">
<div class="col-span-2"
><a-button class="mr-3" type="primary" @click="addMember">{{ t('project.member.addMember') }}</a-button></div
><a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">{{
t('project.member.addMember')
}}</a-button></div
>
<div>
<a-select v-model="roleIds" @change="changeSelect">
@ -60,6 +62,7 @@
</template>
<template #operation="{ record }">
<MsRemoveButton
v-permission="['PROJECT_USER:READ+DELETE']"
position="br"
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })"
:sub-title-tip="t('project.member.subTitle')"
@ -180,10 +183,12 @@
{
label: 'project.member.batchActionAddUserGroup',
eventTag: 'batchAddUserGroup',
permission: ['PROJECT_USER:READ+ADD'],
},
{
label: 'project.member.batchActionRemove',
eventTag: 'batchActionRemove',
permission: ['PROJECT_USER:READ+ADD'],
},
],
};

View File

@ -2,7 +2,13 @@
<MsCard has-breadcrumb simple>
<div class="mb-4 flex items-center justify-between">
<span v-if="isEnableOrdTemplate" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
<a-button v-else type="primary" :disabled="false" @click="createTemplate">
<a-button
v-else
v-permission="['PROJECT_TEMPLATE:READ+ADD']"
type="primary"
:disabled="false"
@click="createTemplate"
>
{{ t('system.orgTemplate.createTemplate') }}
</a-button>
<a-input-search
@ -36,12 +42,22 @@
</div>
</template>
<template #operation="{ record }">
<div class="flex flex-row flex-nowrap">
<MsButton @click="editTemplate(record.id)">{{ t('system.orgTemplate.edit') }}</MsButton>
<MsButton class="!mr-0" @click="copyTemplate(record.id)">{{ t('system.orgTemplate.copy') }}</MsButton>
<a-divider v-if="!record.internal" direction="vertical" />
<div class="flex flex-row flex-nowrap items-center">
<MsButton v-permission="['PROJECT_TEMPLATE:READ+UPDATE']" @click="editTemplate(record.id)">{{
t('system.orgTemplate.edit')
}}</MsButton>
<MsButton v-permission="['PROJECT_TEMPLATE:READ+ADD']" class="!mr-0" @click="copyTemplate(record.id)">{{
t('system.orgTemplate.copy')
}}</MsButton>
<a-divider
v-if="!record.internal"
v-permission="['PROJECT_TEMPLATE:READ+ADD']"
class="h-[16px]"
direction="vertical"
/>
<MsTableMoreAction
v-if="!record.internal"
v-permission="['PROJECT_TEMPLATE:READ+DELETE']"
:list="moreActions"
@select="(item) => handleMoreActionSelect(item, record)"
/>

View File

@ -2,9 +2,13 @@
<MsCard simple>
<div class="mb-4 flex items-center justify-between">
<div>
<a-button class="mr-3" type="primary" @click="addOrEditMember('add')">{{
t('organization.member.addMember')
}}</a-button>
<a-button
v-permission="['ORGANIZATION_MEMBER:READ+ADD']"
class="mr-3"
type="primary"
@click="addOrEditMember('add')"
>{{ t('organization.member.addMember') }}</a-button
>
</div>
<a-input-search
v-model="keyword"
@ -78,8 +82,11 @@
</div>
</template>
<template #action="{ record }">
<MsButton @click="addOrEditMember('edit', record)">{{ t('organization.member.edit') }}</MsButton>
<MsButton v-permission="['ORGANIZATION_MEMBER:READ+UPDATE']" @click="addOrEditMember('edit', record)">{{
t('organization.member.edit')
}}</MsButton>
<MsRemoveButton
v-permission="['ORGANIZATION_MEMBER:READ+DELETE']"
position="br"
:title="t('organization.member.deleteMemberTip', { name: characterLimit(record.name) })"
:sub-title-tip="t('organization.member.subTitle')"
@ -213,10 +220,12 @@
{
label: 'organization.member.batchActionAddProject',
eventTag: 'batchAddProject',
permission: ['ORGANIZATION_MEMBER:READ+ADD'],
},
{
label: 'organization.member.batchActionAddUserGroup',
eventTag: 'batchAddUserGroup',
permission: ['ORGANIZATION_MEMBER:READ+ADD'],
},
],
};

View File

@ -67,11 +67,28 @@
@click="getValidateHandler(item)"
>{{ t('organization.service.testLink') }}</a-button
>
<a-button type="outline" class="arco-btn-outline--secondary" size="mini" @click="editHandler(item)">{{
t('organization.service.edit')
}}</a-button>
<a-button
v-if="item.config"
v-permission="['SYSTEM_SERVICE_INTEGRATION:READ+UPDATE']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
@click="editHandler(item)"
>{{ t('organization.service.edit') }}</a-button
>
<a-button
v-else
v-permission="['SYSTEM_SERVICE_INTEGRATION:READ+ADD']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
@click="editHandler(item)"
>{{ t('common.add') }}</a-button
>
<a-button
v-if="item.config"
v-permission="['SYSTEM_SERVICE_INTEGRATION:READ+RESET']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"

View File

@ -15,7 +15,7 @@
<span @click="fieldSetting">{{ t('system.orgTemplate.fieldSetting') }}</span>
<a-divider direction="vertical" />
</span>
<span class="operation hover:text-[rgb(var(--primary-5))]">
<span v-permission="['ORGANIZATION_TEMPLATE:READ']" class="operation hover:text-[rgb(var(--primary-5))]">
<span @click="templateManagement">{{ t('system.orgTemplate.TemplateManagement') }}</span>
<a-divider v-if="isEnableProject || props.cardItem.key === 'BUG'" direction="vertical" />
</span>
@ -23,7 +23,11 @@
<span @click="workflowSetup">{{ t('system.orgTemplate.workflowSetup') }}</span>
<a-divider v-if="isEnableProject && props.cardItem.key === 'BUG'" direction="vertical" />
</span>
<span v-if="isEnableProject" class="rounded p-[2px] hover:bg-[rgb(var(--primary-9))]">
<span
v-if="isEnableProject"
v-permission="['ORGANIZATION_TEMPLATE:READ+ENABLE']"
class="rounded p-[2px] hover:bg-[rgb(var(--primary-9))]"
>
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect"
/></span>
</div>

View File

@ -5,7 +5,13 @@
}}</a-alert>
<div class="mb-4 flex items-center justify-between">
<span v-if="isEnableOrdTemplate" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
<a-button v-else type="primary" :disabled="false" @click="createTemplate">
<a-button
v-else
v-permission="['ORGANIZATION_TEMPLATE:READ+ADD']"
type="primary"
:disabled="false"
@click="createTemplate"
>
{{ t('system.orgTemplate.createTemplate') }}
</a-button>
<a-input-search
@ -28,12 +34,22 @@
{{ record.enableThirdPart ? t('system.orgTemplate.yes') : t('system.orgTemplate.no') }}
</template>
<template #operation="{ record }">
<div class="flex flex-row flex-nowrap">
<MsButton @click="editTemplate(record.id)">{{ t('system.orgTemplate.edit') }}</MsButton>
<MsButton class="!mr-0" @click="copyTemplate(record.id)">{{ t('system.orgTemplate.copy') }}</MsButton>
<a-divider v-if="!record.internal" class="h-[12px]" direction="vertical" />
<div class="flex flex-row flex-nowrap items-center">
<MsButton v-permission="['ORGANIZATION_TEMPLATE:READ+UPDATE']" @click="editTemplate(record.id)">{{
t('system.orgTemplate.edit')
}}</MsButton>
<MsButton v-permission="['ORGANIZATION_TEMPLATE:READ+ADD']" class="!mr-0" @click="copyTemplate(record.id)">{{
t('system.orgTemplate.copy')
}}</MsButton>
<a-divider
v-if="!record.internal"
v-permission="['ORGANIZATION_TEMPLATE:READ+ADD']"
class="h-[12px]"
direction="vertical"
/>
<MsTableMoreAction
v-if="!record.internal"
v-permission="['ORGANIZATION_TEMPLATE:READ+DELETE']"
:list="moreActions"
@select="(item) => handleMoreActionSelect(item, record)"
/>

View File

@ -58,7 +58,7 @@
>
</li>
<li>
<MsButton class="font-medium" @click="authChecking">{{
<MsButton v-permission="['SYSTEM_AUTH:READ+UPDATE']" class="font-medium" @click="authChecking">{{
t('system.authorized.authorityChecking')
}}</MsButton>
</li>

View File

@ -207,6 +207,7 @@
import { getBaseInfo, getEmailInfo, saveBaseInfo, saveEmailInfo, testEmail } from '@/api/modules/setting/config';
import { useI18n } from '@/hooks/useI18n';
import useLicenseStore from '@/store/modules/setting/license';
import { desensitize } from '@/utils';
import { validateEmail } from '@/utils/validate';
@ -241,22 +242,33 @@
/**
* 初始化基础信息
*/
const licenseStore = useLicenseStore();
async function initBaseInfo() {
try {
baseloading.value = true;
const res = await getBaseInfo();
baseInfo.value = { ...res };
baseInfoForm.value = { ...res };
baseInfoDescs.value = [
{
label: t('system.config.pageUrl'),
value: res.url,
},
{
label: t('system.config.prometheus'),
value: res.prometheusHost,
},
];
if (licenseStore.hasLicense()) {
baseInfoDescs.value = [
{
label: t('system.config.pageUrl'),
value: res.url,
},
{
label: t('system.config.prometheus'),
value: res.prometheusHost,
},
];
} else {
baseInfoDescs.value = [
{
label: t('system.config.pageUrl'),
value: res.url,
},
];
}
} catch (error) {
console.log(error);
} finally {

View File

@ -20,6 +20,7 @@
import pageConfig from './components/pageConfig.vue';
import { useI18n } from '@/hooks/useI18n';
import useLicenseStore from '@/store/modules/setting/license';
const { t } = useI18n();
const route = useRoute();
@ -29,12 +30,12 @@
const isInitAuthConfig = ref(activeTab.value === 'authConfig');
const isInitMemoryCleanup = ref(activeTab.value === 'memoryCleanup');
const authConfigRef = ref<AuthConfigInstance | null>();
const tabList = [
const tabList = ref([
{ key: 'baseConfig', title: t('system.config.baseConfig') },
{ key: 'pageConfig', title: t('system.config.pageConfig') },
{ key: 'authConfig', title: t('system.config.authConfig') },
{ key: 'memoryCleanup', title: t('system.config.memoryCleanup') },
];
]);
watch(
() => activeTab.value,
@ -51,11 +52,15 @@
immediate: true,
}
);
const licenseStore = useLicenseStore();
onMounted(() => {
if (route.query.tab === 'authConfig' && route.query.id) {
authConfigRef.value?.openAuthDetail(route.query.id as string);
}
if (!licenseStore.hasLicense()) {
const excludes = ['baseConfig', 'memoryCleanup'];
tabList.value = tabList.value.filter((item: any) => excludes.includes(item.key));
}
});
</script>

View File

@ -3,7 +3,9 @@
<div>
<a-row class="grid-demo mb-4" :gutter="10">
<a-col :span="5">
<a-button class="mr-3" type="primary" @click="uploadPlugin">{{ t('system.plugin.uploadPlugin') }}</a-button>
<a-button v-permission="['SYSTEM_PLUGIN:READ+ADD']" class="mr-3" type="primary" @click="uploadPlugin">{{
t('system.plugin.uploadPlugin')
}}</a-button>
</a-col>
<a-col :span="5" :offset="9">
<a-select v-model="searchKeys.scene" @change="searchHandler">
@ -108,12 +110,18 @@
</template>
<template #cell="{ record }">
<div class="flex">
<MsButton @click="update(record)">{{ t('system.plugin.edit') }}</MsButton>
<MsButton v-permission="['SYSTEM_PLUGIN:READ+UPDATE']" @click="update(record)">{{
t('system.plugin.edit')
}}</MsButton>
<MsButton v-if="record.enable" @click="disableHandler(record)">{{
t('system.plugin.tableDisable')
}}</MsButton>
<MsButton v-else @click="enableHandler(record)">{{ t('system.plugin.tableEnable') }}</MsButton>
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
<MsTableMoreAction
v-permission="['SYSTEM_PLUGIN:READ+DELETE']"
:list="tableActions"
@select="handleSelect($event, record)"
></MsTableMoreAction>
</div>
</template>
</a-table-column>

View File

@ -136,7 +136,7 @@
<a-form-item v-if="isShowTypeItem" :label="t('system.resourcePool.type')" field="type" class="form-item">
<a-radio-group v-model:model-value="form.type" type="button" @change="changeResourceType">
<a-radio value="Node">Node</a-radio>
<a-radio value="Kubernetes">Kubernetes</a-radio>
<a-radio v-xpack value="Kubernetes">Kubernetes</a-radio>
</a-radio-group>
</a-form-item>
<template v-if="isShowNodeResources">
@ -356,6 +356,7 @@
import { useI18n } from '@/hooks/useI18n';
import useVisit from '@/hooks/useVisit';
import useAppStore from '@/store/modules/app';
import useLicenseStore from '@/store/modules/setting/license';
import { downloadStringFile, sleep } from '@/utils';
import { scrollIntoView } from '@/utils/dom';
@ -363,6 +364,8 @@
import { getYaml, job, YamlType } from './template';
const licenseStore = useLicenseStore();
const isXpack = computed(() => licenseStore.hasLicense());
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
@ -399,7 +402,7 @@
const form = ref({ ...defaultForm });
const formRef = ref<FormInstance | null>(null);
const orgOptions = ref<SelectOptionData>([]);
const useList = [
const useList = ref([
{
label: 'system.resourcePool.usePerformance',
value: 'performance',
@ -412,11 +415,14 @@
label: 'system.resourcePool.useUI',
value: 'UI',
},
];
]);
const defaultGrid = 'http://selenium-hub:4444';
onBeforeMount(async () => {
orgOptions.value = await getSystemOrgOption();
if (!isXpack.value) {
useList.value = useList.value.filter((item) => item.value === 'API');
}
});
async function initPoolInfo() {

View File

@ -1,7 +1,7 @@
<template>
<MsCard :loading="loading" simple>
<div class="mb-4 flex items-center justify-between">
<a-button type="primary" @click="addPool">
<a-button v-xpack type="primary" @click="addPool">
{{ t('system.resourcePool.createPool') }}
</a-button>
<a-input-search
@ -19,10 +19,10 @@
</template>
<template #action="{ record }">
<MsButton @click="editPool(record)">{{ t('system.resourcePool.editPool') }}</MsButton>
<MsButton v-if="record.enable" @click="disabledPool(record)">
<MsButton v-if="record.enable" v-xpack @click="disabledPool(record)">
{{ t('system.resourcePool.tableDisable') }}
</MsButton>
<MsButton v-else @click="enablePool(record)">{{ t('system.resourcePool.tableEnable') }}</MsButton>
<MsButton v-else v-xpack @click="enablePool(record)">{{ t('system.resourcePool.tableEnable') }}</MsButton>
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
</template>
</ms-base-table>
@ -83,7 +83,6 @@
const { t } = useI18n();
const router = useRouter();
const route = useRoute();
const columns: MsTableColumn = [
{
title: 'system.resourcePool.tableColumnName',

View File

@ -66,9 +66,13 @@
<template #operation="{ record }">
<MsButton>{{ t('testPlan.testPlanIndex.execution') }}</MsButton>
<a-divider direction="vertical" :margin="8"></a-divider>
<MsButton>{{ t('testPlan.testPlanIndex.copy') }}</MsButton>
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+ADD']">{{ t('testPlan.testPlanIndex.copy') }}</MsButton>
<a-divider direction="vertical" :margin="8"></a-divider>
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
<MsTableMoreAction
v-permission="['PROJECT_TEST_PLAN:READ+DELETE']"
:list="moreActions"
@select="handleMoreActionSelect($event, record)"
/>
</template>
</ms-base-table>
</template>

View File

@ -1,7 +1,7 @@
<template>
<div class="rounded-2xl bg-white">
<div class="p-[24px] pb-[16px]">
<a-button type="primary">
<a-button v-permission="['PROJECT_TEST_PLAN:READ+ADD']" type="primary">
{{ t('testPlan.testPlanIndex.createTestPlan') }}
</a-button>
</div>