fix(全局): 修改系统设置bug&用例管理需求bug调整

This commit is contained in:
xinxin.wu 2024-02-05 09:54:58 +08:00 committed by Craftsman
parent ff5421775d
commit f065ec134e
40 changed files with 461 additions and 201 deletions

View File

@ -811,7 +811,7 @@
}
/*** 徽标 ****/
.arco-badge-number {
.arco-badge-text {
height: 16px !important;
line-height: 16px;
text-align: center;

View File

@ -215,6 +215,9 @@
async (val) => {
if (val) {
personalMenus.value = [...copyPersonalMenus.value];
if (appStore.packageType === 'enterprise') {
getOrgList();
}
} else {
personalMenus.value.splice(1, 1);
}

View File

@ -39,7 +39,7 @@
</div>
<icon-plus-circle-fill
v-permission="['SYSTEM_USER_ROLE:READ+ADD']"
v-permission="props.addPermission"
class="cursor-pointer text-[rgb(var(--primary-7))]"
size="20"
@click="handleCreateUG(AuthScopeEnum.SYSTEM)"
@ -72,7 +72,7 @@
<div v-if="element.id === currentId && !element.internal" class="flex flex-row items-center gap-[8px]">
<MsMoreAction
v-if="element.type === systemType"
v-permission="['SYSTEM_USER_ROLE:READ+UPDATE']"
v-permission="props.updatePermission"
:list="addMemberActionItem"
@select="handleAddMember"
>
@ -81,6 +81,7 @@
</div>
</MsMoreAction>
<MsMoreAction
v-if="isSystemShowAll"
:list="systemMoreAction"
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.SYSTEM)"
>
@ -126,7 +127,7 @@
</div>
<icon-plus-circle-fill
v-permission="['ORGANIZATION_USER_ROLE:READ+ADD']"
v-permission="props.addPermission"
class="cursor-pointer text-[rgb(var(--primary-7))]"
size="20"
@click="orgUserGroupVisible = true"
@ -159,7 +160,7 @@
<div v-if="element.id === currentId && !element.internal" class="flex flex-row items-center gap-[8px]">
<MsMoreAction
v-if="element.type === systemType"
v-permission="['ORGANIZATION_USER_ROLE:READ+UPDATE']"
v-permission="props.updatePermission"
:list="addMemberActionItem"
@select="handleAddMember"
>
@ -168,7 +169,8 @@
</div>
</MsMoreAction>
<MsMoreAction
v-permission="['ORGANIZATION_USER_ROLE:READ+UPDATE']"
v-if="isOrdShowAll"
v-permission="props.updatePermission"
:list="orgMoreAction"
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.ORGANIZATION)"
>
@ -214,7 +216,7 @@
</div>
<icon-plus-circle-fill
v-permission="['PROJECT_GROUP:READ+ADD']"
v-permission="props.addPermission"
class="cursor-pointer text-[rgb(var(--primary-7))]"
size="20"
@click="projectUserGroupVisible = true"
@ -247,15 +249,16 @@
<div v-if="element.id === currentId && !element.internal" class="flex flex-row items-center gap-[8px]">
<MsMoreAction
v-if="element.type === systemType"
v-permission="['PROJECT_GROUP:READ+UPDATE']"
v-permission="props.updatePermission"
:list="addMemberActionItem"
@select="handleAddMember"
>
<div class="icon-button">
<div v-permission="props.updatePermission" class="icon-button">
<MsIcon type="icon-icon_add_outlined" size="16" />
</div>
</MsMoreAction>
<MsMoreAction
v-if="isProjectShowAll"
:list="projectMoreAction"
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.PROJECT)"
>
@ -306,6 +309,12 @@
(e: 'handleSelect', element: UserGroupItem): void;
(e: 'addUserSuccess', id: string): void;
}>();
const props = defineProps<{
addPermission: string[];
updatePermission: string[];
}>();
const appStore = useAppStore();
const { openModal } = useModal();
@ -354,14 +363,16 @@
return userGroupList.value.filter((ele) => ele.type === AuthScopeEnum.PROJECT);
});
const addMemberActionItem: ActionsItem[] = [{ label: 'system.userGroup.addMember', eventTag: 'addMember' }];
const addMemberActionItem: ActionsItem[] = [
{ label: 'system.userGroup.addMember', eventTag: 'addMember', permission: props.updatePermission },
];
const systemMoreAction: ActionsItem[] = [
{
label: 'system.userGroup.rename',
danger: false,
eventTag: 'rename',
permission: ['SYSTEM_USER_ROLE:READ+UPDATE'],
permission: props.updatePermission,
},
{
isDivider: true,
@ -378,7 +389,7 @@
label: 'system.userGroup.rename',
danger: false,
eventTag: 'rename',
permission: ['ORGANIZATION_USER_ROLE:READ+UPDATE'],
permission: props.updatePermission,
},
{
isDivider: true,
@ -387,7 +398,7 @@
label: 'system.userGroup.delete',
danger: true,
eventTag: 'delete',
permission: ['ORGANIZATION_USER_ROLE:READ+UPDATE'],
permission: ['ORGANIZATION_USER_ROLE:READ+DELETE'],
},
];
const projectMoreAction: ActionsItem[] = [
@ -395,7 +406,7 @@
label: 'system.userGroup.rename',
danger: false,
eventTag: 'rename',
permission: ['PROJECT_GROUP:READ+UPDATE'],
permission: props.updatePermission,
},
{
isDivider: true,
@ -404,7 +415,7 @@
label: 'system.userGroup.delete',
danger: true,
eventTag: 'delete',
permission: ['PROJECT_GROUP:READ+UPDATE'],
permission: ['PROJECT_GROUP:READ+DELETE'],
},
];
@ -550,6 +561,16 @@
emit('addUserSuccess', currentId.value);
}
};
const isSystemShowAll = computed(() => {
return hasAnyPermission([...props.updatePermission, 'SYSTEM_USER_ROLE:READ+DELETE']);
});
const isOrdShowAll = computed(() => {
return hasAnyPermission([...props.updatePermission, 'ORGANIZATION_USER_ROLE:READ+DELETE']);
});
const isProjectShowAll = computed(() => {
return hasAnyPermission([...props.updatePermission, 'PROJECT_GROUP:READ+DELETE']);
});
</script>
<style lang="less" scoped>

View File

@ -272,6 +272,8 @@
() => formRuleList.value,
(val) => {
if (val) emit('update:form-item', formRuleList.value);
fApi.value.refreshValidate();
fApi.value.clearValidateState();
},
{
deep: true,
@ -289,4 +291,13 @@
});
</script>
<style scoped></style>
<style scoped>
:deep(.arco-form-item-status-success .arco-select-view:not(.arco-select-view-disabled).arco-select-view-focus) {
border-color: var(--color-text-input-border);
background: none;
}
:deep(.arco-form-item-status-success .arco-select-view:not(.arco-select-view-disabled)) {
border-color: var(--color-text-input-border);
background: transparent;
}
</style>

View File

@ -209,7 +209,7 @@ export default function useTableProps<T>(
...loadListParams.value,
};
const data = await loadListFunc(tableQueryParams.value);
const tmpArr = data.list;
const tmpArr = data.list || data.data.list;
propsRes.value.data = tmpArr.map((item: MsTableDataItem<T>) => {
if (item.updateTime) {
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');

View File

@ -21,6 +21,7 @@ export default {
'menu.workbench': '工作台',
'menu.testPlan': '测试计划',
'menu.bugManagement': '缺陷管理',
'menu.bugManagement.bugDetail': '缺陷管理',
'menu.caseManagement': '用例管理',
'menu.apiTest': '接口测试',
'menu.apiTest.debug': '接口调试',

View File

@ -1,3 +1,4 @@
import { useRouter } from 'vue-router';
import { defineStore } from 'pinia';
import { Notification } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
@ -10,11 +11,13 @@ import { getPackageType, getSystemVersion } from '@/api/modules/system';
import { getMenuList } from '@/api/modules/user';
import defaultSettings from '@/config/settings.json';
import { useI18n } from '@/hooks/useI18n';
import { NO_PROJECT_ROUTE_NAME } from '@/router/constants';
import { watchStyle, watchTheme } from '@/utils/theme';
import type { PageConfig, PageConfigKeys, Style, Theme } from '@/models/setting/config';
import { ProjectListItem } from '@/models/setting/project';
import useUserStore from '../user';
import type { AppState } from './types';
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
@ -230,6 +233,8 @@ const useAppStore = defineStore('app', {
async initSystemPackage() {
try {
this.packageType = await getPackageType();
// await getPackageType();
// this.packageType = 'community';
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -14,6 +14,7 @@ const useFeatureCaseStore = defineStore('featureCase', {
recycleModulesCount: Record<string, any>; // 回收站模块数量
operatingState: boolean; // 操作状态
tabSettingList: TabItemType[]; // 详情tab
activeTab: string; // 激活tab
} => ({
moduleId: [],
caseTree: [],
@ -21,6 +22,7 @@ const useFeatureCaseStore = defineStore('featureCase', {
recycleModulesCount: {},
operatingState: false,
tabSettingList: [],
activeTab: 'detail',
}),
actions: {
// 设置选择moduleId
@ -65,6 +67,24 @@ const useFeatureCaseStore = defineStore('featureCase', {
getTab() {
return this.tabSettingList.filter((item) => item.enable);
},
// 设置激活tab
setActiveTab(active: string | number) {
this.activeTab = active as string;
},
// 设置菜单模块列表数量
setListCount(type: string, count = 0) {
this.tabSettingList = this.tabSettingList.map((item: any) => {
if (type === item.key) {
return {
...item,
total: count,
};
}
return {
...item,
};
});
},
},
});

View File

@ -2,6 +2,8 @@ import { defineStore } from 'pinia';
import { getLicenseInfo } from '@/api/modules/setting/authorizedManagement';
import useAppStore from '../app';
const useLicenseStore = defineStore('license', {
persist: true,
state: (): { status: string | null } => ({
@ -19,12 +21,16 @@ const useLicenseStore = defineStore('license', {
},
// license校验
async getValidateLicense() {
const appStore = useAppStore();
try {
const result = await getLicenseInfo();
if (!result || !result.status || !result.license || !result.license.count) {
return;
await appStore.initSystemPackage();
if (appStore.packageType === 'enterprise') {
const result = await getLicenseInfo();
if (!result || !result.status || !result.license || !result.license.count) {
return;
}
this.setLicenseStatus(result.status);
}
this.setLicenseStatus(result.status);
} catch (error) {
console.log(error);
}

View File

@ -36,7 +36,6 @@ const useTemplateStore = defineStore('template', {
if (currentOrgId.value && hasAnyPermission(['ORGANIZATION_TEMPLATE:READ'])) {
this.ordStatus = await getOrdTemplate(currentOrgId.value);
}
if (currentProjectId.value && hasAnyPermission(['PROJECT_TEMPLATE:READ'])) {
this.projectStatus = await getProTemplate(currentProjectId.value);
}

View File

@ -39,6 +39,7 @@ const useUserStore = defineStore('user', {
certification: undefined,
role: '',
userRolePermissions: [],
loginType: [],
}),
getters: {
@ -100,7 +101,7 @@ const useUserStore = defineStore('user', {
async getAuthentication() {
try {
const res = await getAuthenticationList();
console.log(res);
this.loginType = res;
} catch (error) {
console.log(error);
}

View File

@ -40,4 +40,5 @@ export interface UserState {
lastOrganizationId?: string;
lastProjectId?: string;
userRolePermissions?: UserRolePermissions[];
loginType: string[];
}

View File

@ -6,6 +6,7 @@
<template #caseStep="{ record }">
<a-textarea
v-if="record.showStep"
:ref="(el: refItem) => setStepRefMap(el, record)"
v-model="record.step"
size="mini"
:auto-size="true"
@ -26,6 +27,7 @@
<template #expectedResult="{ record }">
<a-textarea
v-if="record.showExpected"
:ref="(el: refItem) => setExpectedRefMap(el, record)"
v-model="record.expected"
:max-length="1000"
size="mini"
@ -78,6 +80,7 @@
import type { StepList } from '@/models/caseManagement/featureCase';
import { TableKeyEnum } from '@/enums/tableEnum';
type refItem = Element | ComponentPublicInstance | null;
const { t } = useI18n();
const props = withDefaults(
@ -240,13 +243,32 @@
});
};
const refStepMap: Record<string, any> = {};
function setStepRefMap(el: refItem, record: StepList) {
if (el) {
refStepMap[`${record.id}`] = el;
}
}
const expectedRefMap: Record<string, any> = {};
function setExpectedRefMap(el: refItem, record: StepList) {
if (el) {
expectedRefMap[`${record.id}`] = el;
}
}
//
function edit(record: StepList, type: string) {
if (props.isDisabled) return;
if (type === 'step') {
record.showStep = true;
nextTick(() => {
refStepMap[record.id]?.focus();
});
} else {
record.showExpected = true;
nextTick(() => {
expectedRefMap[record.id]?.focus();
});
}
}
@ -259,6 +281,7 @@
record.showExpected = false;
}
}
const tableRef = ref<InstanceType<typeof MsBaseTable> | null>(null);
watchEffect(() => {

View File

@ -39,6 +39,7 @@
import { createCaseRequest, updateCaseRequest } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useVisit from '@/hooks/useVisit';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { scrollIntoView } from '@/utils/dom';
@ -50,6 +51,7 @@
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const appStore = useAppStore();
const featureCaseStore = useFeatureCaseStore();
@ -61,6 +63,27 @@
fileList: [],
});
const initDetail = {
id: '',
templateId: '',
name: '',
prerequisite: '', // prerequisite
caseEditType: 'STEP', // /
steps: '',
textDescription: '',
expectedResult: '', //
description: '',
publicCase: false, //
moduleId: '',
versionId: '',
tags: [],
projectId: appStore.currentProjectId,
customFields: {}, //
relateFileMetaIds: [], // ID
deleteFileMetaIds: [], // id
unLinkFilesIds: [], // id
};
const title = ref('');
const loading = ref(false);
const isEdit = computed(() => !!route.query.id);
@ -69,8 +92,8 @@
const isContinueFlag = ref(false);
const isShowTip = ref<boolean>(true);
const createSuccessId = ref<string>('');
async function save(isReview: boolean) {
const caseModuleDetailRef = ref();
async function save(isReview: boolean, isContinue: boolean) {
try {
loading.value = true;
//
@ -88,6 +111,11 @@
caseDetailInfo.value.request.reviewId = route.query.reviewId;
}
const res = await createCaseRequest(caseDetailInfo.value);
if (isContinue) {
Message.success(t('caseManagement.featureCase.addSuccess'));
caseModuleDetailRef.value.resetForm();
return;
}
createSuccessId.value = res.data.id;
Message.success(route.params.mode === 'copy' ? t('ms.description.copySuccess') : t('common.addSuccess'));
featureCaseStore.setIsAlreadySuccess(true);
@ -122,8 +150,6 @@
}
}
const caseModuleDetailRef = ref();
//
function saveHandler(isContinue = false, isReview = false) {
const { caseFormRef, formRef, fApi } = caseModuleDetailRef.value;
@ -134,7 +160,7 @@
if (valid === true) {
formRef?.validate().then((result: any) => {
if (!result) {
return save(isReview);
return save(isReview, isContinue);
}
});
}

View File

@ -5,7 +5,7 @@
:width="1200"
:footer="false"
:mask="false"
:title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.id, name: detailInfo?.name })"
:title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.num, name: detailInfo?.name })"
:detail-id="props.detailId"
:detail-index="props.detailIndex"
:get-detail-func="getCaseDetail"
@ -104,7 +104,7 @@
class="ml-1"
:class="activeTab === tab.key ? 'active' : ''"
:count="1000"
:max-count="99"
:text="getTotal(tab.total)"
/> </div
></a-menu-item>
<a-menu-item key="setting">
@ -173,7 +173,7 @@
<!-- 自定义字段结束 -->
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateUser') }}</span>
<span>{{ detailInfo?.createUser }}</span>
<span>{{ detailInfo?.createUserName }}</span>
</div>
<div class="baseItem">
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateTime') }}</span>
@ -289,6 +289,7 @@
const activeTab = ref<string | number>('detail');
function clickMenu(key: string | number) {
activeTab.value = key;
featureCaseStore.setActiveTab(key);
switch (activeTab.value) {
case 'setting':
activeTab.value = 'detail';
@ -524,7 +525,10 @@
event: noticeUserIds.value.join(';') ? 'AT' : 'COMMENT', // (: COMMENT; @: AT; /@: REPLAY;)
};
await createCommentList(params);
commentRef.value.getAllCommentList();
if (activeTab.value === 'comments') {
commentRef.value.getAllCommentList();
}
Message.success(t('common.publishSuccessfully'));
} catch (error) {
console.log(error);
@ -539,6 +543,13 @@
tabDetailRef.value.handleOK();
}, 300);
function getTotal(total: number) {
if (total <= 99) {
return String(total);
}
return `${total}+`;
}
watch(
() => props.detailId,
(val) => {
@ -601,7 +612,7 @@
color: rgb(var(--danger-6));
}
}
:deep(.active .arco-badge-number) {
:deep(.active .arco-badge-text) {
background: rgb(var(--primary-5));
}
</style>

View File

@ -2,11 +2,13 @@
<template>
<!-- 用例表开始 -->
<MsAdvanceFilter
v-model:keyword="keyword"
:filter-config-list="filterConfigList"
:custom-fields-config-list="searchCustomFields"
:row-count="filterRowCount"
@keyword-search="fetchData"
@adv-search="handleAdvSearch"
@reset="fetchData"
>
<template #left>
<div class="text-[var(--color-text-1)]"
@ -34,10 +36,10 @@
@change="changeHandler"
>
<template #num="{ record, rowIndex }">
<span class="flex w-full" @click="showCaseDetail(record.id, rowIndex)">{{ record.num }}</span>
<a-button type="text" class="flex w-full" @click="showCaseDetail(record.id, rowIndex)">{{ record.num }}</a-button>
</template>
<template #name="{ record, rowIndex }">
<a-button type="text" class="px-0" @click="showCaseDetail(record.id, rowIndex)">{{ record.name }}</a-button>
<span type="text" class="px-0" @click="showCaseDetail(record.id, rowIndex)">{{ record.name }}</span>
</template>
<template #caseLevel="{ record }">
<caseLevel :case-level="getCaseLevels(record.customFields)" />
@ -969,7 +971,6 @@
const fetchData = (keywordStr = '') => {
setKeyword(keywordStr);
keyword.value = keywordStr;
getLoadListParams();
loadList();
};

View File

@ -579,16 +579,6 @@
console.log(error);
}
}
watchEffect(() => {
if (props.caseId) {
getCaseInfo();
} else {
initDefaultFields();
}
initSelectTree();
});
//
function getFilesParams() {
form.value.deleteFileMetaIds = deleteFileMetaIds.value;
@ -756,10 +746,32 @@
return data;
}
function resetForm() {
form.value = { ...initForm };
fileList.value = [];
caseFormRef.value?.resetFields();
}
const caseId = ref(props.caseId);
watchEffect(() => {
if (caseId.value) {
getCaseInfo();
} else {
initDefaultFields();
}
initSelectTree();
});
onBeforeMount(() => {
caseId.value = '';
});
defineExpose({
caseFormRef,
formRef,
fApi,
resetForm,
});
</script>

View File

@ -84,6 +84,7 @@
import { postTabletList } from '@/api/modules/project-management/menuManagement';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import type { TableQueryParams } from '@/models/common';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -91,6 +92,7 @@
import Message from '@arco-design/web-vue/es/message';
const appStore = useAppStore();
const featureCaseStore = useFeatureCaseStore();
const { t } = useI18n();
@ -255,14 +257,16 @@
currentSelectCase.value = caseTypeOptions.value[0].value;
}
function getFetch() {
async function getFetch() {
setLoadListParams({
keyword: keyword.value,
sourceId: props.caseId,
projectId: currentProjectId.value,
sourceType: currentSelectCase.value,
});
loadList();
await loadList();
const { msPagination } = propsRes.value;
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
}
async function searchCase() {

View File

@ -40,12 +40,14 @@
import { getDetailCaseReviewPage } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { TableKeyEnum } from '@/enums/tableEnum';
import { getReviewStatusClass, getStatusText } from '../utils';
import debounce from 'lodash-es/debounce';
const featureCaseStore = useFeatureCaseStore();
const { t } = useI18n();
const props = defineProps<{
@ -101,24 +103,17 @@
enableDrag: true,
});
function initData() {
async function initData() {
setLoadListParams({ keyword: keyword.value, caseId: props.caseId });
loadList();
await loadList();
const { msPagination } = propsRes.value;
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
}
const searchList = debounce(() => {
initData();
}, 100);
function getReviewStatus(status: string) {
switch (status) {
case 'UN_REVIEWED':
break;
default:
break;
}
}
onBeforeMount(() => {
initData();
});

View File

@ -83,6 +83,7 @@
import { useI18n } from '@/hooks/useI18n';
import useVisit from '@/hooks/useVisit';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { characterLimit } from '@/utils';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -90,7 +91,7 @@
const { t } = useI18n();
const appStore = useAppStore();
const featureCaseStore = useFeatureCaseStore();
const visitedKey = 'notRemindChangeHistoryTip';
const { addVisited } = useVisit(visitedKey);
const { getIsVisited } = useVisit(visitedKey);
@ -216,13 +217,15 @@
isShowTip.value = !getIsVisited();
};
function initData() {
async function initData() {
setLoadListParams({
projectId: appStore.currentProjectId,
sourceId: props.caseId,
module: 'FUNCTIONAL_CASE',
});
loadList();
await loadList();
const { msPagination } = propsRes.value;
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
}
onBeforeMount(() => {

View File

@ -44,7 +44,9 @@
} from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
const featureCaseStore = useFeatureCaseStore();
const { openModal } = useModal();
const { t } = useI18n();
@ -76,15 +78,24 @@
}
}
function getAllCommentList() {
function setCount(list: CommentItem[]) {
featureCaseStore.setListCount(featureCaseStore.activeTab, list.length);
}
async function getAllCommentList() {
switch (activeComment.value) {
case 'caseComment':
initCommentList();
await initCommentList();
setCount(commentList.value);
break;
case 'reviewComment':
initReviewCommentList();
await initCommentList();
setCount(reviewCommentList.value);
break;
case 'executiveComment':
await initCommentList();
setCount(commentList.value);
break;
default:

View File

@ -36,12 +36,13 @@
import { getDemandList } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import type { DemandItem } from '@/models/caseManagement/featureCase';
const appStore = useAppStore();
const pageConfig = computed(() => appStore.pageConfig);
const featureCaseStore = useFeatureCaseStore();
const { t } = useI18n();
const props = withDefaults(
@ -109,7 +110,9 @@
const initData = async () => {
setLoadListParams({ ...props.funParams });
loadList();
await loadList();
const { msPagination } = propsRes.value;
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
};
onMounted(() => {

View File

@ -28,11 +28,11 @@
<AddDemandModal v-model:visible="showAddModel" :case-id="props.caseId" :form="modelForm" @success="searchList()" />
<MsDrawer
v-model:visible="linkDemandDrawer"
:ok-disabled="tableSelected.length < 1"
:mask="false"
:title="t('caseManagement.featureCase.associatedDemand')"
:ok-text="t('caseManagement.featureCase.associated')"
:ok-loading="drawerLoading"
:ok-disabled="tableSelected.length < 1"
:width="960"
unmount-on-close
:show-continue="false"
@ -41,8 +41,8 @@
>
<div class="flex items-center justify-between">
<div
><span class="font-medium">XXXXXXXXX</span
><span class="ml-1 text-[var(--color-text-4)]">({{ propsRes.value }})</span></div
><span class="font-medium">{{ getPlatName() }}</span
><span class="ml-1 text-[var(--color-text-4)]">({{ propsRes?.msPagination?.total || 0 }})</span></div
>
<a-input-search
v-model="platformKeyword"
@ -61,6 +61,9 @@
<span>({{ (record.children || []).length || 0 }})</span></span
>
</template>
<template v-for="item in customFields" :key="item.slotName" #[item.dataIndex]="{ record }">
<span> {{ getSlotName(record, item) }} </span>
</template>
</ms-base-table>
</MsDrawer>
</div>
@ -68,16 +71,18 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Message } from '@arco-design/web-vue';
import { debounce } from 'lodash-es';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import type { MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import AddDemandModal from './addDemandModal.vue';
import AssociatedDemandTable from './associatedDemandTable.vue';
import { batchAssociationDemand, getThirdDemandList } from '@/api/modules/case-management/featureCase';
import { getCaseRelatedInfo } from '@/api/modules/project-management/menuManagement';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
@ -130,87 +135,49 @@
title: 'caseManagement.featureCase.tableColumnID',
slotName: 'demandId',
dataIndex: 'demandId',
showInTable: true,
width: 200,
showTooltip: true,
},
{
title: 'caseManagement.featureCase.tableColumnName',
slotName: 'demandName',
dataIndex: 'demandName',
width: 300,
},
{
title: 'caseManagement.featureCase.platformDemandState',
width: 300,
dataIndex: 'status',
showInTable: true,
showTooltip: true,
ellipsis: true,
},
{
title: 'caseManagement.featureCase.platformDemandHandler',
width: 300,
dataIndex: 'handler',
showInTable: true,
showTooltip: true,
ellipsis: true,
},
{
title: 'caseManagement.featureCase.IterationPlan',
width: 300,
dataIndex: 'iterationPlan',
showInTable: true,
showTooltip: true,
ellipsis: true,
},
// {
// title: 'caseManagement.featureCase.platformDemandState',
// width: 300,
// dataIndex: 'status',
// showTooltip: true,
// ellipsis: true,
// },
// {
// title: 'caseManagement.featureCase.platformDemandHandler',
// width: 300,
// dataIndex: 'handler',
// showTooltip: true,
// ellipsis: true,
// },
];
const fullColumns = ref<MsTableColumn>([]);
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(getThirdDemandList, {
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEMAND_PLATFORM,
columns,
rowKey: 'id',
columns: fullColumns.value,
rowKey: 'demandId',
scroll: { x: '100%' },
selectable: false,
selectable: true,
showSetting: false,
});
const showDrawer = ref<boolean>(false);
const drawerLoading = ref<boolean>(false);
const tableSelected = computed(() => {
const selectIds = [...propsRes.value.selectedKeys];
return propsRes.value.data.filter((item: any) => selectIds.indexOf(item.id) > -1);
return propsRes.value.data.filter((item: any) => selectIds.indexOf(item.demandId) > -1);
});
async function handleDrawerConfirm() {
const params = {
id: '',
caseId: props.caseId,
demandPlatform: '',
demandList: [
{
demandId: 'string',
parent: 'string',
demandName: 'string',
demandUrl: 'string',
},
],
};
try {
drawerLoading.value = true;
await batchAssociationDemand(params);
} catch (error) {
console.log(error);
} finally {
drawerLoading.value = false;
}
}
function handleDrawerCancel() {
showDrawer.value = false;
}
//
const linkDemandDrawer = ref<boolean>(false);
function associatedDemand() {
@ -229,19 +196,113 @@
resetSelector();
};
watch(
() => linkDemandDrawer.value,
(val) => {
if (val) {
resetSelector();
initData();
const tableRef = ref();
const customFields = ref<any[]>([]);
async function initColumn() {
try {
const res = await getThirdDemandList({
current: 1,
pageSize: 10,
projectId: currentProjectId.value,
});
customFields.value = (res.data.customHeaders || []).map((item: any) => {
return {
title: item.name,
slotName: item.id,
dataIndex: item.id,
width: 200,
options: item.options,
};
}) as any;
fullColumns.value = [...columns, ...customFields.value];
tableRef.value.initColumn(fullColumns.value);
} catch (error) {
console.log(error);
}
}
function getSlotName(record: any, item: MsTableColumnData) {
if (item?.options) {
const currentRecord = {
...record,
...record.customFields,
};
const currentValue = currentRecord[item.dataIndex as string];
const currentOptions = (JSON.parse(item.options) || []).find((it: any) => it.value === currentValue);
if (currentOptions) {
return currentOptions.text;
}
}
return record.customFields[item.dataIndex as string] || '-';
}
const platformInfo = ref<Record<string, any>>({});
async function handleDrawerConfirm() {
const demandList = tableSelected.value.map((item) => {
return {
demandId: item.demandId,
parent: item.parent,
demandName: item.demandName,
demandUrl: item.demandUrl,
};
});
const params = {
id: JSON.parse(platformInfo.value.demand_platform_config).zentaoId,
caseId: props.caseId,
demandPlatform: platformInfo.value.platform_key,
demandList,
};
try {
drawerLoading.value = true;
await batchAssociationDemand(params);
Message.success(t('caseManagement.featureCase.associatedSuccess'));
linkDemandDrawer.value = false;
demandRef.value.initData();
} catch (error) {
console.log(error);
} finally {
drawerLoading.value = false;
}
}
function handleDrawerCancel() {
linkDemandDrawer.value = false;
}
watch(
() => linkDemandDrawer.value,
async (val) => {
if (val) {
resetSelector();
await initColumn();
initData();
}
},
{
immediate: true,
}
);
// onMounted(() => {
// resetSelector();
// initData();
// });
onBeforeMount(async () => {
try {
const result = await getCaseRelatedInfo(currentProjectId.value);
if (result && result.platform_key) {
platformInfo.value = { ...result };
}
} catch (error) {
console.log(error);
}
});
function getPlatName() {
switch (platformInfo.value.platform_key) {
case 'zentao':
return t('caseManagement.featureCase.zentao');
default:
break;
}
}
</script>
<style scoped></style>

View File

@ -75,8 +75,11 @@
import { cancelPreOrPostCase, getDependOnCase } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { characterLimit } from '@/utils';
const featureCaseStore = useFeatureCaseStore();
const appStore = useAppStore();
export type types = 'preposition' | 'postPosition';
@ -145,21 +148,6 @@
enableDrag: false,
});
const cancelLoading = ref<boolean>(false);
//
async function cancelDependency(record: any) {
cancelLoading.value = true;
try {
await cancelPreOrPostCase(record.id);
Message.success(t('caseManagement.featureCase.cancelFollowSuccess'));
loadList();
} catch (error) {
console.log(error);
} finally {
cancelLoading.value = false;
}
}
function getParams() {
setLoadListParams({
projectId: currentProjectId.value,
@ -169,6 +157,28 @@
});
}
async function initData() {
getParams();
await loadList();
const { msPagination } = propsRes.value;
featureCaseStore.setListCount(featureCaseStore.activeTab, msPagination?.total || 0);
}
const cancelLoading = ref<boolean>(false);
//
async function cancelDependency(record: any) {
cancelLoading.value = true;
try {
await cancelPreOrPostCase(record.id);
Message.success(t('caseManagement.featureCase.cancelFollowSuccess'));
initData();
} catch (error) {
console.log(error);
} finally {
cancelLoading.value = false;
}
}
const showDrawer = ref<boolean>(false);
const drawerRef = ref();
//
@ -176,22 +186,19 @@
showDrawer.value = true;
drawerRef.value.initModules();
}
function successHandler() {
loadList();
initData();
}
watch(
() => showType.value,
() => {
getParams();
loadList();
initData();
}
);
onBeforeMount(() => {
getParams();
loadList();
initData();
});
</script>

View File

@ -251,4 +251,5 @@ export default {
'caseManagement.featureCase.associatedSuccess': 'Associated with success',
'caseManagement.featureCase.defectSource': 'defect Source',
'caseManagement.featureCase.sortSuccess': 'Sort successfully',
'caseManagement.featureCase.zentao': 'zentao',
};

View File

@ -246,4 +246,5 @@ export default {
'caseManagement.featureCase.associatedSuccess': '关联成功',
'caseManagement.featureCase.defectSource': '缺陷来源',
'caseManagement.featureCase.sortSuccess': '排序成功',
'caseManagement.featureCase.zentao': '禅道',
};

View File

@ -10,18 +10,16 @@
</div>
<div class="form mt-[32px] min-w-[416px]">
<div class="mb-7 text-[18px] font-medium text-[rgb(var(--primary-5))]">LDAP登录</div>
<div v-if="userInfo.authenticate === 'LOCAL'" class="mb-7 text-[18px] font-medium text-[rgb(var(--primary-5))]">{{
t('login.form.accountLogin')
}}</div>
<div
v-if="isShowLDAP && userInfo.authenticate !== 'LOCAL'"
class="mb-7 text-[18px] font-medium text-[rgb(var(--primary-5))]"
>{{ t('login.form.LDAPLogin') }}</div
>
<a-form ref="formRef" :model="userInfo" @submit="handleSubmit">
<!-- TOTO 第一版本暂时只考虑普通登录 -->
<!-- <a-form-item class="login-form-item" field="radio" hide-label>
<a-radio-group v-model="userInfo.authenticate" type="button">
<a-radio value="LOCAL">{{ t('login.form.normalLogin') }}</a-radio>
<a-radio value="LDAP">LDAP</a-radio>
<a-radio value="OAuth2">{{ t('login.form.oauth2Test') }}</a-radio>
<a-radio value="OIDC 90">OIDC 90</a-radio>
</a-radio-group>
</a-form-item> -->
<!-- TOTO 第一版本暂时只考虑普通登录&LDAP -->
<a-form-item
class="login-form-item"
field="username"
@ -53,7 +51,12 @@
<span class="text-xs font-normal text-[var(--color-text-4)]">{{ t('login.form.modeLoginMethods') }}</span>
</a-divider>
<div class="flex items-center justify-center">
<div class="loginType"> <svg-icon width="18px" height="18px" name="userLogin"></svg-icon></div>
<div v-if="userInfo.authenticate !== 'LDAP' && isShowLDAP" class="loginType" @click="switchLoginType('LDAP')">
<span class="type-text text-[10px]">LDAP</span>
</div>
<div v-if="userInfo.authenticate !== 'LOCAL'" class="loginType" @click="switchLoginType('LOCAL')">
<svg-icon width="18px" height="18px" name="userLogin"></svg-icon
></div>
<div class="loginType">
<span class="type-text text-[10px]">OIDC</span>
</div>
@ -68,7 +71,7 @@
</template>
<script lang="ts" setup>
import { computed, reactive, ref } from 'vue';
import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useStorage } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
@ -113,12 +116,20 @@
password: '',
});
const userInfo = reactive({
const userInfo = ref<{
authenticate: string;
username: string;
password: string;
}>({
authenticate: 'LOCAL',
username: '',
password: '',
});
function switchLoginType(type: string) {
userInfo.value.authenticate = type;
}
const handleSubmit = async ({
errors,
values,
@ -133,7 +144,7 @@
await userStore.login({
username: encrypted(values.username),
password: encrypted(values.password),
authenticate: values.authenticate,
authenticate: userInfo.value.authenticate,
} as LoginData);
Message.success(t('login.form.login.success'));
const { rememberPassword } = loginConfig.value;
@ -161,14 +172,13 @@
}
}
};
onMounted(async () => {
// userStore.getAuthenticationList();
// try {
// const res = await getAuthenticationList();
// console.log(res);
// } catch (error) {
// console.log(error);
// }
const isShowLDAP = computed(() => {
return userStore.loginType.includes('LDAP');
});
onMounted(() => {
userStore.getAuthentication();
});
</script>

View File

@ -13,4 +13,6 @@ export default {
'login.form.normalLogin': 'Normal login',
'login.form.oauth2Test': 'OAuth2 Test',
'login.form.modeLoginMethods': 'More',
'login.form.accountLogin': 'Account login',
'login.form.LDAPLogin': 'LDAP',
};

View File

@ -13,4 +13,6 @@ export default {
'login.form.normalLogin': '普通登录',
'login.form.oauth2Test': 'OAuth2 测试',
'login.form.modeLoginMethods': '更多登录方式',
'login.form.accountLogin': '账号登录',
'login.form.LDAPLogin': 'LDAP登录',
};

View File

@ -55,12 +55,13 @@
level: 2,
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_MENU_MANAGEMENT,
},
{
key: 'projectVersion',
title: t('project.permission.projectVersion'),
level: 2,
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_VERSION,
},
// TODO
// {
// key: 'projectVersion',
// title: t('project.permission.projectVersion'),
// level: 2,
// name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_PERMISSION_VERSION,
// },
{
key: 'memberPermission',
title: t('project.permission.memberPermission'),

View File

@ -154,7 +154,6 @@
{ ...form, DEMAND_PLATFORM_CONFIG: JSON.stringify(formData) },
currentProjectId.value
);
Message.success(t('common.createSuccess'));
handleCancel(true);
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -383,7 +383,7 @@
);
onMounted(() => {
setBreadText();
// setBreadText();
getClassifyField();
if (!isEdit.value) {
selectData.value = totalTemplateField.value.filter((item) => item.internal);

View File

@ -156,10 +156,8 @@
const result = await configScript(cuurentPluginId);
formRules.value = [...result];
if (type.value === 'edit') {
// fApi.value.nextTick(() => {
fApi.value.setValue({ ...formItem.value.configuration });
fApi.value.refresh();
// });
}
} catch (error) {
console.log(error);

View File

@ -90,7 +90,7 @@
</a-form-item>
<!-- 日期和数值 -->
<a-form-item
v-if="showDateOrNumber"
v-if="showDateOrNumber?.length"
field="selectFormat"
:label="
fieldForm.type === 'NUMBER' ? t('system.orgTemplate.numberFormat') : t('system.orgTemplate.dateFormat')
@ -180,8 +180,10 @@
//
const showDateOrNumber = computed(() => {
selectFormat.value = getFieldType(fieldForm.value.type)[0].value;
if (fieldForm.value.type) return getFieldType(fieldForm.value.type);
if (getFieldType(fieldForm.value.type)[0]) {
selectFormat.value = getFieldType(fieldForm.value.type)[0]?.value;
if (fieldForm.value.type) return getFieldType(fieldForm.value.type);
}
});
// -1.

View File

@ -66,8 +66,10 @@ export function getFieldType(selectFieldType: FormItemType): { label: string; va
switch (selectFieldType) {
case 'DATE':
return dateOptions;
default:
case 'NUMBER':
return numberTypeOptions;
default:
return [];
}
}

View File

@ -369,7 +369,7 @@
);
onMounted(() => {
setBreadText();
// setBreadText();
getClassifyField();
if (!isEdit.value) {
selectData.value = totalTemplateField.value.filter((item) => item.internal);

View File

@ -2,7 +2,13 @@
<div class="card">
<MsSplitBox v-model:width="leftWidth" @expand-change="handleCollapse">
<template #first>
<UserGroupLeft ref="ugLeftRef" @handle-select="handleSelect" @add-user-success="handleAddMember" />
<UserGroupLeft
ref="ugLeftRef"
:add-permission="['ORGANIZATION_USER_ROLE:READ+ADD']"
:update-permission="['ORGANIZATION_USER_ROLE:READ+UPDATE']"
@handle-select="handleSelect"
@add-user-success="handleAddMember"
/>
</template>
<template #second>
<div class="p-[24px]">

View File

@ -6,7 +6,7 @@
</div>
<a-radio-group v-model:model-value="activeType" type="button">
<a-radio value="log">{{ t('system.config.memoryCleanup.log') }}</a-radio>
<a-radio v-xpack value="history">{{ t('system.config.memoryCleanup.history') }}</a-radio>
<a-radio value="history">{{ t('system.config.memoryCleanup.history') }}</a-radio>
</a-radio-group>
<template v-if="activeType === 'log'">
<div class="mb-[8px] mt-[16px] flex items-center">

View File

@ -10,7 +10,11 @@
<template #title>
<span v-if="isEdit">
{{ t('system.organization.updateOrganization') }}
<span class="text-[var(--color-text-4)]">({{ props.currentOrganization?.name }})</span>
<a-tooltip :content="props.currentOrganization?.name" position="right">
<span class="font-normal text-[var(--color-text-4)]"
>({{ characterLimit(props.currentOrganization?.name) }})</span
>
</a-tooltip>
</span>
<span v-else>
{{ t('system.organization.createOrganization') }}
@ -69,6 +73,7 @@
import { createOrUpdateOrg } from '@/api/modules/setting/organizationAndProject';
import { useI18n } from '@/hooks/useI18n';
import { characterLimit } from '@/utils';
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';

View File

@ -2,7 +2,13 @@
<div class="card">
<MsSplitBox v-model:width="leftWidth" @expand-change="handleCollapse">
<template #first>
<UserGroupLeft ref="ugLeftRef" @handle-select="handleSelect" @add-user-success="handleAddMember" />
<UserGroupLeft
ref="ugLeftRef"
:add-permission="['SYSTEM_USER_ROLE:READ+ADD']"
:update-permission="['SYSTEM_USER_ROLE:READ+UPDATE']"
@handle-select="handleSelect"
@add-user-success="handleAddMember"
/>
</template>
<template #second>
<div class="p-[24px]">