fix(all): 修复bugs

This commit is contained in:
baiqi 2024-04-17 19:51:35 +08:00 committed by 刘瑞斌
parent 6a6bcc381f
commit e64630f330
36 changed files with 548 additions and 475 deletions

View File

@ -175,7 +175,7 @@ export function addRepositoryFile(data: AddRepositoryFileParams) {
// 更新存储库文件 // 更新存储库文件
export function updateRepositoryFile(id: string) { export function updateRepositoryFile(id: string) {
return MSR.get({ url: UpdateRepositoryFileUrl, params: id }); return MSR.get<string>({ url: UpdateRepositoryFileUrl, params: id });
} }
// 获取存储库信息 // 获取存储库信息

View File

@ -273,3 +273,13 @@
} }
} }
} }
/** 表格 **/
.ms-table--special-small() {
:deep(.arco-table-cell) {
padding: 8px;
}
:deep(#ms-table-footer-wrapper) {
margin-top: 8px;
}
}

View File

@ -14,12 +14,8 @@
</div> </div>
</template> </template>
<div class="flex h-full w-full"> <div class="flex h-full w-full">
<div class="h-full w-[180px] bg-white"> <div class="flex h-full w-[180px] flex-col bg-white">
<a-menu <a-menu class="ms-message-menu" :default-selected-keys="[defaultModule]" @menu-item-click="clickModule">
class="mr-[16px] w-[180px] min-w-[180px] bg-white p-[4px]"
:default-selected-keys="[defaultModule]"
@menu-item-click="clickModule"
>
<a-menu-item :key="'all'"> <a-menu-item :key="'all'">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span>{{ t('ms.message.all') }}</span> <span>{{ t('ms.message.all') }}</span>
@ -33,16 +29,18 @@
</div> </div>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<div class="case flex-1"> <div class="mt-auto">
<a-divider direction="horizontal" margin="8px" /> <div
<div class="flex items-center px-[20px]" :class="{ clickable: 'hovered' }" @click="openMessageManage"> class="flex cursor-pointer items-center border-t border-[var(--color-text-n8)] px-[20px] py-[16px]"
<MsIcon type="icon-icon_setting_filled" class="folder-icon" /> @click="openMessageManage"
>
<MsIcon type="icon-icon_setting_filled" class="text-[var(--color-text-4)]" />
<div class="folder-name mx-[4px]">{{ t('ms.message.setting') }}</div> <div class="folder-name mx-[4px]">{{ t('ms.message.setting') }}</div>
</div> </div>
</div> </div>
</div> </div>
<a-divider direction="vertical" margin="8px"></a-divider> <a-divider direction="vertical" :margin="0"></a-divider>
<div class="flex-1 justify-between p-[24px]"> <div class="flex-1 justify-between overflow-x-hidden p-[16px]">
<a-radio-group v-model="position" type="button" @change="changeShowType"> <a-radio-group v-model="position" type="button" @change="changeShowType">
<a-radio value="all">{{ t('ms.message.list.all') }}</a-radio> <a-radio value="all">{{ t('ms.message.list.all') }}</a-radio>
<a-radio value="mentioned_me">{{ t('ms.message.list.me', { var: '@' }) }}</a-radio> <a-radio value="mentioned_me">{{ t('ms.message.list.me', { var: '@' }) }}</a-radio>
@ -54,7 +52,7 @@
<MsIcon type="icon-icon_logs_outlined" class="mr-1 font-[16px] text-[rgb(var(--primary-5))]" /> <MsIcon type="icon-icon_logs_outlined" class="mr-1 font-[16px] text-[rgb(var(--primary-5))]" />
{{ t('ms.message.make.as.read') }} {{ t('ms.message.make.as.read') }}
</a-button> </a-button>
<div class="mt-[26px] flex"> <div class="mt-[16px] flex">
<MsList <MsList
v-model:data="messageHistoryList" v-model:data="messageHistoryList"
mode="remote" mode="remote"
@ -69,112 +67,68 @@
@reach-bottom="handleReachBottom" @reach-bottom="handleReachBottom"
> >
<template #item="{ item }"> <template #item="{ item }">
<span class="p-[23px]"> <div v-if="item.type === 'MENTIONED_ME'" class="ms-message-item">
<div v-if="item.type === 'MENTIONED_ME'"> <div class="flex items-center">
<div class="flex items-center"> <MSAvatar v-if="item.avatar" :avatar="item.avatar" :word="item.userName" />
<MSAvatar :avatar="item.avatar" /> <div class="ml-[8px] flex">
<div class="ml-[8px] flex"> <div class="text-[var(--color-text-2)]">
<a-tooltip v-if="translateTextToPX(item.subject) > 300"> {{ item.subject }}
<template #content>
<span>
{{ item.subject }}
</span>
</template>
<div class="one-line-text font-medium text-[var(--color-text-2)]" style="max-width: 300px">{{
item.subject
}}</div>
</a-tooltip>
<div v-else class="font-medium text-[var(--color-text-2)]">{{ item.subject }}</div>
<div class="font-medium text-[rgb(var(--primary-5))]"
>&nbsp;&nbsp;{{ t('ms.message.me', { var: '@' }) }}</div
>
</div> </div>
</div> <div class="font-medium text-[rgb(var(--primary-5))]"
<div class="ml-[50px] flex items-center"> >&nbsp;&nbsp;{{ t('ms.message.me', { var: '@' }) }}</div
<a-tooltip v-if="translateTextToPX(item.userName) > 300"> >
<template #content>
<span>
{{ item.userName }}
</span>
</template>
<div class="one-line-text font-medium text-[var(--color-text-2)]" style="max-width: 300px">{{
item.userName
}}</div>
</a-tooltip>
<div v-else class="font-medium text-[var(--color-text-2)]">{{ item.userName }}</div>
<div class="font-medium text-[var(--color-text-2)]">{{ item.subject }}</div>
<MsButton @click="handleNameClick(item)">
<a-tooltip :content="item.resourceName" :mouse-enter-delay="300">
<div class="one-line-text max-w-[300px]">
{{ item.resourceName }}
</div>
</a-tooltip>
</MsButton>
</div>
<div class="ml-[50px] flex items-center">
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</div> </div>
</div> </div>
<div v-else> <div class="flex items-center">
<div class="flex items-center"> <div class="text-[var(--color-text-2)]">{{ item.content.split(':')[0] }}</div>
<a-badge v-if="item.status === 'UNREAD'" :count="9" dot :offset="[6, -2]"> <div v-if="item.operation.includes('DELETE')" class="text-[var(--color-text-1)]">
<a-tooltip v-if="translateTextToPX(item.subject) > 300"> {{ item.resourceName }}
<template #content> </div>
<span> <MsButton v-else @click="handleNameClick(item)">
{{ item.subject }} <a-tooltip :content="item.resourceName" :mouse-enter-delay="300">
</span> <div class="one-line-text">
</template> {{ item.resourceName }}
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{ </div>
item.subject </a-tooltip>
}}</div> </MsButton>
</a-tooltip> </div>
<div v-else class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{ <div class="flex items-center text-[var(--color-text-4)]">
item.subject {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
}}</div> </div>
</div>
<div v-else class="ms-message-item">
<MSAvatar v-if="item.avatar" :avatar="item.avatar" :word="item.userName" />
<div class="ml-[8px] flex flex-col">
<div class="flex items-center overflow-x-hidden">
<a-badge v-if="item.status === 'UNREAD'" :count="9" dot :offset="[6, -2]" class="w-full">
<div class="font-medium leading-[22px] text-[var(--color-text-1)]">
{{ item.subject }}
</div>
</a-badge> </a-badge>
<a-tooltip v-if="item.status === 'READ' && translateTextToPX(item.subject) > 300"> <a-tooltip v-else-if="item.status === 'READ'" :content="item.subject">
<template #content> <div class="font-medium leading-[22px] text-[var(--color-text-1)]">
<span> {{ item.subject }}
{{ item.subject }} </div>
</span>
</template>
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{
item.subject
}}</div>
</a-tooltip> </a-tooltip>
<div
v-if="item.status === 'READ' && translateTextToPX(item.subject) <= 300"
class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]"
>
{{ item.subject }}</div
>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<a-tooltip v-if="translateTextToPX(item.userName) > 300"> <div class="text-[var(--color-text-2)]">{{ item.content.split(':')[0] }}</div>
<template #content> <div v-if="item.operation.includes('DELETE')" class="text-[var(--color-text-1)]">
<span> {{ item.resourceName }}
{{ item.userName }} </div>
</span> <MsButton v-else @click="handleNameClick(item)">
</template>
<div class="one-line-text font-medium text-[var(--color-text-2)]" style="max-width: 300px">{{
item.userName
}}</div>
</a-tooltip>
<div v-else class="font-medium text-[var(--color-text-2)]">{{ item.userName }}</div>
<div class="font-medium text-[var(--color-text-2)]">{{ item.subject }}</div>
<MsButton @click="handleNameClick(item)">
<a-tooltip :content="item.resourceName" :mouse-enter-delay="300"> <a-tooltip :content="item.resourceName" :mouse-enter-delay="300">
<div class="one-line-text max-w-[300px]"> <div class="one-line-text">
{{ item.resourceName }} {{ item.resourceName }}
</div> </div>
</a-tooltip> </a-tooltip>
</MsButton> </MsButton>
</div> </div>
<div class="flex items-center"> <div class="flex items-center text-[var(--color-text-4)]">
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }} {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</div> </div>
</div> </div>
</span> </div>
</template> </template>
</MsList> </MsList>
</div> </div>
@ -204,31 +158,37 @@
} from '@/api/modules/message'; } from '@/api/modules/message';
import { getMessageList } from '@/api/modules/project-management/messageManagement'; import { getMessageList } from '@/api/modules/project-management/messageManagement';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import usePathMap from '@/hooks/usePathMap'; import useOpenNewPage from '@/hooks/useOpenNewPage';
import { translateTextToPX } from '@/utils/css'; import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
import { MessageItem } from '@/models/projectManagement/message'; import { MessageItem } from '@/models/projectManagement/message';
import { ProjectManagementRouteEnum } from '@/enums/routeEnum'; import { MessageResourceType } from '@/enums/messageEnum';
import {
ApiTestRouteEnum,
BugManagementRouteEnum,
CaseManagementRouteEnum,
ProjectManagementRouteEnum,
} from '@/enums/routeEnum';
import useAppStore from '../../../store/modules/app';
import useUserStore from '../../../store/modules/user';
const options = ref<OptionItem[]>([]);
const messageHistoryList = ref<MessageHistoryItem[]>([]);
const appStore = useAppStore();
const userStore = useUserStore();
const projectId = ref<string>(appStore.currentProjectId);
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:visible', val: boolean): void; (e: 'update:visible', val: boolean): void;
}>(); }>();
const appStore = useAppStore();
const userStore = useUserStore();
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const { jumpRouteByMapKey, getRouteMapByAlias } = usePathMap(); const { openNewPage } = useOpenNewPage();
const innerVisible = useVModel(props, 'visible', emit); const innerVisible = useVModel(props, 'visible', emit);
const projectId = ref<string>(appStore.currentProjectId);
const position = ref('all'); const position = ref('all');
const options = ref<OptionItem[]>([]);
const messageHistoryList = ref<MessageHistoryItem[]>([]);
const noMoreData = ref<boolean>(false); const noMoreData = ref<boolean>(false);
const moduleList = ref<MessageItem[]>([]); const moduleList = ref<MessageItem[]>([]);
const defaultModule = ref<string>('all'); const defaultModule = ref<string>('all');
@ -330,7 +290,7 @@
} }
// //
function changeShowType(value: string | number | boolean, ev: Event) { function changeShowType(value: string | number | boolean) {
messageHistoryList.value = []; messageHistoryList.value = [];
pageNation.value.current = 1; pageNation.value.current = 1;
loadMessageHistoryList(value as string, currentResourceType.value); loadMessageHistoryList(value as string, currentResourceType.value);
@ -381,21 +341,34 @@
return count; return count;
} }
const resourceTypeRouteMap = {
[MessageResourceType.BUG_TASK]: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
[MessageResourceType.BUG_SYNC_TASK]: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
[MessageResourceType.FUNCTIONAL_CASE_TASK]: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
[MessageResourceType.CASE_REVIEW_TASK]: CaseManagementRouteEnum.CASE_MANAGEMENT_REVIEW_DETAIL,
[MessageResourceType.API_DEFINITION_TASK]: ApiTestRouteEnum.API_TEST_MANAGEMENT,
[MessageResourceType.API_SCENARIO_TASK]: ApiTestRouteEnum.API_TEST_SCENARIO,
};
// //
function handleNameClick(item: MessageHistoryItem) { function handleNameClick(item: MessageHistoryItem) {
const routeQuery: Record<string, any> = { const routeQuery: Record<string, any> = {
orgId: item.organizationId,
pId: item.projectId, pId: item.projectId,
id: item.resourceId, id: item.resourceId,
}; };
if (item.organizationId === 'SYSTEM') {
delete routeQuery.organizationId;
}
if (item.projectId === 'SYSTEM' || item.projectId === 'ORGANIZATION') { if (item.projectId === 'SYSTEM' || item.projectId === 'ORGANIZATION') {
delete routeQuery.projectId; delete routeQuery.projectId;
} }
const routeMap = getRouteMapByAlias(item.resourceType); if (item.resourceType === MessageResourceType.API_DEFINITION_TASK) {
jumpRouteByMapKey(routeMap?.key, routeQuery, true); delete routeQuery.id;
if (item.operation.includes('CASE')) {
routeQuery.cId = item.resourceId;
} else if (!item.operation.includes('MOCK')) {
routeQuery.dId = item.resourceId;
}
}
const route = resourceTypeRouteMap[item.resourceType];
openNewPage(route, routeQuery);
} }
// //
@ -425,6 +398,28 @@
); );
</script> </script>
<style lang="less">
.ms-drawer {
.ms-message-menu {
@apply bg-white;
.arco-menu-inner {
padding: 16px;
}
}
.ms-message-item {
@apply flex;
padding: 8px;
&:not(:last-child) {
margin-bottom: 16px;
}
&:hover {
background-color: var(--color-text-n9);
}
}
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
.right-align { .right-align {
float: right; float: right;
@ -440,15 +435,4 @@
box-sizing: border-box; box-sizing: border-box;
line-height: 1.8715; line-height: 1.8715;
} }
.case {
/* 底部样式 */
position: fixed;
bottom: 0;
padding: 20px;
text-align: center;
}
.clickable {
cursor: pointer;
transition: background-color 0.3s ease;
}
</style> </style>

View File

@ -35,6 +35,12 @@
block-node block-node
default-expand-all default-expand-all
:selectable="false" :selectable="false"
:virtual-list-props="{
height: '100%',
threshold: 200,
fixedSize: true,
buffer: 15, // 10 padding
}"
@check="onSelect" @check="onSelect"
> >
<template #title="nodeData"> <template #title="nodeData">

View File

@ -554,7 +554,6 @@
@apply cursor-default; @apply cursor-default;
} }
} }
overflow-y: auto;
} }
} }
</style> </style>

View File

@ -13,7 +13,7 @@
:size="props.size" :size="props.size"
class="bg-[rgb(var(--primary-1))] text-[rgb(var(--primary-6))]" class="bg-[rgb(var(--primary-1))] text-[rgb(var(--primary-6))]"
> >
<slot>{{ userStore.name?.substring(0, 4) }}</slot> <slot>{{ props.word?.substring(0, 4) || userStore.name?.substring(0, 4) }}</slot>
</a-avatar> </a-avatar>
<a-avatar v-else :image-url="avatar" :size="props.size"></a-avatar> <a-avatar v-else :image-url="avatar" :size="props.size"></a-avatar>
</template> </template>
@ -29,6 +29,7 @@
defineProps<{ defineProps<{
avatar?: 'default' | 'word' | string; avatar?: 'default' | 'word' | string;
size?: number; size?: number;
word?: string; //
}>(), }>(),
{ {
avatar: 'default', avatar: 'default',

View File

@ -210,7 +210,8 @@
</a-table> </a-table>
<div <div
v-if="showBatchAction || !!attrs.showPagination" v-if="showBatchAction || !!attrs.showPagination"
class="mt-[16px] flex h-[32px] flex-row flex-nowrap items-center" id="ms-table-footer-wrapper"
class="mt-[16px] flex h-[32px] w-full flex-row flex-nowrap items-center overflow-hidden"
:class="{ 'justify-between': showBatchAction }" :class="{ 'justify-between': showBatchAction }"
> >
<span v-if="props.actionConfig && selectedCount > 0 && !showBatchAction" class="title text-[var(--color-text-2)]"> <span v-if="props.actionConfig && selectedCount > 0 && !showBatchAction" class="title text-[var(--color-text-2)]">
@ -225,21 +226,20 @@
class="flex-1" class="flex-1"
:select-row-count="selectedCount" :select-row-count="selectedCount"
:action-config="props.actionConfig" :action-config="props.actionConfig"
wrapper-id="ms-table-footer-wrapper"
@batch-action="handleBatchAction" @batch-action="handleBatchAction"
@clear="emit('clearSelector')" @clear="emit('clearSelector')"
/> />
</div> </div>
<div class="min-w-[500px]"> <ms-pagination
<ms-pagination v-if="!!attrs.showPagination"
v-if="!!attrs.showPagination" :size="props.paginationSize || 'small'"
size="small" v-bind="(attrs.msPagination as MsPaginationI)"
v-bind="(attrs.msPagination as MsPaginationI)" :simple="!!showBatchAction"
:simple="!!showBatchAction" :show-jumper="(attrs.msPagination as MsPaginationI).total / (attrs.msPagination as MsPaginationI).pageSize > 5"
:show-jumper="(attrs.msPagination as MsPaginationI).total / (attrs.msPagination as MsPaginationI).pageSize > 5" @change="pageChange"
@change="pageChange" @page-size-change="pageSizeChange"
@page-size-change="pageSizeChange" />
/>
</div>
</div> </div>
<ColumnSelector <ColumnSelector
v-if="props.showSetting" v-if="props.showSetting"
@ -316,6 +316,7 @@
spanAll?: boolean; spanAll?: boolean;
showSelectorAll?: boolean; showSelectorAll?: boolean;
firstColumnWidth?: number; // firstColumnWidth?: number; //
paginationSize?: 'small' | 'mini' | 'medium' | 'large';
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'batchAction', value: BatchActionParams, queryParams: BatchActionQueryParams): void; (e: 'batchAction', value: BatchActionParams, queryParams: BatchActionQueryParams): void;

View File

@ -8,7 +8,7 @@
class="ml-[12px]" class="ml-[12px]"
:class="{ :class="{
'arco-btn-outline--danger': element.danger, 'arco-btn-outline--danger': element.danger,
'ml-[16px]': idx === 0, 'ml-[8px]': idx === 0,
}" }"
type="outline" type="outline"
@click="handleSelect(element)" @click="handleSelect(element)"
@ -24,7 +24,7 @@
class="ml-[12px]" class="ml-[12px]"
:class="{ :class="{
'arco-btn-outline--danger': element.danger, 'arco-btn-outline--danger': element.danger,
'ml-[16px]': idx === 0, 'ml-[8px]': idx === 0,
}" }"
type="outline" type="outline"
@click="handleSelect" @click="handleSelect"
@ -41,7 +41,7 @@
</a-dropdown> </a-dropdown>
<!-- baseAction多菜单选择 --> <!-- baseAction多菜单选择 -->
</template> </template>
<div v-if="moreActionLength > 0" class="drop-down relative ml-[16px] inline-block"> <div v-if="moreActionLength > 0" class="drop-down relative ml-[8px] inline-block">
<a-dropdown position="tr" @select="handleSelect"> <a-dropdown position="tr" @select="handleSelect">
<a-button type="outline"><MsIcon type="icon-icon_more_outlined" /></a-button> <a-button type="outline"><MsIcon type="icon-icon_more_outlined" /></a-button>
<template #content> <template #content>
@ -58,7 +58,7 @@
</template> </template>
</a-dropdown> </a-dropdown>
</div> </div>
<a-button class="clear-btn ml-[16px]" type="text" @click="emit('clear')">{{ t('msTable.batch.clear') }}</a-button> <a-button class="clear-btn ml-[8px]" type="text" @click="emit('clear')">{{ t('msTable.batch.clear') }}</a-button>
</div> </div>
</template> </template>
@ -79,6 +79,7 @@
const props = defineProps<{ const props = defineProps<{
selectRowCount?: number; selectRowCount?: number;
actionConfig?: BatchActionConfig; actionConfig?: BatchActionConfig;
wrapperId: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'batchAction', value: BatchActionParams): void; (e: 'batchAction', value: BatchActionParams): void;
@ -120,7 +121,8 @@
computedStatus.value = true; computedStatus.value = true;
return; return;
} }
const wrapperWidth = getNodeWidth(refWrapper.value);
const wrapperWidth = (document.querySelector(`#${props.wrapperId}`)?.clientWidth || 0) - 370; // 370
const childNodeList = [].slice.call(refWrapper.value.children) as HTMLElement[]; const childNodeList = [].slice.call(refWrapper.value.children) as HTMLElement[];
@ -140,24 +142,24 @@
// title100px // title100px
totalWidth += 100; totalWidth += 100;
} else if (isDropDown) { } else if (isDropDown) {
// dropDown48px + MarginLeft 16px // dropDown48px + MarginLeft 8px
totalWidth += 64; totalWidth += 56;
} else if (isClearBtn) { } else if (isClearBtn) {
// 60px + MarginLeft 16px // 60px + MarginLeft 8px
totalWidth += 76; totalWidth += 68;
} else { } else {
// + marginLeft 16px // + marginLeft 8px
totalWidth += getNodeWidth(node) + 16; totalWidth += getNodeWidth(node) + 8;
menuItemIndex++;
} }
if (totalWidth > wrapperWidth) { if (totalWidth > wrapperWidth) {
const value = menuItemIndex - 1; const value = isClearBtn ? menuItemIndex - 1 : menuItemIndex - 2;
baseAction.value = allAction.value.slice(0, value); baseAction.value = allAction.value.slice(0, value);
moreAction.value = allAction.value.slice(value); moreAction.value = allAction.value.slice(value);
handleMoreActionLength(); handleMoreActionLength();
computedStatus.value = false; computedStatus.value = false;
return; return;
} }
menuItemIndex++;
} }
moreAction.value = props.actionConfig?.moreAction || []; moreAction.value = props.actionConfig?.moreAction || [];
baseAction.value = props.actionConfig?.baseAction || []; baseAction.value = props.actionConfig?.baseAction || [];

View File

@ -106,6 +106,7 @@ export interface MsTableProps<T> {
showJumpMethod?: boolean; // 是否展示跳转方法 showJumpMethod?: boolean; // 是否展示跳转方法
isSimpleSetting?: boolean; // 是否是简单的设置 isSimpleSetting?: boolean; // 是否是简单的设置
filterIconAlignLeft?: boolean; // 筛选图标是否靠左 filterIconAlignLeft?: boolean; // 筛选图标是否靠左
paginationSize?: 'small' | 'mini' | 'medium' | 'large';
[key: string]: any; [key: string]: any;
} }

View File

@ -0,0 +1,18 @@
export enum MessageResourceType {
TEST_PLAN_TASK = 'TEST_PLAN_TASK',
TEST_PLAN_REPORT_TASK = 'TEST_PLAN_REPORT_TASK',
BUG_TASK = 'BUG_TASK',
BUG_SYNC_TASK = 'BUG_SYNC_TASK',
FUNCTIONAL_CASE_TASK = 'FUNCTIONAL_CASE_TASK',
CASE_REVIEW_TASK = 'CASE_REVIEW_TASK',
API_DEFINITION_TASK = 'API_DEFINITION_TASK',
API_SCENARIO_TASK = 'API_SCENARIO_TASK',
API_REPORT_TASK = 'API_REPORT_TASK',
UI_SCENARIO_TASK = 'UI_SCENARIO_TASK',
UI_REPORT_TASK = 'UI_REPORT_TASK',
LOAD_TEST_TASK = 'LOAD_TEST_TASK',
LOAD_REPORT_TASK = 'LOAD_REPORT_TASK',
SCHEDULE_TASK = 'SCHEDULE_TASK',
}
export default {};

View File

@ -23,4 +23,3 @@ export enum ExecutionMethodsLabel {
API = 'project.taskCenter.interfaceCall', // 接口调用 API = 'project.taskCenter.interfaceCall', // 接口调用
BATCH = 'project.taskCenter.batchExecution', // 批量执行 BATCH = 'project.taskCenter.batchExecution', // 批量执行
} }
export default {};

View File

@ -49,7 +49,7 @@ const BugManagement: AppRouteRecordRaw = {
], ],
}, },
}, },
// 创建用例成功 // 缺陷创建成功
{ {
path: 'create-success', path: 'create-success',
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS, name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,

View File

@ -89,7 +89,10 @@
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<div class="text-[var(--color-text-1)]">{{ t('apiTestDebug.matchResult') }}</div> <div class="text-[var(--color-text-1)]">{{ t('apiTestDebug.matchResult') }}</div>
<a-tooltip :content-style="{ maxWidth: '500px' }"> <a-tooltip
v-if="expressionForm.extractType === RequestExtractExpressionEnum.REGEX"
:content-style="{ maxWidth: '500px' }"
>
<icon-question-circle <icon-question-circle
class="ml-[4px] cursor-pointer text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]" class="ml-[4px] cursor-pointer text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
size="16" size="16"

View File

@ -842,7 +842,7 @@
return item; return item;
}); });
const lastTwoIsSame = const lastTwoIsSame =
arr.length === 1 || (arr.length === 1 && !hasNoIdItem) ||
(arr.length >= 2 && filterKeyValParams([arr[arr.length - 2]], arr[arr.length - 1]).lastDataIsDefault); (arr.length >= 2 && filterKeyValParams([arr[arr.length - 2]], arr[arr.length - 1]).lastDataIsDefault);
if ( if (
hasNoIdItem && hasNoIdItem &&

View File

@ -18,7 +18,7 @@
type="line" type="line"
></a-switch> ></a-switch>
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPostCondition') }}</div> <div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPostCondition') }}</div>
<a-tooltip :content="t('apiTestDebug.openGlobalPostConditionTip')" position="left"> <a-tooltip :content="props.tipContent || t('apiTestDebug.openGlobalPostConditionTip')" position="left">
<icon-question-circle <icon-question-circle
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]" class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
size="16" size="16"
@ -47,6 +47,7 @@
isScenario?: boolean; // isScenario?: boolean; //
disabled?: boolean; disabled?: boolean;
sqlCodeEditorHeight?: string; sqlCodeEditorHeight?: string;
tipContent?: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:params', params: ExecuteConditionProcessor[]): void; (e: 'update:params', params: ExecuteConditionProcessor[]): void;

View File

@ -16,7 +16,7 @@
type="line" type="line"
></a-switch> ></a-switch>
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPrecondition') }}</div> <div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPrecondition') }}</div>
<a-tooltip :content="t('apiTestDebug.openGlobalPreconditionTip')" position="left"> <a-tooltip :content="props.tipContent || t('apiTestDebug.openGlobalPreconditionTip')" position="left">
<icon-question-circle <icon-question-circle
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]" class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
size="16" size="16"
@ -42,6 +42,7 @@
isScenario?: boolean; // isScenario?: boolean; //
disabled?: boolean; disabled?: boolean;
sqlCodeEditorHeight?: string; sqlCodeEditorHeight?: string;
tipContent?: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:config', params: ExecuteConditionConfig): void; (e: 'update:config', params: ExecuteConditionConfig): void;

View File

@ -1,6 +1,6 @@
<template> <template>
<div :class="['p-[0_16px_16px_16px]', props.class]"> <div :class="['p-[0_16px_8px_16px]', props.class]">
<div class="mb-[16px] flex items-center justify-end"> <div class="mb-[8px] flex items-center justify-end">
<div class="flex items-center gap-[8px]"> <div class="flex items-center gap-[8px]">
<a-input-search <a-input-search
v-model:model-value="keyword" v-model:model-value="keyword"
@ -507,7 +507,7 @@
]), ]),
showSelectAll: !props.readOnly, showSelectAll: !props.readOnly,
draggable: hasAnyPermission(['PROJECT_API_DEFINITION:READ+UPDATE']) ? { type: 'handle', width: 32 } : undefined, draggable: hasAnyPermission(['PROJECT_API_DEFINITION:READ+UPDATE']) ? { type: 'handle', width: 32 } : undefined,
heightUsed: 308, heightUsed: 256,
showSubdirectory: true, showSubdirectory: true,
}, },
(item) => ({ (item) => ({
@ -977,4 +977,5 @@
} }
} }
} }
.ms-table--special-small();
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="flex flex-1 flex-col overflow-hidden"> <div class="flex flex-1 flex-col overflow-hidden">
<div v-if="activeApiTab.id === 'all'" class="flex-1 pt-[16px]"> <div v-if="activeApiTab.id === 'all'" class="flex-1 pt-[8px]">
<apiTable <apiTable
:active-module="props.activeModule" :active-module="props.activeModule"
:offspring-ids="props.offspringIds" :offspring-ids="props.offspringIds"

View File

@ -25,7 +25,7 @@
<MsFormTable <MsFormTable
v-show="headerShowType === 'table'" v-show="headerShowType === 'table'"
:columns="headerColumns" :columns="headerColumns"
:data="previewDetail.headers || []" :data="previewDetail.headers?.filter((e) => e.key !== '') || []"
:selectable="false" :selectable="false"
/> />
<MsCodeEditor <MsCodeEditor
@ -64,7 +64,7 @@
<MsFormTable <MsFormTable
v-show="queryShowType === 'table'" v-show="queryShowType === 'table'"
:columns="queryRestColumns" :columns="queryRestColumns"
:data="previewDetail.query || []" :data="previewDetail.query?.filter((e) => e.key !== '') || []"
:selectable="false" :selectable="false"
/> />
<MsCodeEditor <MsCodeEditor
@ -102,7 +102,7 @@
</div> </div>
<MsFormTable <MsFormTable
v-show="restShowType === 'table'" v-show="restShowType === 'table'"
:columns="queryRestColumns" :columns="queryRestColumns?.filter((e) => e.key !== '')"
:data="previewDetail.rest || []" :data="previewDetail.rest || []"
:selectable="false" :selectable="false"
/> />
@ -204,7 +204,7 @@
<MsFormTable <MsFormTable
v-show="pluginShowType === 'table'" v-show="pluginShowType === 'table'"
:columns="pluginTableColumns" :columns="pluginTableColumns"
:data="pluginTableData" :data="pluginTableData?.filter((e) => e.key !== '')"
:selectable="false" :selectable="false"
/> />
<MsCodeEditor <MsCodeEditor
@ -315,7 +315,11 @@
{{ t('apiTestDebug.responseHeader') }} {{ t('apiTestDebug.responseHeader') }}
</div> </div>
</div> </div>
<MsFormTable :columns="responseHeaderColumns" :data="activeResponse?.headers || []" :selectable="false" /> <MsFormTable
:columns="responseHeaderColumns"
:data="activeResponse?.headers?.filter((e) => e.key !== '') || []"
:selectable="false"
/>
</div> </div>
</template> </template>
<a-spin v-else :loading="previewDetail.executeLoading" class="h-[calc(100%-45px)] w-full pb-[18px]"> <a-spin v-else :loading="previewDetail.executeLoading" class="h-[calc(100%-45px)] w-full pb-[18px]">
@ -657,12 +661,14 @@
const bodyTableData = computed(() => { const bodyTableData = computed(() => {
switch (previewDetail.value.body.bodyType) { switch (previewDetail.value.body.bodyType) {
case RequestBodyFormat.FORM_DATA: case RequestBodyFormat.FORM_DATA:
return (previewDetail.value.body.formDataBody?.formValues || []).map((e) => ({ return (previewDetail.value.body.formDataBody?.formValues || [])
...e, .map((e) => ({
value: e.paramType === RequestParamsType.FILE ? e.files?.map((file) => file.fileName).join('、') : e.value, ...e,
})); value: e.paramType === RequestParamsType.FILE ? e.files?.map((file) => file.fileName).join('、') : e.value,
}))
?.filter((e) => e.key !== '');
case RequestBodyFormat.WWW_FORM: case RequestBodyFormat.WWW_FORM:
return previewDetail.value.body.wwwFormBody?.formValues || []; return previewDetail.value.body.wwwFormBody?.formValues?.filter((e) => e.key !== '') || [];
case RequestBodyFormat.BINARY: case RequestBodyFormat.BINARY:
return [ return [
{ {

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="overflow-hidden p-[16px_22px]"> <div class="overflow-hidden p-[8px_22px]">
<div :class="['mb-[16px]', 'flex', 'items-center', props.isApi ? 'justify-between' : 'justify-end']"> <div :class="['mb-[8px]', 'flex', 'items-center', props.isApi ? 'justify-between' : 'justify-end']">
<a-button <a-button
v-show="props.isApi" v-show="props.isApi"
v-permission="['PROJECT_API_DEFINITION_CASE:READ+ADD']" v-permission="['PROJECT_API_DEFINITION_CASE:READ+ADD']"
@ -601,7 +601,7 @@
draggable: hasAnyPermission(['PROJECT_API_DEFINITION_CASE:READ+UPDATE']) draggable: hasAnyPermission(['PROJECT_API_DEFINITION_CASE:READ+UPDATE'])
? { type: 'handle', width: 32 } ? { type: 'handle', width: 32 }
: undefined, : undefined,
heightUsed: props.isApi ? 356 : 308, heightUsed: 256,
showSubdirectory: true, showSubdirectory: true,
}); });
const batchActions = { const batchActions = {
@ -1039,4 +1039,5 @@
} }
} }
} }
.ms-table--special-small();
</style> </style>

View File

@ -29,6 +29,7 @@
</template> </template>
</MsEditableTab> </MsEditableTab>
<environmentSelect <environmentSelect
v-show="activeApiTab.id !== 'all'"
ref="environmentSelectRef" ref="environmentSelectRef"
v-model:current-env="activeApiTab.environmentId" v-model:current-env="activeApiTab.environmentId"
:set-default-env="false" :set-default-env="false"

View File

@ -5,6 +5,7 @@
v-model:config="preProcessorConfig" v-model:config="preProcessorConfig"
:is-definition="false" :is-definition="false"
sql-code-editor-height="300px" sql-code-editor-height="300px"
:tip-content="t('apiScenario.openGlobalPreConditionTip')"
is-scenario is-scenario
@change="emit('change', true)" @change="emit('change', true)"
/> />
@ -16,6 +17,7 @@
:is-definition="false" :is-definition="false"
:layout="activeLayout" :layout="activeLayout"
sql-code-editor-height="300px" sql-code-editor-height="300px"
:tip-content="t('apiScenario.openGlobalPostConditionTip')"
is-scenario is-scenario
@change="emit('change', false)" @change="emit('change', false)"
/> />
@ -27,12 +29,16 @@
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue'; import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
import precondition from '@/views/api-test/components/requestComposition/precondition.vue'; import precondition from '@/views/api-test/components/requestComposition/precondition.vue';
import { useI18n } from '@/hooks/useI18n';
import { ExecuteConditionConfig } from '@/models/apiTest/common'; import { ExecuteConditionConfig } from '@/models/apiTest/common';
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'change', isChangePre: boolean): void; (e: 'change', isChangePre: boolean): void;
}>(); }>();
const { t } = useI18n();
const activeLayout = ref<'horizontal' | 'vertical'>('vertical'); const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
const preProcessorConfig = defineModel<ExecuteConditionConfig>('preProcessorConfig', { const preProcessorConfig = defineModel<ExecuteConditionConfig>('preProcessorConfig', {
required: true, required: true,

View File

@ -1,6 +1,6 @@
<template> <template>
<div :class="['p-[16px_16px]', props.class]"> <div :class="['p-[8px_16px]', props.class]">
<div class="mb-[16px] flex items-center justify-between"> <div class="mb-[8px] flex items-center justify-between">
<div class="flex items-center"> </div> <div class="flex items-center"> </div>
<div class="items-right flex gap-[8px]"> <div class="items-right flex gap-[8px]">
<a-input-search <a-input-search
@ -893,8 +893,9 @@
]), ]),
showSelectAll: !props.readOnly, showSelectAll: !props.readOnly,
draggable: hasAnyPermission(['PROJECT_API_SCENARIO:READ+UPDATE']) ? { type: 'handle', width: 32 } : undefined, draggable: hasAnyPermission(['PROJECT_API_SCENARIO:READ+UPDATE']) ? { type: 'handle', width: 32 } : undefined,
heightUsed: 374, heightUsed: 256,
showSubdirectory: true, showSubdirectory: true,
paginationSize: 'mini',
}, },
(item) => ({ (item) => ({
...item, ...item,
@ -1186,12 +1187,14 @@
await resetScheduleConfig(record); await resetScheduleConfig(record);
showScheduleModal.value = true; showScheduleModal.value = true;
} }
async function deleteScenarioSchedule(scenarioId: string) { async function deleteScenarioSchedule(scenarioId: string) {
try { try {
await deleteScheduleConfig(scenarioId); await deleteScheduleConfig(scenarioId);
Message.success(t('common.deleteSuccess')); Message.success(t('common.deleteSuccess'));
loadScenarioList(false); loadScenarioList(false);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console
console.log(error); console.log(error);
} }
} }
@ -1540,4 +1543,5 @@
} }
} }
} }
.ms-table--special-small();
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<MsCard no-content-padding simple> <MsCard no-content-padding simple>
<div class="flex items-center justify-between p-[24px_24px_8px_24px]"> <div class="flex items-center justify-between p-[8px_16px_8px_16px]">
<MsEditableTab <MsEditableTab
v-model:active-tab="activeScenarioTab" v-model:active-tab="activeScenarioTab"
v-model:tabs="scenarioTabs" v-model:tabs="scenarioTabs"
@ -68,14 +68,16 @@
</div> </div>
</template> </template>
<template #second> <template #second>
<ScenarioTable <div class="overflow-x-hidden">
ref="apiTableRef" <ScenarioTable
:active-module="activeModule" ref="apiTableRef"
:offspring-ids="offspringIds" :active-module="activeModule"
@refresh-module-tree="refreshTree" :offspring-ids="offspringIds"
@open-scenario="openScenarioTab" @refresh-module-tree="refreshTree"
@create-scenario="() => newTab()" @open-scenario="openScenarioTab"
/> @create-scenario="() => newTab()"
/>
</div>
</template> </template>
</MsSplitBox> </MsSplitBox>
</div> </div>
@ -623,7 +625,7 @@
<style scoped lang="less"> <style scoped lang="less">
.pageWrap { .pageWrap {
height: calc(100% - 65px); height: calc(100% - 54px);
border-radius: var(--border-radius-large); border-radius: var(--border-radius-large);
@apply bg-white; @apply bg-white;
.case { .case {

View File

@ -193,6 +193,10 @@ export default {
'apiScenario.deleteStepConfirmWithChildren': 'apiScenario.deleteStepConfirmWithChildren':
'Are you sure you want to delete the selected step and all substeps within the step?', 'Are you sure you want to delete the selected step and all substeps within the step?',
'apiScenario.quoteScenarioStepNotAllowDelete': 'Substeps that reference a scenario cannot be deleted', 'apiScenario.quoteScenarioStepNotAllowDelete': 'Substeps that reference a scenario cannot be deleted',
'apiScenario.openGlobalPostConditionTip':
'It is closed by default. If it is closed, the global post-processing will not be executed when running this interface.',
'apiScenario.openGlobalPreConditionTip':
'It is closed by default. If it is closed, the global prefix will not be executed when running this interface.',
// Execution History // Execution History
'apiScenario.executeHistory.searchPlaceholder': 'Search by ID or name', 'apiScenario.executeHistory.searchPlaceholder': 'Search by ID or name',
'apiScenario.executeHistory.num': 'No.', 'apiScenario.executeHistory.num': 'No.',

View File

@ -184,6 +184,8 @@ export default {
'apiScenario.deleteStepConfirm': '确认删除 {name} 吗?', 'apiScenario.deleteStepConfirm': '确认删除 {name} 吗?',
'apiScenario.deleteStepConfirmWithChildren': '确认删除所选步骤以及步骤内所有子步骤?', 'apiScenario.deleteStepConfirmWithChildren': '确认删除所选步骤以及步骤内所有子步骤?',
'apiScenario.quoteScenarioStepNotAllowDelete': '引用场景的子步骤不能删除', 'apiScenario.quoteScenarioStepNotAllowDelete': '引用场景的子步骤不能删除',
'apiScenario.openGlobalPostConditionTip': '默认关闭,关闭则运行该接口时不执行全局后置',
'apiScenario.openGlobalPreConditionTip': '默认关闭,关闭则运行该接口时不执行全局前置',
// 执行历史 // 执行历史
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索', 'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
'apiScenario.executeHistory.num': '序号', 'apiScenario.executeHistory.num': '序号',

View File

@ -1,143 +1,145 @@
<template> <template>
<MsCard simple> <MsCard simple no-content-padding>
<MsAdvanceFilter <div class="h-full p-[16px]">
v-model:keyword="keyword" <MsAdvanceFilter
:search-placeholder="t('caseManagement.featureCase.searchByNameAndId')" v-model:keyword="keyword"
:filter-config-list="filterConfigList" :search-placeholder="t('caseManagement.featureCase.searchByNameAndId')"
:row-count="filterRowCount" :filter-config-list="filterConfigList"
@keyword-search="fetchData" :row-count="filterRowCount"
@adv-search="handleAdvSearch" @keyword-search="fetchData"
@refresh="handleAdvSearch" @adv-search="handleAdvSearch"
> @refresh="handleAdvSearch"
<template #left> >
<div class="flex gap-[12px]"> <template #left>
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="primary" @click="handleCreate" <div class="flex gap-[12px]">
>{{ t('bugManagement.createBug') }} <a-button v-permission="['PROJECT_BUG:READ+ADD']" type="primary" @click="handleCreate"
>{{ t('bugManagement.createBug') }}
</a-button>
<a-button v-if="currentPlatform !== 'Local'" :loading="!isComplete" type="outline" @click="handleSync"
>{{ t('bugManagement.syncBug') }}
</a-button>
</div>
</template>
</MsAdvanceFilter>
<MsBaseTable
class="mt-[8px]"
v-bind="propsRes"
:action-config="tableBatchActions"
v-on="propsEvent"
@batch-action="handleTableBatch"
@sorter-change="saveSort"
>
<!-- ID -->
<template #num="{ record, rowIndex }">
<a-button type="text" class="px-0" size="mini" @click="handleShowDetail(record.id, rowIndex)">
{{ record.num }}
</a-button> </a-button>
<a-button v-if="currentPlatform !== 'Local'" :loading="!isComplete" type="outline" @click="handleSync" </template>
>{{ t('bugManagement.syncBug') }} <template #operation="{ record }">
<div class="flex flex-nowrap items-center">
<span v-permission="['PROJECT_BUG:READ+UPDATE']" class="flex flex-row items-center">
<MsButton class="!mr-0" :disabled="currentPlatform !== record.platform" @click="handleEdit(record)">
{{ t('common.edit') }}
</MsButton>
<a-divider class="!mx-2 h-[12px]" direction="vertical" />
</span>
<span v-permission="['PROJECT_BUG:READ+ADD']" class="flex flex-row items-center">
<MsButton class="!mr-0" :disabled="currentPlatform !== record.platform" @click="handleCopy(record)">
{{ t('common.copy') }}
</MsButton>
<a-divider class="!mx-2 h-[12px]" direction="vertical" />
</span>
<MsTableMoreAction
v-permission="['PROJECT_BUG:READ+DELETE']"
:list="moreActionList"
trigger="click"
@select="handleMoreActionSelect($event, record)"
/>
</div>
</template>
<template #relationCaseCount="{ record, rowIndex }">
<a-button type="text" class="px-0" size="mini" @click="showRelateCaseCount(record.id, rowIndex)">
{{ record.relationCaseCount }}
</a-button> </a-button>
</div> </template>
</template>
</MsAdvanceFilter>
<MsBaseTable
class="mt-[16px]"
v-bind="propsRes"
:action-config="tableBatchActions"
v-on="propsEvent"
@batch-action="handleTableBatch"
@sorter-change="saveSort"
>
<!-- ID -->
<template #num="{ record, rowIndex }">
<a-button type="text" class="px-0" @click="handleShowDetail(record.id, rowIndex)">{{ record.num }}</a-button>
</template>
<template #operation="{ record }">
<div class="flex flex-nowrap items-center">
<span v-permission="['PROJECT_BUG:READ+UPDATE']" class="flex flex-row items-center">
<MsButton class="!mr-0" :disabled="currentPlatform !== record.platform" @click="handleEdit(record)">{{
t('common.edit')
}}</MsButton>
<a-divider class="!mx-2 h-[12px]" direction="vertical" />
</span>
<span v-permission="['PROJECT_BUG:READ+ADD']" class="flex flex-row items-center">
<MsButton class="!mr-0" :disabled="currentPlatform !== record.platform" @click="handleCopy(record)">{{
t('common.copy')
}}</MsButton>
<a-divider class="!mx-2 h-[12px]" direction="vertical" />
</span>
<MsTableMoreAction
v-permission="['PROJECT_BUG:READ+DELETE']"
:list="moreActionList"
trigger="click"
@select="handleMoreActionSelect($event, record)"
/>
</div>
</template>
<template #relationCaseCount="{ record, rowIndex }"> <template #createUserFilter="{ columnConfig }">
<a-button type="text" class="px-0" @click="showRelateCaseCount(record.id, rowIndex)">{{ <TableFilter
record.relationCaseCount v-model:visible="createUserFilterVisible"
}}</a-button> v-model:status-filters="createUserFilterValue"
</template> :title="(columnConfig.title as string)"
:list="createUserFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</template>
<template #createUserFilter="{ columnConfig }"> <template #updateUserFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="createUserFilterVisible" v-model:visible="updateUserFilterVisible"
v-model:status-filters="createUserFilterValue" v-model:status-filters="updateUserFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="createUserFilterOptions" :list="updateUserFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #updateUserFilter="{ columnConfig }"> <template #handleUserFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="updateUserFilterVisible" v-model:visible="handleUserFilterVisible"
v-model:status-filters="updateUserFilterValue" v-model:status-filters="handleUserFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="updateUserFilterOptions" :list="handleUserFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #handleUserFilter="{ columnConfig }"> <template #statusFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="handleUserFilterVisible" v-model:visible="statusFilterVisible"
v-model:status-filters="handleUserFilterValue" v-model:status-filters="statusFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="handleUserFilterOptions" :list="statusFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #statusFilter="{ columnConfig }"> <template #severityFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="statusFilterVisible" v-model:visible="severityFilterVisible"
v-model:status-filters="statusFilterValue" v-model:status-filters="severityFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="statusFilterOptions" :list="severityFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
</MsBaseTable>
<template #severityFilter="{ columnConfig }"> </div>
<TableFilter
v-model:visible="severityFilterVisible"
v-model:status-filters="severityFilterValue"
:title="(columnConfig.title as string)"
:list="severityFilterOptions"
value-key="value"
@search="searchData()"
>
<template #item="{ item }">
{{ item.text }}
</template>
</TableFilter>
</template>
<template #empty> </template>
</MsBaseTable>
</MsCard> </MsCard>
<a-modal <a-modal
v-model:visible="syncVisible" v-model:visible="syncVisible"
@ -218,7 +220,7 @@
import { useIntervalFn } from '@vueuse/core'; import { useIntervalFn } from '@vueuse/core';
import { Message, TableData } from '@arco-design/web-vue'; import { Message, TableData } from '@arco-design/web-vue';
import { MsAdvanceFilter, timeSelectOptions } from '@/components/pure/ms-advance-filter'; import { timeSelectOptions } from '@/components/pure/ms-advance-filter';
import { BackEndEnum, FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type'; import { BackEndEnum, FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
@ -501,7 +503,8 @@
selectable: true, selectable: true,
noDisable: false, noDisable: false,
showSetting: true, showSetting: true,
heightUsed: 380, heightUsed: 256,
paginationSize: 'mini',
}, },
(record: TableData) => ({ (record: TableData) => ({
...record, ...record,
@ -893,4 +896,5 @@
:deep(.arco-divider-vertical) { :deep(.arco-divider-vertical) {
margin: 0 8px; margin: 0 8px;
} }
.ms-table--special-small();
</style> </style>

View File

@ -1,130 +1,132 @@
<template> <template>
<MsCard simple> <MsCard simple no-content-padding>
<MsAdvanceFilter <div class="h-full p-[16px]">
v-model:keyword="keyword" <MsAdvanceFilter
:search-placeholder="t('bugManagement.recycle.searchPlaceholder')" v-model:keyword="keyword"
:filter-config-list="filterConfigList" :search-placeholder="t('bugManagement.recycle.searchPlaceholder')"
:row-count="filterRowCount" :filter-config-list="filterConfigList"
@keyword-search="fetchData" :row-count="filterRowCount"
@refresh="fetchData('')" @keyword-search="fetchData"
> @refresh="fetchData('')"
<template #left> >
<div></div> <template #left>
</template> <div></div>
</MsAdvanceFilter> </template>
<MsBaseTable </MsAdvanceFilter>
class="mt-[16px]" <MsBaseTable
v-bind="propsRes" class="mt-[16px]"
:action-config="tableAction" v-bind="propsRes"
v-on="propsEvent" :action-config="tableAction"
@batch-action="handleTableBatch" v-on="propsEvent"
> @batch-action="handleTableBatch"
<template #operation="{ record }"> >
<div class="flex flex-row flex-nowrap"> <template #operation="{ record }">
<MsButton class="!mr-0" @click="handleRecover(record)">{{ t('bugManagement.recycle.recover') }}</MsButton> <div class="flex flex-row flex-nowrap">
<a-divider direction="vertical" /> <MsButton class="!mr-0" @click="handleRecover(record)">{{ t('bugManagement.recycle.recover') }}</MsButton>
<MsButton class="!mr-0" @click="handleDelete(record)">{{ <a-divider direction="vertical" />
t('bugManagement.recycle.permanentlyDelete') <MsButton class="!mr-0" @click="handleDelete(record)">{{
}}</MsButton> t('bugManagement.recycle.permanentlyDelete')
</div> }}</MsButton>
</template> </div>
</template>
<template #deleteTimeColumn="{ record }"> <template #deleteTimeColumn="{ record }">
{{ dayjs(record.deleteTime).format('YYYY-MM-DD HH:mm:ss') || '-' }} {{ dayjs(record.deleteTime).format('YYYY-MM-DD HH:mm:ss') || '-' }}
</template> </template>
<template #createUserFilter="{ columnConfig }"> <template #createUserFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="createUserFilterVisible" v-model:visible="createUserFilterVisible"
v-model:status-filters="createUserFilterValue" v-model:status-filters="createUserFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="createUserFilterOptions" :list="createUserFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #updateUserFilter="{ columnConfig }"> <template #updateUserFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="updateUserFilterVisible" v-model:visible="updateUserFilterVisible"
v-model:status-filters="updateUserFilterValue" v-model:status-filters="updateUserFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="updateUserFilterOptions" :list="updateUserFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #deleteUserFilter="{ columnConfig }"> <template #deleteUserFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="deleteUserFilterVisible" v-model:visible="deleteUserFilterVisible"
v-model:status-filters="deleteUserFilterValue" v-model:status-filters="deleteUserFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="deleteUserFilterOptions" :list="deleteUserFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #handleUserFilter="{ columnConfig }"> <template #handleUserFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="handleUserFilterVisible" v-model:visible="handleUserFilterVisible"
v-model:status-filters="handleUserFilterValue" v-model:status-filters="handleUserFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="handleUserFilterOptions" :list="handleUserFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #statusFilter="{ columnConfig }"> <template #statusFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="statusFilterVisible" v-model:visible="statusFilterVisible"
v-model:status-filters="statusFilterValue" v-model:status-filters="statusFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="statusFilterOptions" :list="statusFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #severityFilter="{ columnConfig }"> <template #severityFilter="{ columnConfig }">
<TableFilter <TableFilter
v-model:visible="severityFilterVisible" v-model:visible="severityFilterVisible"
v-model:status-filters="severityFilterValue" v-model:status-filters="severityFilterValue"
:title="(columnConfig.title as string)" :title="(columnConfig.title as string)"
:list="severityFilterOptions" :list="severityFilterOptions"
value-key="value" value-key="value"
@search="searchData()" @search="searchData()"
> >
<template #item="{ item }"> <template #item="{ item }">
{{ item.text }} {{ item.text }}
</template> </template>
</TableFilter> </TableFilter>
</template> </template>
<template #empty> </template> <template #empty> </template>
</MsBaseTable> </MsBaseTable>
</div>
</MsCard> </MsCard>
</template> </template>
@ -377,6 +379,7 @@
noDisable: true, noDisable: true,
showSetting: true, showSetting: true,
scroll: { x: '1900px' }, scroll: { x: '1900px' },
heightUsed: 256,
}, },
(record: TableData) => ({ (record: TableData) => ({
...record, ...record,
@ -548,3 +551,7 @@
fetchData(); fetchData();
}); });
</script> </script>
<style lang="less" scoped>
.ms-table--special-small();
</style>

View File

@ -39,7 +39,7 @@
v-bind="propsRes" v-bind="propsRes"
ref="tableRef" ref="tableRef"
filter-icon-align-left filter-icon-align-left
class="mt-4" class="mt-[8px]"
:action-config="tableBatchActions" :action-config="tableBatchActions"
@selected-change="handleTableSelect" @selected-change="handleTableSelect"
v-on="propsEvent" v-on="propsEvent"
@ -67,6 +67,7 @@
v-model:model-value="record.caseLevel" v-model:model-value="record.caseLevel"
:placeholder="t('common.pleaseSelect')" :placeholder="t('common.pleaseSelect')"
class="param-input w-full" class="param-input w-full"
size="mini"
@change="() => handleStatusChange(record)" @change="() => handleStatusChange(record)"
> >
<template #label> <template #label>
@ -97,7 +98,12 @@
trigger="click" trigger="click"
@popup-visible-change="handleFilterHidden" @popup-visible-change="handleFilterHidden"
> >
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="executeResultFilterVisible = true"> <a-button
type="text"
class="arco-btn-text--secondary p-[8px_4px]"
size="mini"
@click="executeResultFilterVisible = true"
>
<div class="font-medium"> <div class="font-medium">
{{ t(columnConfig.title as string) }} {{ t(columnConfig.title as string) }}
</div> </div>
@ -106,7 +112,7 @@
<template #content> <template #content>
<div class="arco-table-filters-content"> <div class="arco-table-filters-content">
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]"> <div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="executeResultFilters" direction="vertical" size="small"> <a-checkbox-group v-model:model-value="executeResultFilters" direction="vertical" size="mini">
<a-checkbox v-for="key of Object.keys(executionResultMap)" :key="key" :value="key"> <a-checkbox v-for="key of Object.keys(executionResultMap)" :key="key" :value="key">
<MsIcon <MsIcon
:type="executionResultMap[key]?.icon || ''" :type="executionResultMap[key]?.icon || ''"
@ -202,6 +208,7 @@
v-model:model-value="record.lastExecuteResult" v-model:model-value="record.lastExecuteResult"
:placeholder="t('common.pleaseSelect')" :placeholder="t('common.pleaseSelect')"
class="param-input w-full" class="param-input w-full"
size="mini"
@change="() => handleStatusChange(record)" @change="() => handleStatusChange(record)"
> >
<template #label> <template #label>
@ -228,6 +235,7 @@
height: 200, height: 200,
}, },
}" }"
size="mini"
@change="(value) => handleChangeModule(record, value)" @change="(value) => handleChangeModule(record, value)"
> >
<template #tree-slot-title="node"> <template #tree-slot-title="node">
@ -899,9 +907,10 @@
tableKey: TableKeyEnum.CASE_MANAGEMENT_TABLE, tableKey: TableKeyEnum.CASE_MANAGEMENT_TABLE,
selectable: true, selectable: true,
showSetting: true, showSetting: true,
heightUsed: 380, heightUsed: 256,
enableDrag: true, enableDrag: true,
showSubdirectory: true, showSubdirectory: true,
paginationSize: 'mini',
}, },
(record) => { (record) => {
return { return {
@ -1688,4 +1697,5 @@
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
} }
.ms-table--special-small();
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<MsCard simple no-content-padding> <MsCard simple no-content-padding>
<div class="flex items-center border-b border-[var(--color-text-n8)] p-[24px_24px_16px_24px]"> <div class="flex items-center border-b border-[var(--color-text-n8)] p-[8px_24px]">
<a-button v-permission="['FUNCTIONAL_CASE:READ+ADD']" 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>
@ -11,10 +11,10 @@
{{ t('caseManagement.featureCase.importXmind') }} {{ t('caseManagement.featureCase.importXmind') }}
</a-button> --> </a-button> -->
</div> </div>
<div class="pageWrap relative h-[calc(100%-73px)]"> <div class="pageWrap relative h-[calc(100%-49px)]">
<MsSplitBox> <MsSplitBox>
<template #first> <template #first>
<div class="p-[24px] pb-0"> <div class="p-[8px_24px] pb-0">
<div class="feature-case h-[100%]"> <div class="feature-case h-[100%]">
<a-input-search <a-input-search
v-model:model-value="groupKeyword" v-model:model-value="groupKeyword"
@ -92,7 +92,7 @@
</div> </div>
</template> </template>
<template #second> <template #second>
<div class="p-[24px]"> <div class="p-[8px_24px]">
<CaseTable <CaseTable
ref="caseTableRef" ref="caseTableRef"
:active-folder="activeFolder" :active-folder="activeFolder"

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="px-[24px] py-[16px]"> <div class="px-[24px] py-[8px]">
<div class="mb-[16px]"> <div class="mb-[8px]">
<MsAdvanceFilter <MsAdvanceFilter
v-model:keyword="keyword" v-model:keyword="keyword"
:filter-config-list="filterConfigList" :filter-config-list="filterConfigList"
@ -92,13 +92,13 @@
</template> </template>
<template #num="{ record }"> <template #num="{ record }">
<a-tooltip :content="`${record.num}`"> <a-tooltip :content="`${record.num}`">
<a-button type="text" class="px-0" @click="openDetail(record.id)"> <a-button type="text" class="px-0" size="mini" @click="openDetail(record.id)">
<div class="one-line-text max-w-[168px]">{{ record.num }}</div> <div class="one-line-text max-w-[168px]">{{ record.num }}</div>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</template> </template>
<template #status="{ record }"> <template #status="{ record }">
<statusTag :status="record.status" /> <statusTag :status="record.status" size="small" />
</template> </template>
<template #reviewPassRule="{ record }"> <template #reviewPassRule="{ record }">
{{ {{
@ -496,8 +496,9 @@
showSetting: true, showSetting: true,
selectable: true, selectable: true,
showSelectAll: true, showSelectAll: true,
heightUsed: 344, heightUsed: 256,
showSubdirectory: true, showSubdirectory: true,
paginationSize: 'mini',
}, },
(item) => { (item) => {
return { return {
@ -764,4 +765,6 @@
} }
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped>
.ms-table--special-small();
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<a-tag :color="statusMap[props.status]?.color" :class="statusMap[props.status]?.class"> <a-tag :color="statusMap[props.status]?.color" :class="statusMap[props.status]?.class" :size="props.size">
{{ t(statusMap[props.status]?.label) }} {{ t(statusMap[props.status]?.label) }}
</a-tag> </a-tag>
</template> </template>
@ -11,6 +11,7 @@
const props = defineProps<{ const props = defineProps<{
status: ReviewStatus; status: ReviewStatus;
size?: 'small' | 'medium' | 'large';
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();

View File

@ -1,6 +1,6 @@
<template> <template>
<MsCard simple no-content-padding> <MsCard simple no-content-padding>
<div class="flex items-center justify-between border-b border-[var(--color-text-n8)] p-[24px_24px_16px_24px]"> <div class="flex items-center justify-between border-b border-[var(--color-text-n8)] p-[8px_24px]">
<a-button v-permission="['CASE_REVIEW:READ+ADD']" type="primary" @click="goCreateReview"> <a-button v-permission="['CASE_REVIEW:READ+ADD']" type="primary" @click="goCreateReview">
{{ t('caseManagement.caseReview.create') }} {{ t('caseManagement.caseReview.create') }}
</a-button> </a-button>
@ -10,7 +10,7 @@
<a-radio value="createByMe">{{ t('caseManagement.caseReview.myCreate') }}</a-radio> <a-radio value="createByMe">{{ t('caseManagement.caseReview.myCreate') }}</a-radio>
</a-radio-group> </a-radio-group>
</div> </div>
<div class="relative h-[calc(100%-73px)]"> <div class="relative h-[calc(100%-49px)]">
<MsSplitBox> <MsSplitBox>
<template #first> <template #first>
<div class="px-[24px] py-[16px]"> <div class="px-[24px] py-[16px]">

View File

@ -114,13 +114,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineModel, ref } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { driverOptionFun, validateDatabaseEnv } from '@/api/modules/project-management/envManagement'; import { driverOptionFun, validateDatabaseEnv } from '@/api/modules/project-management/envManagement';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore'; import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { getGenerateId } from '@/utils'; import { getGenerateId } from '@/utils';
@ -141,7 +139,6 @@
const loading = ref(false); const loading = ref(false);
const driverOption = ref<{ label: string; value: string }[]>([]); const driverOption = ref<{ label: string; value: string }[]>([]);
const appStore = useAppStore(); const appStore = useAppStore();
const licenseStore = useLicenseStore();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'cancel', shouldSearch: boolean): void; (e: 'cancel', shouldSearch: boolean): void;
@ -282,7 +279,7 @@
form.value = { form.value = {
...currentItem, ...currentItem,
id: '', id: '',
dataSource: `copy_${currentItem.dataSource}`, dataSource: `copy_${currentItem.dataSource}`.substring(0, 255),
}; };
} else { } else {
form.value = { form.value = {

View File

@ -371,9 +371,10 @@
async function upgradeRepositoryFile() { async function upgradeRepositoryFile() {
try { try {
fileLoading.value = true; fileLoading.value = true;
await updateRepositoryFile(innerFileId.value); const res = await updateRepositoryFile(innerFileId.value);
Message.success(t('common.updateSuccess')); Message.success(t('common.updateSuccess'));
detailDrawerRef.value?.initDetail(); innerFileId.value = res;
detailDrawerRef.value?.initDetail(res);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);

View File

@ -1,10 +1,7 @@
import type { MsTableColumn } from '@/components/pure/ms-table/type'; import type { MsTableColumn } from '@/components/pure/ms-table/type';
import { useAppStore } from '@/store';
import { TaskCenterEnum } from '@/enums/taskCenter'; import { TaskCenterEnum } from '@/enums/taskCenter';
const appStore = useAppStore();
export const TaskStatus = { export const TaskStatus = {
[TaskCenterEnum.API_CASE]: { [TaskCenterEnum.API_CASE]: {
SUCCESS: { SUCCESS: {