feat(功能用例): 用例导入联调&bug修改&部分权限&非路由xpack入口控制
This commit is contained in:
parent
9117526096
commit
4305aa9add
|
@ -53,8 +53,10 @@
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
try {
|
try {
|
||||||
appStore.initSystemVersion(); // 初始化系统版本
|
appStore.initSystemVersion(); // 初始化系统版本
|
||||||
appStore.initPageConfig(); // 初始化页面配置
|
|
||||||
licenseStore.getValidateLicense(); // 初始化校验license
|
licenseStore.getValidateLicense(); // 初始化校验license
|
||||||
|
if (licenseStore.hasLicense()) {
|
||||||
|
appStore.initPageConfig(); // 初始化页面配置
|
||||||
|
}
|
||||||
// 项目初始化时需要获取基础设置信息,看当前站点 url是否为系统内置默认地址,如果是需要替换为当前项目部署的 url 地址
|
// 项目初始化时需要获取基础设置信息,看当前站点 url是否为系统内置默认地址,如果是需要替换为当前项目部署的 url 地址
|
||||||
const isInitUrl = getLocalStorage('isInitUrl'); // 是否已经初始化过 url
|
const isInitUrl = getLocalStorage('isInitUrl'); // 是否已经初始化过 url
|
||||||
if (isInitUrl === 'true') return;
|
if (isInitUrl === 'true') return;
|
||||||
|
|
|
@ -55,6 +55,7 @@ import {
|
||||||
GetSearchCustomFieldsUrl,
|
GetSearchCustomFieldsUrl,
|
||||||
getTransferTreeUrl,
|
getTransferTreeUrl,
|
||||||
GetTrashCaseModuleTreeUrl,
|
GetTrashCaseModuleTreeUrl,
|
||||||
|
importExcelCaseUrl,
|
||||||
MoveCaseModuleTreeUrl,
|
MoveCaseModuleTreeUrl,
|
||||||
PreviewEditorImageUrl,
|
PreviewEditorImageUrl,
|
||||||
PreviewFileUrl,
|
PreviewFileUrl,
|
||||||
|
@ -395,5 +396,9 @@ export function editorUploadFile(data: { fileList: File[] }) {
|
||||||
export function editorPreviewImages(data: PreviewImages) {
|
export function editorPreviewImages(data: PreviewImages) {
|
||||||
return MSR.post({ url: PreviewEditorImageUrl, data });
|
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 {};
|
export default {};
|
||||||
|
|
|
@ -141,3 +141,5 @@ export const EditorUploadFileUrl = '/attachment/upload/temp/file';
|
||||||
export const PreviewEditorImageUrl = '/attachment/download/file';
|
export const PreviewEditorImageUrl = '/attachment/download/file';
|
||||||
// 导入excel文件检查
|
// 导入excel文件检查
|
||||||
export const exportExcelCheckUrl = '/functional/case/pre-check/excel';
|
export const exportExcelCheckUrl = '/functional/case/pre-check/excel';
|
||||||
|
// 导入excel文件
|
||||||
|
export const importExcelCaseUrl = '/functional/case/import/excel';
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-upload
|
<a-upload
|
||||||
ref="uploadRef"
|
ref="uploadRef"
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="innerFileList"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="beforeUpload"
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { useVModel } from '@vueuse/core';
|
||||||
|
|
||||||
import type { MsFileItem } from '@/components/pure/ms-upload/types';
|
import type { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
|
|
||||||
|
@ -46,19 +46,26 @@
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
fileList: MsFileItem[];
|
||||||
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'upload', file: File): void;
|
(e: 'upload', file: File): void;
|
||||||
(e: 'change', _fileList: MsFileItem[], fileItem: MsFileItem): void;
|
(e: 'change', _fileList: MsFileItem[], fileItem: MsFileItem): void;
|
||||||
(e: 'linkFile'): 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) {
|
function beforeUpload(file: File) {
|
||||||
emit('upload', file);
|
emit('upload', file);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
||||||
|
innerFileList.value = _fileList;
|
||||||
emit('change', _fileList, fileItem);
|
emit('change', _fileList, fileItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,9 +55,13 @@
|
||||||
<a-radio value="commonScript">{{ t('project.commonScript.commonScript') }}</a-radio>
|
<a-radio value="commonScript">{{ t('project.commonScript.commonScript') }}</a-radio>
|
||||||
<a-radio value="executionResult">{{ t('project.commonScript.executionResult') }}</a-radio>
|
<a-radio value="executionResult">{{ t('project.commonScript.executionResult') }}</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a-button type="outline" :loading="loading" @click="testScript">{{
|
<a-button
|
||||||
t('project.commonScript.scriptTest')
|
v-permission="['PROJECT_CUSTOM_FUNCTION:READ+EXECUTE']"
|
||||||
}}</a-button>
|
type="outline"
|
||||||
|
:loading="loading"
|
||||||
|
@click="testScript"
|
||||||
|
>{{ t('project.commonScript.scriptTest') }}</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<ScriptDefined
|
<ScriptDefined
|
||||||
v-model:language="form.type"
|
v-model:language="form.type"
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
import useUser from '@/hooks/useUser';
|
import useUser from '@/hooks/useUser';
|
||||||
import { BOTTOM_MENU_LIST } from '@/router/constants';
|
import { BOTTOM_MENU_LIST } from '@/router/constants';
|
||||||
import { useAppStore, useUserStore } from '@/store';
|
import { useAppStore, useUserStore } from '@/store';
|
||||||
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
import { openWindow, regexUrl } from '@/utils';
|
import { openWindow, regexUrl } from '@/utils';
|
||||||
import { listenerRouteChange } from '@/utils/route-listener';
|
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 isActiveSwitchOrg = ref(false);
|
||||||
const personalMenus = [
|
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(
|
watch(
|
||||||
() => personalMenusVisible.value,
|
() => personalMenusVisible.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<MsPopconfirm
|
<span>
|
||||||
type="error"
|
<MsPopconfirm
|
||||||
:title="props.title"
|
type="error"
|
||||||
:sub-title-tip="props.subTitleTip"
|
:title="props.title"
|
||||||
:loading="props.loading"
|
:sub-title-tip="props.subTitleTip"
|
||||||
:visible="currentVisible"
|
:loading="props.loading"
|
||||||
:ok-text="props.okText"
|
:visible="currentVisible"
|
||||||
@confirm="handleOk"
|
:ok-text="props.okText"
|
||||||
@cancel="handleCancel"
|
@confirm="handleOk"
|
||||||
>
|
@cancel="handleCancel"
|
||||||
<MsButton @click="showPopover">{{ t(props.removeText) }}</MsButton>
|
>
|
||||||
</MsPopconfirm>
|
<MsButton @click="showPopover">{{ t(props.removeText) }}</MsButton>
|
||||||
|
</MsPopconfirm>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
|
@ -22,7 +22,7 @@ const CaseManagement: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/case-management/caseManagementFeature/index.vue'),
|
component: () => import('@/views/case-management/caseManagementFeature/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.caseManagement.featureCase',
|
locale: 'menu.caseManagement.featureCase',
|
||||||
roles: ['*'],
|
roles: ['FUNCTIONAL_CASE:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@ const ProjectManagement: AppRouteRecordRaw = {
|
||||||
redirect: '/project-management/permission/basicInfo',
|
redirect: '/project-management/permission/basicInfo',
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.projectManagement.projectPermission',
|
locale: 'menu.projectManagement.projectPermission',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_USER:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@ -34,7 +34,7 @@ const ProjectManagement: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/project-management/projectAndPermission/basicInfos/index.vue'),
|
component: () => import('@/views/project-management/projectAndPermission/basicInfos/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'project.permission.basicInfo',
|
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'),
|
component: () => import('@/views/project-management/template/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.projectManagement.templateManager',
|
locale: 'menu.projectManagement.templateManager',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_TEMPLATE:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -227,7 +227,7 @@ const ProjectManagement: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/project-management/commonScript/index.vue'),
|
component: () => import('@/views/project-management/commonScript/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.projectManagement.commonScript',
|
locale: 'menu.projectManagement.commonScript',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_CUSTOM_FUNCTION:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
// import useLicenseStore from '@/store/modules/setting/license';
|
||||||
|
|
||||||
import { SettingRouteEnum } from '@/enums/routeEnum';
|
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import { DEFAULT_LAYOUT } from '../base';
|
import { DEFAULT_LAYOUT } from '../base';
|
||||||
import type { AppRouteRecordRaw } from '../types';
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
|
||||||
|
// const licenseStore = useLicenseStore();
|
||||||
|
|
||||||
const Setting: AppRouteRecordRaw = {
|
const Setting: AppRouteRecordRaw = {
|
||||||
path: '/setting',
|
path: '/setting',
|
||||||
name: SettingRouteEnum.SETTING,
|
name: SettingRouteEnum.SETTING,
|
||||||
|
@ -101,7 +105,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/setting/system/authorizedManagement/index.vue'),
|
component: () => import('@/views/setting/system/authorizedManagement/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.authorizedManagement',
|
locale: 'menu.settings.system.authorizedManagement',
|
||||||
roles: ['*'],
|
roles: ['SYSTEM_AUTH:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -121,7 +125,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/setting/system/pluginManager/index.vue'),
|
component: () => import('@/views/setting/system/pluginManager/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.system.pluginManager',
|
locale: 'menu.settings.system.pluginManager',
|
||||||
roles: ['*'],
|
roles: ['SYSTEM_PLUGIN:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -144,7 +148,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/setting/organization/member/index.vue'),
|
component: () => import('@/views/setting/organization/member/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.organization.member',
|
locale: 'menu.settings.organization.member',
|
||||||
roles: ['*'],
|
roles: ['ORGANIZATION_MEMBER:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -174,7 +178,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/setting/organization/serviceIntegration/index.vue'),
|
component: () => import('@/views/setting/organization/serviceIntegration/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.organization.serviceIntegration',
|
locale: 'menu.settings.organization.serviceIntegration',
|
||||||
roles: ['*'],
|
roles: ['SYSTEM_SERVICE_INTEGRATION:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -184,7 +188,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/setting/organization/template/index.vue'),
|
component: () => import('@/views/setting/organization/template/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.settings.organization.template',
|
locale: 'menu.settings.organization.template',
|
||||||
roles: ['*'],
|
roles: ['ORGANIZATION_TEMPLATE:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,7 +22,7 @@ const TestPlan: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/test-plan/testPlan/index.vue'),
|
component: () => import('@/views/test-plan/testPlan/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.testPlan',
|
locale: 'menu.testPlan',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_TEST_PLAN:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,6 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
persist: true,
|
persist: true,
|
||||||
state: (): {
|
state: (): {
|
||||||
moduleId: string[]; // 当前选中模块
|
moduleId: string[]; // 当前选中模块
|
||||||
allModuleId: string[]; // 所有模块
|
|
||||||
caseTree: ModuleTreeNode[]; // 用例树
|
caseTree: ModuleTreeNode[]; // 用例树
|
||||||
modulesCount: Record<string, any>; // 用例树模块数量
|
modulesCount: Record<string, any>; // 用例树模块数量
|
||||||
recycleModulesCount: Record<string, any>; // 回收站模块数量
|
recycleModulesCount: Record<string, any>; // 回收站模块数量
|
||||||
|
@ -17,7 +16,6 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
tabSettingList: TabItemType[]; // 详情tab
|
tabSettingList: TabItemType[]; // 详情tab
|
||||||
} => ({
|
} => ({
|
||||||
moduleId: [],
|
moduleId: [],
|
||||||
allModuleId: [],
|
|
||||||
caseTree: [],
|
caseTree: [],
|
||||||
modulesCount: {},
|
modulesCount: {},
|
||||||
recycleModulesCount: {},
|
recycleModulesCount: {},
|
||||||
|
@ -26,10 +24,11 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
// 设置选择moduleId
|
// 设置选择moduleId
|
||||||
setModuleId(currentModuleId: string[], offspringIds: string[]) {
|
setModuleId(currentModuleId: string[]) {
|
||||||
this.moduleId = currentModuleId;
|
if (['all', 'recycle'].includes(currentModuleId[0])) {
|
||||||
if (offspringIds.length > 0) {
|
this.moduleId = ['root'];
|
||||||
this.allModuleId = offspringIds;
|
} else {
|
||||||
|
this.moduleId = currentModuleId;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 设置用例树
|
// 设置用例树
|
||||||
|
|
|
@ -24,7 +24,8 @@ const useLicenseStore = defineStore('license', {
|
||||||
if (!result || !result.status || !result.license || !result.license.count) {
|
if (!result || !result.status || !result.license || !result.license.count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setLicenseStatus(result.status);
|
// this.setLicenseStatus(result.status);
|
||||||
|
this.setLicenseStatus('fail');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
<span class="w-[calc(100%-36%)]">
|
<span class="w-[calc(100%-36%)]">
|
||||||
<a-tree-select
|
<a-tree-select
|
||||||
v-model="detailInfo.moduleId"
|
v-model="detailInfo.moduleId"
|
||||||
:data="featureCaseStore.caseTree"
|
:data="caseTree"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:allow-search="true"
|
:allow-search="true"
|
||||||
:field-names="{
|
:field-names="{
|
||||||
|
@ -179,6 +179,7 @@
|
||||||
</div>
|
</div>
|
||||||
<inputComment
|
<inputComment
|
||||||
v-model:content="content"
|
v-model:content="content"
|
||||||
|
v-permission="['FUNCTIONAL_CASE:READ+COMMENT']"
|
||||||
:is-active="isActive"
|
:is-active="isActive"
|
||||||
is-show-avatar
|
is-show-avatar
|
||||||
is-use-bottom
|
is-use-bottom
|
||||||
|
@ -223,15 +224,17 @@
|
||||||
deleteCaseRequest,
|
deleteCaseRequest,
|
||||||
followerCaseRequest,
|
followerCaseRequest,
|
||||||
getCaseDetail,
|
getCaseDetail,
|
||||||
|
getCaseModuleTree,
|
||||||
} from '@/api/modules/case-management/featureCase';
|
} from '@/api/modules/case-management/featureCase';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
import useUserStore from '@/store/modules/user';
|
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 type { CustomAttributes, DetailCase, TabItemType } from '@/models/caseManagement/featureCase';
|
||||||
|
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
||||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import { getCaseLevels } from './utils';
|
import { getCaseLevels } from './utils';
|
||||||
|
@ -310,20 +313,26 @@
|
||||||
reviewStatus: 'UN_REVIEWED',
|
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 detailInfo = ref<DetailCase>({ ...initDetail });
|
||||||
const customFields = ref<CustomAttributes[]>([]);
|
const customFields = ref<CustomAttributes[]>([]);
|
||||||
const caseLevels = ref<CaseLevel>('P0');
|
const caseLevels = ref<CaseLevel>('P0');
|
||||||
function loadedCase(detail: DetailCase) {
|
function loadedCase(detail: DetailCase) {
|
||||||
|
getCaseTree();
|
||||||
detailInfo.value = { ...detail };
|
detailInfo.value = { ...detail };
|
||||||
customFields.value = detailInfo.value.customFields;
|
customFields.value = detailInfo.value.customFields;
|
||||||
caseLevels.value = getCaseLevels(customFields.value) as CaseLevel;
|
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);
|
const editLoading = ref<boolean>(false);
|
||||||
|
|
||||||
function updateSuccess() {
|
function updateSuccess() {
|
||||||
|
|
|
@ -82,26 +82,32 @@
|
||||||
</template> -->
|
</template> -->
|
||||||
<!-- 渲染自定义字段结束 -->
|
<!-- 渲染自定义字段结束 -->
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsButton @click="operateCase(record, 'edit')">{{ t('common.edit') }}</MsButton>
|
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" @click="operateCase(record, 'edit')">{{
|
||||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
t('common.edit')
|
||||||
<MsButton @click="operateCase(record, 'copy')">{{ t('caseManagement.featureCase.copy') }}</MsButton>
|
}}</MsButton>
|
||||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
<a-divider v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" direction="vertical" :margin="8"></a-divider>
|
||||||
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
|
<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>
|
||||||
|
|
||||||
<template v-if="(keyword || '').trim() === ''" #empty>
|
<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') }}
|
{{ 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') }}
|
{{ t('caseManagement.featureCase.creatingCase') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
{{ t('caseManagement.featureCase.or') }}
|
{{ 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') }}
|
{{ t('caseManagement.featureCase.importExcel') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
<MsButton class="ml-[4px]" @click="emit('import', 'Xmind')">
|
<!-- <MsButton class="ml-[4px]" @click="emit('import', 'Xmind')">
|
||||||
{{ t('caseManagement.featureCase.importXmind') }}
|
{{ t('caseManagement.featureCase.importXmind') }}
|
||||||
</MsButton>
|
</MsButton> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
|
@ -1098,7 +1104,7 @@
|
||||||
initData();
|
initData();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onBeforeMount(() => {
|
||||||
if (route.query.id) {
|
if (route.query.id) {
|
||||||
showCaseDetail(route.query.id as string, 0);
|
showCaseDetail(route.query.id as string, 0);
|
||||||
}
|
}
|
||||||
|
@ -1116,10 +1122,12 @@
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.activeFolder,
|
() => props.activeFolder,
|
||||||
() => {
|
(val) => {
|
||||||
keyword.value = '';
|
keyword.value = '';
|
||||||
initData();
|
if (props.activeFolder !== 'recycle' && val) {
|
||||||
resetSelector();
|
initData();
|
||||||
|
resetSelector();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -77,7 +77,12 @@
|
||||||
:upload-image="handleUploadImage"
|
:upload-image="handleUploadImage"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</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>
|
</a-form>
|
||||||
<!-- 文件列表开始 -->
|
<!-- 文件列表开始 -->
|
||||||
<div class="w-[90%]">
|
<div class="w-[90%]">
|
||||||
|
@ -260,6 +265,7 @@
|
||||||
getAssociatedFileListUrl,
|
getAssociatedFileListUrl,
|
||||||
getCaseDefaultFields,
|
getCaseDefaultFields,
|
||||||
getCaseDetail,
|
getCaseDetail,
|
||||||
|
getCaseModuleTree,
|
||||||
previewFile,
|
previewFile,
|
||||||
transferFileRequest,
|
transferFileRequest,
|
||||||
updateFile,
|
updateFile,
|
||||||
|
@ -311,14 +317,6 @@
|
||||||
|
|
||||||
const featureCaseStore = useFeatureCaseStore();
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
const modelId = computed(() => featureCaseStore.moduleId[0]);
|
const modelId = computed(() => featureCaseStore.moduleId[0]);
|
||||||
const caseTree = computed(() => {
|
|
||||||
return mapTree<ModuleTreeNode>(featureCaseStore.caseTree, (e) => {
|
|
||||||
return {
|
|
||||||
...e,
|
|
||||||
draggable: false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const initForm: DetailCase = {
|
const initForm: DetailCase = {
|
||||||
id: '',
|
id: '',
|
||||||
|
@ -560,7 +558,6 @@
|
||||||
async function getCaseInfo() {
|
async function getCaseInfo() {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
// await getAllCaseFields();
|
|
||||||
const detailResult: DetailCase = await getCaseDetail(props.caseId);
|
const detailResult: DetailCase = await getCaseDetail(props.caseId);
|
||||||
const fileIds = (detailResult.attachments || []).map((item: any) => item.id);
|
const fileIds = (detailResult.attachments || []).map((item: any) => item.id);
|
||||||
if (fileIds.length) {
|
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') {
|
if (route.params.mode === 'edit' || route.params.mode === 'copy') {
|
||||||
getCaseInfo();
|
getCaseInfo();
|
||||||
} else {
|
} else {
|
||||||
initDefaultFields();
|
initDefaultFields();
|
||||||
}
|
}
|
||||||
|
initSelectTree();
|
||||||
});
|
});
|
||||||
// 处理关联文件和已关联文件本地文件和已上传文本文件
|
// 处理关联文件和已关联文件本地文件和已上传文本文件
|
||||||
function getFilesParams() {
|
function getFilesParams() {
|
||||||
|
@ -619,7 +627,7 @@
|
||||||
if (val) {
|
if (val) {
|
||||||
params.value.request = { ...form.value };
|
params.value.request = { ...form.value };
|
||||||
emit('update:formModeValue', params.value);
|
emit('update:formModeValue', params.value);
|
||||||
featureCaseStore.setModuleId([form.value.moduleId], []);
|
featureCaseStore.setModuleId([form.value.moduleId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -96,6 +96,8 @@
|
||||||
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
|
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
|
||||||
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
||||||
|
|
||||||
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openModal } = useModal();
|
const { openModal } = useModal();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
@ -141,6 +143,7 @@
|
||||||
() => props.selectedKeys,
|
() => props.selectedKeys,
|
||||||
(val) => {
|
(val) => {
|
||||||
selectedNodeKeys.value = val || [];
|
selectedNodeKeys.value = val || [];
|
||||||
|
featureCaseStore.setModuleId(val as string[]);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -150,7 +153,6 @@
|
||||||
emits('update:selectedKeys', val);
|
emits('update:selectedKeys', val);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const featureCaseStore = useFeatureCaseStore();
|
|
||||||
/**
|
/**
|
||||||
* 初始化模块树
|
* 初始化模块树
|
||||||
* @param isSetDefaultKey 是否设置第一个节点为选中节点
|
* @param isSetDefaultKey 是否设置第一个节点为选中节点
|
||||||
|
@ -169,6 +171,7 @@
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
featureCaseStore.setModulesTree(caseTree.value);
|
featureCaseStore.setModulesTree(caseTree.value);
|
||||||
|
featureCaseStore.setModuleId(['all']);
|
||||||
if (isSetDefaultKey) {
|
if (isSetDefaultKey) {
|
||||||
selectedNodeKeys.value = [caseTree.value[0].id];
|
selectedNodeKeys.value = [caseTree.value[0].id];
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,11 +43,12 @@
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:disabled="confirmLoading"
|
:disabled="confirmLoading"
|
||||||
></MsUpload>
|
></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-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-option v-for="item of versionOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item> -->
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
|
@ -69,9 +70,14 @@
|
||||||
></a-checkbox>
|
></a-checkbox>
|
||||||
<div>
|
<div>
|
||||||
<a-button type="secondary" @click="handleCancel">{{ t('system.plugin.pluginCancel') }}</a-button>
|
<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">{{
|
<a-button
|
||||||
t('caseManagement.featureCase.checkTemplate')
|
class="ml-3"
|
||||||
}}</a-button>
|
type="primary"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
:loading="props.confirmLoading"
|
||||||
|
@click="saveConfirm"
|
||||||
|
>{{ t('caseManagement.featureCase.checkTemplate') }}</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -95,6 +101,7 @@
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
validateType: 'Excel' | 'Xmind';
|
validateType: 'Excel' | 'Xmind';
|
||||||
|
confirmLoading: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -112,19 +119,17 @@
|
||||||
set: (val) => emit('update:visible', val),
|
set: (val) => emit('update:visible', val),
|
||||||
});
|
});
|
||||||
|
|
||||||
const confirmLoading = ref<boolean>(false);
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
fileList.value = [];
|
fileList.value = [];
|
||||||
emit('close');
|
emit('close');
|
||||||
};
|
};
|
||||||
|
|
||||||
const versionOptions = ref([
|
// const versionOptions = ref([
|
||||||
{
|
// {
|
||||||
id: '1001',
|
// id: '1001',
|
||||||
name: 'V1.0',
|
// name: 'V1.0',
|
||||||
},
|
// },
|
||||||
]);
|
// ]);
|
||||||
|
|
||||||
const isRecover = ref<boolean>(false);
|
const isRecover = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
|
@ -119,19 +119,18 @@
|
||||||
|
|
||||||
import { ValidateInfo } from '@/models/caseManagement/featureCase';
|
import { ValidateInfo } from '@/models/caseManagement/featureCase';
|
||||||
|
|
||||||
import type { FileItem } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
validateType: 'Excel' | 'Xmind';
|
validateType: 'Excel' | 'Xmind';
|
||||||
validateInfo: ValidateInfo;
|
validateInfo: ValidateInfo;
|
||||||
|
importLoading: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:visible', val: boolean): void;
|
(e: 'update:visible', val: boolean): void;
|
||||||
(e: 'save', files: FileItem[]): void;
|
|
||||||
(e: 'close'): void;
|
(e: 'close'): void;
|
||||||
|
(e: 'save'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const validateResultModal = computed({
|
const validateResultModal = computed({
|
||||||
|
@ -175,7 +174,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确定继续导入
|
// 确定继续导入
|
||||||
function confirmImport() {}
|
function confirmImport() {
|
||||||
|
emit('save');
|
||||||
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
validateResultInfo.value = { ...props.validateInfo };
|
validateResultInfo.value = { ...props.validateInfo };
|
||||||
|
|
|
@ -124,10 +124,15 @@
|
||||||
</div>
|
</div>
|
||||||
</template> -->
|
</template> -->
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsButton @click="recoverCase(record.id)">{{ t('caseManagement.featureCase.batchRecover') }}</MsButton>
|
<MsButton v-permission="['FUNCTIONAL_CASE:READ+DELETE']" @click="recoverCase(record.id)">{{
|
||||||
<MsButton class="!mr-0" @click="handleBatchCleanOut(record)">{{
|
t('caseManagement.featureCase.batchRecover')
|
||||||
t('caseManagement.featureCase.batchCleanOut')
|
|
||||||
}}</MsButton>
|
}}</MsButton>
|
||||||
|
<MsButton
|
||||||
|
v-permission="['FUNCTIONAL_CASE:READ+DELETE']"
|
||||||
|
class="!mr-0"
|
||||||
|
@click="handleBatchCleanOut(record)"
|
||||||
|
>{{ t('caseManagement.featureCase.batchCleanOut') }}</MsButton
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
</div>
|
</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(() => {
|
onMounted(() => {
|
||||||
getDefaultFields();
|
getDefaultFields();
|
||||||
initFilter();
|
initFilter();
|
||||||
|
|
|
@ -53,9 +53,11 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
|
|
||||||
import type { TabItemType } from '@/models/caseManagement/featureCase';
|
import type { TabItemType } from '@/models/caseManagement/featureCase';
|
||||||
|
|
||||||
|
const licenseStore = useLicenseStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const featureCaseStore = useFeatureCaseStore();
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
|
@ -98,6 +100,19 @@
|
||||||
|
|
||||||
let buggerTab: TabItemType[] = [];
|
let buggerTab: TabItemType[] = [];
|
||||||
let testPlanTab: TabItemType[] = [];
|
let testPlanTab: TabItemType[] = [];
|
||||||
|
|
||||||
|
function getTabList() {
|
||||||
|
if (licenseStore.hasLicense()) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'changeHistory',
|
||||||
|
title: 'caseManagement.featureCase.changeHistory',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const tabDefaultSettingList = ref<TabItemType[]>([
|
const tabDefaultSettingList = ref<TabItemType[]>([
|
||||||
{
|
{
|
||||||
key: 'case',
|
key: 'case',
|
||||||
|
@ -119,11 +134,7 @@
|
||||||
title: 'caseManagement.featureCase.comments',
|
title: 'caseManagement.featureCase.comments',
|
||||||
enable: true,
|
enable: true,
|
||||||
},
|
},
|
||||||
{
|
...getTabList(),
|
||||||
key: 'changeHistory',
|
|
||||||
title: 'caseManagement.featureCase.changeHistory',
|
|
||||||
enable: true,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
async function getTabModule() {
|
async function getTabModule() {
|
||||||
buggerTab = [];
|
buggerTab = [];
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
<MsButton @click="cancelLink(record.id)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
<MsButton @click="cancelLink(record.id)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="(keyword || '').trim() === ''" #empty>
|
<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') }}
|
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||||
<MsButton class="ml-[8px]" @click="linkDefect">
|
<MsButton class="ml-[8px]" @click="linkDefect">
|
||||||
{{ t('caseManagement.featureCase.linkDefect') }}
|
{{ t('caseManagement.featureCase.linkDefect') }}
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
<MsButton @click="cancelLink(record.id)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
<MsButton @click="cancelLink(record.id)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="(keyword || '').trim() === ''" #empty>
|
<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') }}
|
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||||
<MsButton class="ml-[8px]" @click="createDefect">
|
<MsButton class="ml-[8px]" @click="createDefect">
|
||||||
{{ t('caseManagement.featureCase.createDefect') }}
|
{{ t('caseManagement.featureCase.createDefect') }}
|
||||||
|
|
|
@ -26,6 +26,22 @@
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||||
</template>
|
</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>
|
</ms-base-table>
|
||||||
<MsCaseAssociate
|
<MsCaseAssociate
|
||||||
v-model:visible="innerVisible"
|
v-model:visible="innerVisible"
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
}}</MsButton>
|
}}</MsButton>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="(props.funParams.keyword || '').trim() === ''" #empty>
|
<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') }}
|
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||||
<MsButton class="ml-[8px]" @click="emit('create')">
|
<MsButton class="ml-[8px]" @click="emit('create')">
|
||||||
{{ t('caseManagement.featureCase.addDemand') }}
|
{{ t('caseManagement.featureCase.addDemand') }}
|
||||||
|
|
|
@ -84,7 +84,15 @@
|
||||||
</div>
|
</div>
|
||||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||||
<template #caseLevel="{ record }">
|
<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>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
|
@ -114,8 +122,10 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
import { Message } from '@arco-design/web-vue';
|
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 MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
@ -139,6 +149,9 @@
|
||||||
import type { CaseManagementTable, CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
|
import type { CaseManagementTable, CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
|
||||||
import type { TableQueryParams } from '@/models/common';
|
import type { TableQueryParams } from '@/models/common';
|
||||||
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
||||||
|
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
import { getCaseLevels } from '../../utils';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
|
@ -451,6 +464,13 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
function createCase() {
|
||||||
|
router.push({
|
||||||
|
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
resetSelector();
|
resetSelector();
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="(keyword || '').trim() === ''" #empty>
|
<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') }}
|
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||||
<MsButton class="ml-[8px]" @click="addCase">
|
<MsButton class="ml-[8px]" @click="addCase">
|
||||||
{{
|
{{
|
||||||
|
|
|
@ -9,7 +9,13 @@
|
||||||
asterisk-position="end"
|
asterisk-position="end"
|
||||||
>
|
>
|
||||||
<span class="absolute right-[6px] top-0">
|
<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))]" />{{
|
<MsIcon type="icon-icon_edit_outlined" class="mr-1 font-[16px] text-[rgb(var(--primary-5))]" />{{
|
||||||
t('caseManagement.featureCase.contentEdit')
|
t('caseManagement.featureCase.contentEdit')
|
||||||
}}</a-button
|
}}</a-button
|
||||||
|
@ -99,7 +105,7 @@
|
||||||
<div v-if="props.allowEdit" class="flex flex-col">
|
<div v-if="props.allowEdit" class="flex flex-col">
|
||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
<a-dropdown position="tr" trigger="hover">
|
<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
|
<template #icon> <icon-plus class="text-[14px]" /> </template
|
||||||
>{{ t('system.orgTemplate.addAttachment') }}</a-button
|
>{{ t('system.orgTemplate.addAttachment') }}</a-button
|
||||||
>
|
>
|
||||||
|
@ -261,7 +267,7 @@
|
||||||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
import { downloadByteFile, encrypted, getGenerateId, sleep } from '@/utils';
|
||||||
import { scrollIntoView } from '@/utils/dom';
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
import type { AssociatedList, DetailCase, StepList } from '@/models/caseManagement/featureCase';
|
import type { AssociatedList, DetailCase, StepList } from '@/models/caseManagement/featureCase';
|
||||||
|
@ -479,10 +485,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileListRef = ref<InstanceType<typeof MsFileList>>();
|
const fileListRef = ref<InstanceType<typeof MsFileList>>();
|
||||||
async function startUpload() {
|
|
||||||
await fileListRef.value?.startUpload();
|
|
||||||
emit('updateSuccess');
|
|
||||||
}
|
|
||||||
|
|
||||||
function beforeUpload(file: File) {
|
function beforeUpload(file: File) {
|
||||||
const _maxSize = 50 * 1024 * 1024;
|
const _maxSize = 50 * 1024 * 1024;
|
||||||
|
@ -600,16 +602,18 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
async function startUpload() {
|
||||||
|
await sleep(300);
|
||||||
|
await fileListRef.value?.startUpload();
|
||||||
|
emit('updateSuccess');
|
||||||
|
}
|
||||||
// 文件列表单个上传
|
// 文件列表单个上传
|
||||||
watch(
|
watch(
|
||||||
() => fileList.value,
|
() => fileList.value,
|
||||||
(val) => {
|
async (val) => {
|
||||||
if (val) {
|
const isNewFiles = val.filter((item) => item.status === 'init').length;
|
||||||
if (val.filter((item) => item.status === 'init').length) {
|
if (val && isNewFiles) {
|
||||||
setTimeout(() => {
|
startUpload();
|
||||||
startUpload();
|
|
||||||
}, 30);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded-2xl bg-white">
|
<div class="rounded-2xl bg-white">
|
||||||
<div class="p-[24px] pb-[16px]">
|
<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') }}
|
{{ t('caseManagement.featureCase.creatingCase') }}
|
||||||
</a-button>
|
</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') }}
|
{{ t('caseManagement.featureCase.importExcel') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="outline" @click="importCase('Xmind')">
|
<!-- <a-button type="outline" @click="importCase('Xmind')">
|
||||||
{{ t('caseManagement.featureCase.importXmind') }}
|
{{ t('caseManagement.featureCase.importXmind') }}
|
||||||
</a-button>
|
</a-button> -->
|
||||||
</div>
|
</div>
|
||||||
<a-divider class="!my-0" />
|
<a-divider class="!my-0" />
|
||||||
<div class="pageWrap">
|
<div class="pageWrap">
|
||||||
|
@ -98,6 +98,7 @@
|
||||||
<ExportExcelModal
|
<ExportExcelModal
|
||||||
v-model:visible="showExcelModal"
|
v-model:visible="showExcelModal"
|
||||||
:validate-type="validateType"
|
:validate-type="validateType"
|
||||||
|
:confirm-loading="validateLoading"
|
||||||
@save="validateTemplate"
|
@save="validateTemplate"
|
||||||
@close="showExcelModal = false"
|
@close="showExcelModal = false"
|
||||||
/>
|
/>
|
||||||
|
@ -112,7 +113,9 @@
|
||||||
v-model:visible="validateResultModal"
|
v-model:visible="validateResultModal"
|
||||||
:validate-type="validateType"
|
:validate-type="validateType"
|
||||||
:validate-info="validateInfo"
|
:validate-info="validateInfo"
|
||||||
|
:import-loading="importLoading"
|
||||||
@close="closeHandler"
|
@close="closeHandler"
|
||||||
|
@save="conFirmImport"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -133,7 +136,7 @@
|
||||||
import ValidateModal from './components/export/validateModal.vue';
|
import ValidateModal from './components/export/validateModal.vue';
|
||||||
import ValidateResult from './components/export/validateResult.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 { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
|
@ -193,7 +196,7 @@
|
||||||
[activeFolder.value] = keys;
|
[activeFolder.value] = keys;
|
||||||
activeCaseType.value = 'module';
|
activeCaseType.value = 'module';
|
||||||
offspringIds.value = [..._offspringIds];
|
offspringIds.value = [..._offspringIds];
|
||||||
featureCaseStore.setModuleId(keys, offspringIds.value);
|
featureCaseStore.setModuleId(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmLoading = ref(false);
|
const confirmLoading = ref(false);
|
||||||
|
@ -322,8 +325,15 @@
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileList = ref<FileItem[]>([]);
|
||||||
|
const isCover = ref<boolean>(false);
|
||||||
|
const validateLoading = ref<boolean>(false);
|
||||||
|
|
||||||
// 校验导入模版
|
// 校验导入模版
|
||||||
async function validateTemplate(files: FileItem[], cover: boolean) {
|
async function validateTemplate(files: FileItem[], cover: boolean) {
|
||||||
|
fileList.value = files;
|
||||||
|
isCover.value = cover;
|
||||||
|
validateLoading.value = true;
|
||||||
try {
|
try {
|
||||||
start();
|
start();
|
||||||
validateModal.value = true;
|
validateModal.value = true;
|
||||||
|
@ -339,6 +349,8 @@
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
validateLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,6 +365,29 @@
|
||||||
function closeHandler() {
|
function closeHandler() {
|
||||||
showExcelModal.value = false;
|
showExcelModal.value = false;
|
||||||
validateResultModal.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(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -3,6 +3,7 @@ export default {
|
||||||
'caseManagement.featureCase.importCase': 'Import Case',
|
'caseManagement.featureCase.importCase': 'Import Case',
|
||||||
'caseManagement.featureCase.importExcel': 'Import Excel',
|
'caseManagement.featureCase.importExcel': 'Import Excel',
|
||||||
'caseManagement.featureCase.importXmind': 'Import Xmind',
|
'caseManagement.featureCase.importXmind': 'Import Xmind',
|
||||||
|
'caseManagement.featureCase.importSuccess': 'Import successfully',
|
||||||
'caseManagement.featureCase.publicCase': 'Public of Cases',
|
'caseManagement.featureCase.publicCase': 'Public of Cases',
|
||||||
'caseManagement.featureCase.allCase': 'All of Cases',
|
'caseManagement.featureCase.allCase': 'All of Cases',
|
||||||
'caseManagement.featureCase.searchTip': 'Please enter a group name',
|
'caseManagement.featureCase.searchTip': 'Please enter a group name',
|
||||||
|
|
|
@ -3,6 +3,7 @@ export default {
|
||||||
'caseManagement.featureCase.importCase': '导入用例',
|
'caseManagement.featureCase.importCase': '导入用例',
|
||||||
'caseManagement.featureCase.importExcel': 'Excel导入',
|
'caseManagement.featureCase.importExcel': 'Excel导入',
|
||||||
'caseManagement.featureCase.importXmind': 'Xmind导入',
|
'caseManagement.featureCase.importXmind': 'Xmind导入',
|
||||||
|
'caseManagement.featureCase.importSuccess': '导入成功',
|
||||||
'caseManagement.featureCase.publicCase': '公共用例库',
|
'caseManagement.featureCase.publicCase': '公共用例库',
|
||||||
'caseManagement.featureCase.allCase': '全部用例',
|
'caseManagement.featureCase.allCase': '全部用例',
|
||||||
'caseManagement.featureCase.searchTip': '请输入分组名称',
|
'caseManagement.featureCase.searchTip': '请输入分组名称',
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<a-form-item class="login-form-item" field="radio" hide-label>
|
<a-form-item class="login-form-item" field="radio" hide-label>
|
||||||
<a-radio-group v-model="userInfo.authenticate" type="button">
|
<a-radio-group v-model="userInfo.authenticate" type="button">
|
||||||
<a-radio value="LOCAL">普通登陆</a-radio>
|
<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="OAuth2">OAuth2 测试</a-radio>
|
||||||
<a-radio value="OIDC 90">OIDC 90</a-radio>
|
<a-radio value="OIDC 90">OIDC 90</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<MsCard simple>
|
<MsCard simple>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<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
|
<a-input-search
|
||||||
v-model:model-value="keyword"
|
v-model:model-value="keyword"
|
||||||
:placeholder="t('project.commonScript.searchByNameAndId')"
|
:placeholder="t('project.commonScript.searchByNameAndId')"
|
||||||
|
@ -43,11 +45,12 @@
|
||||||
<MsTag v-else>{{ t('project.commonScript.draft') }}</MsTag>
|
<MsTag v-else>{{ t('project.commonScript.draft') }}</MsTag>
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<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') }}
|
{{ t('common.edit') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
<MsTableMoreAction
|
<MsTableMoreAction
|
||||||
v-if="!record.internal"
|
v-if="!record.internal"
|
||||||
|
v-permission="['PROJECT_CUSTOM_FUNCTION:READ+DELETE']"
|
||||||
:list="actions"
|
:list="actions"
|
||||||
@select="(item:ActionsItem) => handleMoreActionSelect(item,record)"
|
@select="(item:ActionsItem) => handleMoreActionSelect(item,record)"
|
||||||
/></template>
|
/></template>
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper mb-6 flex justify-between">
|
<div class="wrapper mb-6 flex justify-between">
|
||||||
<span class="font-medium text-[var(--color-text-000)]">{{ t('project.basicInfo.basicInfo') }}</span>
|
<span class="font-medium text-[var(--color-text-000)]">{{ t('project.basicInfo.basicInfo') }}</span>
|
||||||
<a-button v-if="!projectDetail?.deleted" type="outline" @click="editHandler">{{
|
<a-button
|
||||||
t('project.basicInfo.edit')
|
v-if="!projectDetail?.deleted"
|
||||||
}}</a-button>
|
v-permission="['SYSTEM_PARAMETER_SETTING_BASE:READ+UPDATE']"
|
||||||
|
type="outline"
|
||||||
|
@click="editHandler"
|
||||||
|
>{{ t('project.basicInfo.edit') }}</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-info mb-6 h-[112px] bg-white p-1">
|
<div class="project-info mb-6 h-[112px] bg-white p-1">
|
||||||
<div class="inner-wrapper rounded-md p-4">
|
<div class="inner-wrapper rounded-md p-4">
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
import MsMenuPanel from '@/components/pure/ms-menu-panel/index.vue';
|
import MsMenuPanel from '@/components/pure/ms-menu-panel/index.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
|
|
||||||
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
@ -31,6 +32,21 @@
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
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([
|
const menuList = ref([
|
||||||
{
|
{
|
||||||
key: 'project',
|
key: 'project',
|
||||||
|
@ -50,12 +66,7 @@
|
||||||
level: 2,
|
level: 2,
|
||||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_MENU_MANAGEMENT,
|
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_MENU_MANAGEMENT,
|
||||||
},
|
},
|
||||||
{
|
...getProjectVersion(),
|
||||||
key: 'projectVersion',
|
|
||||||
title: t('project.permission.projectVersion'),
|
|
||||||
level: 2,
|
|
||||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_VERSION,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: 'memberPermission',
|
key: 'memberPermission',
|
||||||
title: t('project.permission.memberPermission'),
|
title: t('project.permission.memberPermission'),
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-4 grid grid-cols-4 gap-2">
|
<div class="mb-4 grid grid-cols-4 gap-2">
|
||||||
<div class="col-span-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>
|
<div>
|
||||||
<a-select v-model="roleIds" @change="changeSelect">
|
<a-select v-model="roleIds" @change="changeSelect">
|
||||||
|
@ -60,6 +62,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsRemoveButton
|
<MsRemoveButton
|
||||||
|
v-permission="['PROJECT_USER:READ+DELETE']"
|
||||||
position="br"
|
position="br"
|
||||||
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })"
|
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })"
|
||||||
:sub-title-tip="t('project.member.subTitle')"
|
:sub-title-tip="t('project.member.subTitle')"
|
||||||
|
@ -180,10 +183,12 @@
|
||||||
{
|
{
|
||||||
label: 'project.member.batchActionAddUserGroup',
|
label: 'project.member.batchActionAddUserGroup',
|
||||||
eventTag: 'batchAddUserGroup',
|
eventTag: 'batchAddUserGroup',
|
||||||
|
permission: ['PROJECT_USER:READ+ADD'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'project.member.batchActionRemove',
|
label: 'project.member.batchActionRemove',
|
||||||
eventTag: 'batchActionRemove',
|
eventTag: 'batchActionRemove',
|
||||||
|
permission: ['PROJECT_USER:READ+ADD'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,13 @@
|
||||||
<MsCard has-breadcrumb simple>
|
<MsCard has-breadcrumb simple>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<span v-if="isEnableOrdTemplate" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
|
<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') }}
|
{{ t('system.orgTemplate.createTemplate') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
|
@ -36,12 +42,22 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap items-center">
|
||||||
<MsButton @click="editTemplate(record.id)">{{ t('system.orgTemplate.edit') }}</MsButton>
|
<MsButton v-permission="['PROJECT_TEMPLATE:READ+UPDATE']" @click="editTemplate(record.id)">{{
|
||||||
<MsButton class="!mr-0" @click="copyTemplate(record.id)">{{ t('system.orgTemplate.copy') }}</MsButton>
|
t('system.orgTemplate.edit')
|
||||||
<a-divider v-if="!record.internal" direction="vertical" />
|
}}</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
|
<MsTableMoreAction
|
||||||
v-if="!record.internal"
|
v-if="!record.internal"
|
||||||
|
v-permission="['PROJECT_TEMPLATE:READ+DELETE']"
|
||||||
:list="moreActions"
|
:list="moreActions"
|
||||||
@select="(item) => handleMoreActionSelect(item, record)"
|
@select="(item) => handleMoreActionSelect(item, record)"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,9 +2,13 @@
|
||||||
<MsCard simple>
|
<MsCard simple>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<a-button class="mr-3" type="primary" @click="addOrEditMember('add')">{{
|
<a-button
|
||||||
t('organization.member.addMember')
|
v-permission="['ORGANIZATION_MEMBER:READ+ADD']"
|
||||||
}}</a-button>
|
class="mr-3"
|
||||||
|
type="primary"
|
||||||
|
@click="addOrEditMember('add')"
|
||||||
|
>{{ t('organization.member.addMember') }}</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
v-model="keyword"
|
v-model="keyword"
|
||||||
|
@ -78,8 +82,11 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ record }">
|
<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
|
<MsRemoveButton
|
||||||
|
v-permission="['ORGANIZATION_MEMBER:READ+DELETE']"
|
||||||
position="br"
|
position="br"
|
||||||
:title="t('organization.member.deleteMemberTip', { name: characterLimit(record.name) })"
|
:title="t('organization.member.deleteMemberTip', { name: characterLimit(record.name) })"
|
||||||
:sub-title-tip="t('organization.member.subTitle')"
|
:sub-title-tip="t('organization.member.subTitle')"
|
||||||
|
@ -213,10 +220,12 @@
|
||||||
{
|
{
|
||||||
label: 'organization.member.batchActionAddProject',
|
label: 'organization.member.batchActionAddProject',
|
||||||
eventTag: 'batchAddProject',
|
eventTag: 'batchAddProject',
|
||||||
|
permission: ['ORGANIZATION_MEMBER:READ+ADD'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'organization.member.batchActionAddUserGroup',
|
label: 'organization.member.batchActionAddUserGroup',
|
||||||
eventTag: 'batchAddUserGroup',
|
eventTag: 'batchAddUserGroup',
|
||||||
|
permission: ['ORGANIZATION_MEMBER:READ+ADD'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,11 +67,28 @@
|
||||||
@click="getValidateHandler(item)"
|
@click="getValidateHandler(item)"
|
||||||
>{{ t('organization.service.testLink') }}</a-button
|
>{{ 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
|
<a-button
|
||||||
v-if="item.config"
|
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"
|
type="outline"
|
||||||
class="arco-btn-outline--secondary"
|
class="arco-btn-outline--secondary"
|
||||||
size="mini"
|
size="mini"
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<span @click="fieldSetting">{{ t('system.orgTemplate.fieldSetting') }}</span>
|
<span @click="fieldSetting">{{ t('system.orgTemplate.fieldSetting') }}</span>
|
||||||
<a-divider direction="vertical" />
|
<a-divider direction="vertical" />
|
||||||
</span>
|
</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>
|
<span @click="templateManagement">{{ t('system.orgTemplate.TemplateManagement') }}</span>
|
||||||
<a-divider v-if="isEnableProject || props.cardItem.key === 'BUG'" direction="vertical" />
|
<a-divider v-if="isEnableProject || props.cardItem.key === 'BUG'" direction="vertical" />
|
||||||
</span>
|
</span>
|
||||||
|
@ -23,7 +23,11 @@
|
||||||
<span @click="workflowSetup">{{ t('system.orgTemplate.workflowSetup') }}</span>
|
<span @click="workflowSetup">{{ t('system.orgTemplate.workflowSetup') }}</span>
|
||||||
<a-divider v-if="isEnableProject && props.cardItem.key === 'BUG'" direction="vertical" />
|
<a-divider v-if="isEnableProject && props.cardItem.key === 'BUG'" direction="vertical" />
|
||||||
</span>
|
</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"
|
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect"
|
||||||
/></span>
|
/></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,13 @@
|
||||||
}}</a-alert>
|
}}</a-alert>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<span v-if="isEnableOrdTemplate" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
|
<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') }}
|
{{ t('system.orgTemplate.createTemplate') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
|
@ -28,12 +34,22 @@
|
||||||
{{ record.enableThirdPart ? t('system.orgTemplate.yes') : t('system.orgTemplate.no') }}
|
{{ record.enableThirdPart ? t('system.orgTemplate.yes') : t('system.orgTemplate.no') }}
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap items-center">
|
||||||
<MsButton @click="editTemplate(record.id)">{{ t('system.orgTemplate.edit') }}</MsButton>
|
<MsButton v-permission="['ORGANIZATION_TEMPLATE:READ+UPDATE']" @click="editTemplate(record.id)">{{
|
||||||
<MsButton class="!mr-0" @click="copyTemplate(record.id)">{{ t('system.orgTemplate.copy') }}</MsButton>
|
t('system.orgTemplate.edit')
|
||||||
<a-divider v-if="!record.internal" class="h-[12px]" direction="vertical" />
|
}}</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
|
<MsTableMoreAction
|
||||||
v-if="!record.internal"
|
v-if="!record.internal"
|
||||||
|
v-permission="['ORGANIZATION_TEMPLATE:READ+DELETE']"
|
||||||
:list="moreActions"
|
:list="moreActions"
|
||||||
@select="(item) => handleMoreActionSelect(item, record)"
|
@select="(item) => handleMoreActionSelect(item, record)"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<MsButton class="font-medium" @click="authChecking">{{
|
<MsButton v-permission="['SYSTEM_AUTH:READ+UPDATE']" class="font-medium" @click="authChecking">{{
|
||||||
t('system.authorized.authorityChecking')
|
t('system.authorized.authorityChecking')
|
||||||
}}</MsButton>
|
}}</MsButton>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -207,6 +207,7 @@
|
||||||
|
|
||||||
import { getBaseInfo, getEmailInfo, saveBaseInfo, saveEmailInfo, testEmail } from '@/api/modules/setting/config';
|
import { getBaseInfo, getEmailInfo, saveBaseInfo, saveEmailInfo, testEmail } from '@/api/modules/setting/config';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
import { desensitize } from '@/utils';
|
import { desensitize } from '@/utils';
|
||||||
import { validateEmail } from '@/utils/validate';
|
import { validateEmail } from '@/utils/validate';
|
||||||
|
|
||||||
|
@ -241,22 +242,33 @@
|
||||||
/**
|
/**
|
||||||
* 初始化基础信息
|
* 初始化基础信息
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const licenseStore = useLicenseStore();
|
||||||
async function initBaseInfo() {
|
async function initBaseInfo() {
|
||||||
try {
|
try {
|
||||||
baseloading.value = true;
|
baseloading.value = true;
|
||||||
const res = await getBaseInfo();
|
const res = await getBaseInfo();
|
||||||
baseInfo.value = { ...res };
|
baseInfo.value = { ...res };
|
||||||
baseInfoForm.value = { ...res };
|
baseInfoForm.value = { ...res };
|
||||||
baseInfoDescs.value = [
|
if (licenseStore.hasLicense()) {
|
||||||
{
|
baseInfoDescs.value = [
|
||||||
label: t('system.config.pageUrl'),
|
{
|
||||||
value: res.url,
|
label: t('system.config.pageUrl'),
|
||||||
},
|
value: res.url,
|
||||||
{
|
},
|
||||||
label: t('system.config.prometheus'),
|
{
|
||||||
value: res.prometheusHost,
|
label: t('system.config.prometheus'),
|
||||||
},
|
value: res.prometheusHost,
|
||||||
];
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
baseInfoDescs.value = [
|
||||||
|
{
|
||||||
|
label: t('system.config.pageUrl'),
|
||||||
|
value: res.url,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
import pageConfig from './components/pageConfig.vue';
|
import pageConfig from './components/pageConfig.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -29,12 +30,12 @@
|
||||||
const isInitAuthConfig = ref(activeTab.value === 'authConfig');
|
const isInitAuthConfig = ref(activeTab.value === 'authConfig');
|
||||||
const isInitMemoryCleanup = ref(activeTab.value === 'memoryCleanup');
|
const isInitMemoryCleanup = ref(activeTab.value === 'memoryCleanup');
|
||||||
const authConfigRef = ref<AuthConfigInstance | null>();
|
const authConfigRef = ref<AuthConfigInstance | null>();
|
||||||
const tabList = [
|
const tabList = ref([
|
||||||
{ key: 'baseConfig', title: t('system.config.baseConfig') },
|
{ key: 'baseConfig', title: t('system.config.baseConfig') },
|
||||||
{ key: 'pageConfig', title: t('system.config.pageConfig') },
|
{ key: 'pageConfig', title: t('system.config.pageConfig') },
|
||||||
{ key: 'authConfig', title: t('system.config.authConfig') },
|
{ key: 'authConfig', title: t('system.config.authConfig') },
|
||||||
{ key: 'memoryCleanup', title: t('system.config.memoryCleanup') },
|
{ key: 'memoryCleanup', title: t('system.config.memoryCleanup') },
|
||||||
];
|
]);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => activeTab.value,
|
() => activeTab.value,
|
||||||
|
@ -51,11 +52,15 @@
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const licenseStore = useLicenseStore();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (route.query.tab === 'authConfig' && route.query.id) {
|
if (route.query.tab === 'authConfig' && route.query.id) {
|
||||||
authConfigRef.value?.openAuthDetail(route.query.id as string);
|
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>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<div>
|
<div>
|
||||||
<a-row class="grid-demo mb-4" :gutter="10">
|
<a-row class="grid-demo mb-4" :gutter="10">
|
||||||
<a-col :span="5">
|
<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>
|
||||||
<a-col :span="5" :offset="9">
|
<a-col :span="5" :offset="9">
|
||||||
<a-select v-model="searchKeys.scene" @change="searchHandler">
|
<a-select v-model="searchKeys.scene" @change="searchHandler">
|
||||||
|
@ -108,12 +110,18 @@
|
||||||
</template>
|
</template>
|
||||||
<template #cell="{ record }">
|
<template #cell="{ record }">
|
||||||
<div class="flex">
|
<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)">{{
|
<MsButton v-if="record.enable" @click="disableHandler(record)">{{
|
||||||
t('system.plugin.tableDisable')
|
t('system.plugin.tableDisable')
|
||||||
}}</MsButton>
|
}}</MsButton>
|
||||||
<MsButton v-else @click="enableHandler(record)">{{ t('system.plugin.tableEnable') }}</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
</a-table-column>
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
<a-form-item v-if="isShowTypeItem" :label="t('system.resourcePool.type')" field="type" class="form-item">
|
<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-group v-model:model-value="form.type" type="button" @change="changeResourceType">
|
||||||
<a-radio value="Node">Node</a-radio>
|
<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-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="isShowNodeResources">
|
<template v-if="isShowNodeResources">
|
||||||
|
@ -356,6 +356,7 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useVisit from '@/hooks/useVisit';
|
import useVisit from '@/hooks/useVisit';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
import useLicenseStore from '@/store/modules/setting/license';
|
||||||
import { downloadStringFile, sleep } from '@/utils';
|
import { downloadStringFile, sleep } from '@/utils';
|
||||||
import { scrollIntoView } from '@/utils/dom';
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
|
@ -363,6 +364,8 @@
|
||||||
|
|
||||||
import { getYaml, job, YamlType } from './template';
|
import { getYaml, job, YamlType } from './template';
|
||||||
|
|
||||||
|
const licenseStore = useLicenseStore();
|
||||||
|
const isXpack = computed(() => licenseStore.hasLicense());
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -399,7 +402,7 @@
|
||||||
const form = ref({ ...defaultForm });
|
const form = ref({ ...defaultForm });
|
||||||
const formRef = ref<FormInstance | null>(null);
|
const formRef = ref<FormInstance | null>(null);
|
||||||
const orgOptions = ref<SelectOptionData>([]);
|
const orgOptions = ref<SelectOptionData>([]);
|
||||||
const useList = [
|
const useList = ref([
|
||||||
{
|
{
|
||||||
label: 'system.resourcePool.usePerformance',
|
label: 'system.resourcePool.usePerformance',
|
||||||
value: 'performance',
|
value: 'performance',
|
||||||
|
@ -412,11 +415,14 @@
|
||||||
label: 'system.resourcePool.useUI',
|
label: 'system.resourcePool.useUI',
|
||||||
value: 'UI',
|
value: 'UI',
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
const defaultGrid = 'http://selenium-hub:4444';
|
const defaultGrid = 'http://selenium-hub:4444';
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
orgOptions.value = await getSystemOrgOption();
|
orgOptions.value = await getSystemOrgOption();
|
||||||
|
if (!isXpack.value) {
|
||||||
|
useList.value = useList.value.filter((item) => item.value === 'API');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function initPoolInfo() {
|
async function initPoolInfo() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MsCard :loading="loading" simple>
|
<MsCard :loading="loading" simple>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<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') }}
|
{{ t('system.resourcePool.createPool') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
|
@ -19,10 +19,10 @@
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<MsButton @click="editPool(record)">{{ t('system.resourcePool.editPool') }}</MsButton>
|
<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') }}
|
{{ t('system.resourcePool.tableDisable') }}
|
||||||
</MsButton>
|
</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>
|
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
|
||||||
</template>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
|
@ -83,7 +83,6 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const columns: MsTableColumn = [
|
const columns: MsTableColumn = [
|
||||||
{
|
{
|
||||||
title: 'system.resourcePool.tableColumnName',
|
title: 'system.resourcePool.tableColumnName',
|
||||||
|
|
|
@ -66,9 +66,13 @@
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsButton>{{ t('testPlan.testPlanIndex.execution') }}</MsButton>
|
<MsButton>{{ t('testPlan.testPlanIndex.execution') }}</MsButton>
|
||||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
<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>
|
<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>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded-2xl bg-white">
|
<div class="rounded-2xl bg-white">
|
||||||
<div class="p-[24px] pb-[16px]">
|
<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') }}
|
{{ t('testPlan.testPlanIndex.createTestPlan') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue