fix: 部分缺陷修复
This commit is contained in:
parent
9deabde693
commit
6de3b98fce
|
@ -419,14 +419,9 @@
|
|||
.arco-checkbox-icon {
|
||||
border: 1px solid var(--color-text-input-border);
|
||||
}
|
||||
&:hover {
|
||||
.arco-checkbox-icon-hover::before {
|
||||
background-color: rgb(var(--primary-9)) !important;
|
||||
}
|
||||
}
|
||||
.arco-checkbox-icon-hover {
|
||||
&:hover::before {
|
||||
background-color: rgb(var(--primary-9)) !important;
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
@change="emit('change')"
|
||||
/>
|
||||
<a-input-number
|
||||
v-if="model.type === 'inputNumber'"
|
||||
v-else-if="model.type === 'inputNumber'"
|
||||
v-model:model-value="element[model.filed]"
|
||||
class="flex-1"
|
||||
:placeholder="t(model.placeholder || '')"
|
||||
|
@ -73,7 +73,7 @@
|
|||
@change="emit('change')"
|
||||
/>
|
||||
<MsTagsInput
|
||||
v-if="model.type === 'tagInput'"
|
||||
v-else-if="model.type === 'tagInput'"
|
||||
v-model:model-value="element[model.filed]"
|
||||
class="flex-1"
|
||||
:placeholder="t(model.placeholder || 'common.tagPlaceholder')"
|
||||
|
@ -84,7 +84,7 @@
|
|||
@change="emit('change')"
|
||||
/>
|
||||
<a-select
|
||||
v-if="model.type === 'select'"
|
||||
v-else-if="model.type === 'select'"
|
||||
v-model="element[model.filed]"
|
||||
class="flex-1"
|
||||
:placeholder="t(model.placeholder || '')"
|
||||
|
@ -92,7 +92,7 @@
|
|||
:field-names="model.filedNames"
|
||||
@change="emit('change')"
|
||||
/>
|
||||
<div v-if="model.type === 'multiple'" class="flex flex-row gap-[4px]">
|
||||
<div v-else-if="model.type === 'multiple'" class="flex flex-row gap-[4px]">
|
||||
<a-form-item
|
||||
v-for="(child, childIndex) in model.children"
|
||||
:key="`${child.filed}${childIndex}${index}`"
|
||||
|
@ -114,7 +114,7 @@
|
|||
@change="emit('change')"
|
||||
/>
|
||||
<a-select
|
||||
v-if="child.type === 'select'"
|
||||
v-else-if="child.type === 'select'"
|
||||
v-model="element[child.filed]"
|
||||
:class="child.className"
|
||||
:placeholder="t(child.placeholder || '')"
|
||||
|
|
|
@ -26,22 +26,3 @@ export interface FormItemModel {
|
|||
defaultValue?: string | string[] | number | number[] | boolean; // 默认值
|
||||
hasRedStar?: boolean; // 是否有红星
|
||||
}
|
||||
|
||||
declare const _default: import('vue').DefineComponent<
|
||||
{
|
||||
models: FormItemModel[];
|
||||
formMode: FormMode;
|
||||
addText: string;
|
||||
maxHeight?: string;
|
||||
defaultVals?: Record<string, string[] | string>; // 当外层是编辑状态时,可传入已填充的数据
|
||||
},
|
||||
unknown,
|
||||
import('vue').ComponentOptionsMixin,
|
||||
import('vue').ComponentOptionsMixin,
|
||||
{
|
||||
formValidate: (cb: (res?: Record<string, any>) => void, isSubmit = true) => void;
|
||||
getFormResult: <T>() => T[];
|
||||
}
|
||||
>;
|
||||
|
||||
export declare type MsBatchFormInstance = InstanceType<typeof _default>;
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
</a-tooltip>
|
||||
</template>
|
||||
<template #caseLevel="{ record }">
|
||||
<span>{{ t(record.priority) }}</span>
|
||||
<caseLevel :case-level="getCaseLevel(record)" />
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<div class="footer">
|
||||
|
@ -168,6 +168,7 @@
|
|||
import type { CommonList, ModuleTreeNode, TableQueryParams } from '@/models/common';
|
||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import type { CaseLevel } from './types';
|
||||
import { initGetModuleCountFunc, type RequestModuleEnum } from './utils';
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -328,7 +329,8 @@
|
|||
sortDirections: ['ascend', 'descend'],
|
||||
sorter: true,
|
||||
},
|
||||
width: 200,
|
||||
width: 150,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: 'ms.case.associate.caseName',
|
||||
|
@ -338,7 +340,7 @@
|
|||
sorter: true,
|
||||
},
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
width: 250,
|
||||
},
|
||||
{
|
||||
title: 'ms.case.associate.caseLevel',
|
||||
|
@ -357,6 +359,7 @@
|
|||
props.getTableFunc,
|
||||
{
|
||||
columns,
|
||||
scroll: { x: '100%' },
|
||||
showSetting: false,
|
||||
selectable: true,
|
||||
showSelectAll: true,
|
||||
|
@ -375,6 +378,11 @@
|
|||
}
|
||||
);
|
||||
|
||||
// 用例等级
|
||||
function getCaseLevel(record: CaseManagementTable) {
|
||||
return (record.customFields.find((item: any) => item.name === '用例等级')?.value as CaseLevel) || 'P1';
|
||||
}
|
||||
|
||||
const searchParams = ref<TableQueryParams>({
|
||||
moduleIds: [],
|
||||
version: version.value,
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsPasswordInput from '@/components/pure/ms-password-input/index.vue';
|
||||
|
@ -39,13 +40,12 @@
|
|||
|
||||
import { updatePsw } from '@/api/modules/user';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useUser from '@/hooks/useUser';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { encrypted } from '@/utils';
|
||||
import { validatePasswordLength, validateWordPassword } from '@/utils/validate';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const { logout } = useUser();
|
||||
const { t } = useI18n();
|
||||
|
||||
const form = ref({
|
||||
|
@ -111,7 +111,13 @@
|
|||
}, 1000);
|
||||
setTimeout(() => {
|
||||
clearInterval(timer);
|
||||
logout();
|
||||
router.push({
|
||||
name: 'login',
|
||||
query: {
|
||||
...router.currentRoute.value.query,
|
||||
redirect: router.currentRoute.value.name as string,
|
||||
},
|
||||
});
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -458,7 +458,7 @@
|
|||
.arco-tree-node-drag-icon {
|
||||
@apply cursor-move;
|
||||
|
||||
right: 6px;
|
||||
right: 16px;
|
||||
.arco-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
:popup-visible="currentVisible"
|
||||
position="bl"
|
||||
trigger="click"
|
||||
class="w-[277px]"
|
||||
class="w-[350px]"
|
||||
:content-class="props.id ? 'move-left' : ''"
|
||||
>
|
||||
<template #content>
|
||||
|
@ -18,13 +18,12 @@
|
|||
:label-col-props="{ span: 0 }"
|
||||
:wrapper-col-props="{ span: 24 }"
|
||||
>
|
||||
<div class="mb-[8px] text-[14px] font-medium text-[var(--color-text-1)]">{{
|
||||
props.id ? t('system.userGroup.rename') : t('system.userGroup.createUserGroup')
|
||||
}}</div>
|
||||
<div class="mb-[8px] text-[14px] font-medium text-[var(--color-text-1)]">
|
||||
{{ props.id ? t('system.userGroup.rename') : t('system.userGroup.createUserGroup') }}
|
||||
</div>
|
||||
<a-form-item field="name" :rules="[{ validator: validateName }]">
|
||||
<a-input
|
||||
v-model="form.name"
|
||||
class="w-[243px]"
|
||||
:max-length="255"
|
||||
:placeholder="t('system.userGroup.searchHolder')"
|
||||
allow-clear
|
||||
|
@ -107,7 +106,7 @@
|
|||
callback(t('system.userGroup.userGroupNameIsExist', { name: value }));
|
||||
}
|
||||
}
|
||||
if (value.length === 255) {
|
||||
if (value.length > 255) {
|
||||
callback(t('common.nameIsTooLang'));
|
||||
}
|
||||
callback();
|
||||
|
|
|
@ -445,7 +445,13 @@
|
|||
if (isSelect) {
|
||||
// leftCollapse 切换时不重复数据请求
|
||||
if (id) {
|
||||
handleListItemClick(res.find((i) => i.id === id) || res[0]);
|
||||
const item = res.find((i) => i.id === id);
|
||||
if (item) {
|
||||
handleListItemClick(item);
|
||||
} else {
|
||||
Message.warning(t('common.resourceDeleted'));
|
||||
handleListItemClick(res[0]);
|
||||
}
|
||||
} else {
|
||||
handleListItemClick(res[0]);
|
||||
}
|
||||
|
@ -465,8 +471,8 @@
|
|||
|
||||
// 点击更多操作
|
||||
const handleMoreAction = (item: ActionsItem, id: string, authScope: AuthScopeEnum) => {
|
||||
if (item.eventTag === 'rename') {
|
||||
const tmpObj = userGroupList.value.filter((ele) => ele.id === id)[0];
|
||||
if (item.eventTag === 'rename') {
|
||||
const visibleItem: PopVisibleItem = { visible: true, authScope, defaultName: tmpObj.name, id };
|
||||
popVisible.value[id] = visibleItem;
|
||||
}
|
||||
|
@ -485,7 +491,7 @@
|
|||
}
|
||||
openModal({
|
||||
type: 'error',
|
||||
title: t('system.userGroup.isDeleteUserGroup', { name: characterLimit(currentName.value) }),
|
||||
title: t('system.userGroup.isDeleteUserGroup', { name: characterLimit(tmpObj.name) }),
|
||||
content,
|
||||
okText: t('system.userGroup.confirmDelete'),
|
||||
cancelText: t('system.userGroup.cancel'),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<MsRemoveButton
|
||||
:title="t('system.userGroup.removeName', { name: characterLimit(record.name) })"
|
||||
:sub-title-tip="t('system.userGroup.removeTip')"
|
||||
:disabled="record.userId === 'admin'"
|
||||
:disabled="systemType === AuthScopeEnum.SYSTEM && record.userId === 'admin'"
|
||||
@ok="handleRemove(record)"
|
||||
/>
|
||||
</template>
|
||||
|
@ -77,7 +77,7 @@
|
|||
};
|
||||
});
|
||||
|
||||
const userGroupUsercolumns: MsTableColumn = [
|
||||
const userGroupUserColumns: MsTableColumn = [
|
||||
{
|
||||
title: 'system.userGroup.name',
|
||||
dataIndex: 'name',
|
||||
|
@ -113,7 +113,7 @@
|
|||
const { propsRes, propsEvent, loadList, setLoadListParams, setKeyword } = useTable(
|
||||
getRequestBySystemType(),
|
||||
{
|
||||
columns: userGroupUsercolumns,
|
||||
columns: userGroupUserColumns,
|
||||
scroll: { x: '100%', minWidth: 700, y: '100%' },
|
||||
selectable: false,
|
||||
noDisable: true,
|
||||
|
@ -130,7 +130,6 @@
|
|||
|
||||
const handlePermission = (permission: string[], cb: () => void) => {
|
||||
if (!hasAnyPermission(permission)) {
|
||||
Message.error(t('common.noPermission'));
|
||||
return false;
|
||||
}
|
||||
cb();
|
||||
|
|
|
@ -58,10 +58,10 @@
|
|||
<div class="flex justify-end gap-[16px]">
|
||||
<a-button type="secondary" @click="back">{{ t('mscard.defaultCancelText') }}</a-button>
|
||||
<a-button v-if="!props.hideContinue && !props.isEdit" type="secondary" @click="emit('saveAndContinue')">
|
||||
{{ t('mscard.defaultSaveAndContinueText') }}
|
||||
{{ props.saveAndContinueText || t('mscard.defaultSaveAndContinueText') }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="emit('save')">
|
||||
{{ t(props.isEdit ? 'mscard.defaultUpdate' : 'mscard.defaultConfirm') }}
|
||||
{{ props.saveText || t(props.isEdit ? 'mscard.defaultUpdate' : 'mscard.defaultConfirm') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</slot>
|
||||
|
@ -102,6 +102,8 @@
|
|||
handleBack: () => void; // 自定义返回按钮触发事件
|
||||
dividerHasPX: boolean; // 分割线是否有左右padding;
|
||||
showFullScreen: boolean; // 是否显示全屏按钮
|
||||
saveText?: string; // 保存按钮文案
|
||||
saveAndContinueText?: string; // 保存并继续按钮文案
|
||||
}>
|
||||
>(),
|
||||
{
|
||||
|
@ -151,6 +153,10 @@
|
|||
// 简单模式没有标题、没有底部
|
||||
return props.noContentPadding ? 66 + _specialHeight : 114 + _specialHeight;
|
||||
}
|
||||
if (props.hideFooter) {
|
||||
// 没有底部
|
||||
return props.noContentPadding ? 120 + _specialHeight : 168 + _specialHeight;
|
||||
}
|
||||
return 230 + _specialHeight;
|
||||
});
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
</a-tooltip>
|
||||
<div ref="tabNav" class="ms-editable-tab-nav">
|
||||
<div
|
||||
v-for="tab in props.tabs"
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
class="ms-editable-tab"
|
||||
:class="{ active: innerActiveTab?.id === tab.id }"
|
||||
:class="{ active: activeTab?.id === tab.id }"
|
||||
@click="handleTabClick(tab)"
|
||||
>
|
||||
<div :draggable="!!tab.draggable" class="flex items-center">
|
||||
|
@ -29,7 +29,7 @@
|
|||
</slot>
|
||||
<div v-if="tab.unSaved" class="ml-[8px] h-[8px] w-[8px] rounded-full bg-[rgb(var(--primary-5))]"></div>
|
||||
<MsButton
|
||||
v-if="props.atLeastOne ? props.tabs.length > 1 && tab.closable : tab.closable !== false"
|
||||
v-if="props.atLeastOne ? tabs.length > 1 && tab.closable : tab.closable !== false"
|
||||
type="icon"
|
||||
status="secondary"
|
||||
class="ms-editable-tab-close-button"
|
||||
|
@ -54,20 +54,20 @@
|
|||
<a-tooltip
|
||||
v-if="!props.readonly && showAdd"
|
||||
:content="t('ms.editableTab.limitTip', { max: props.limit })"
|
||||
:disabled="!props.limit || props.tabs.length >= props.limit"
|
||||
:disabled="!props.limit || tabs.length >= props.limit"
|
||||
>
|
||||
<MsButton
|
||||
type="icon"
|
||||
status="secondary"
|
||||
class="ms-editable-tab-button !mr-[4px]"
|
||||
:disabled="!!props.limit && props.tabs.length >= props.limit"
|
||||
:disabled="!!props.limit && tabs.length >= props.limit"
|
||||
@click="addTab"
|
||||
>
|
||||
<MsIcon type="icon-icon_add_outlined" />
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
<MsMoreAction
|
||||
v-if="!props.hideMoreAction && !props.readonly"
|
||||
v-if="!props.hideMoreAction && !props.readonly && mergedMoreActionList.length > 0"
|
||||
:list="mergedMoreActionList"
|
||||
@select="handleMoreActionSelect"
|
||||
>
|
||||
|
@ -95,8 +95,6 @@
|
|||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
tabs: TabItem[];
|
||||
activeTab?: TabItem;
|
||||
moreActionList?: ActionsItem[];
|
||||
showAdd?: boolean; // 是否展示增加tab按钮
|
||||
limit?: number; // 最多可打开的tab数量
|
||||
|
@ -109,8 +107,6 @@
|
|||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:tabs', tabs: TabItem[]): void;
|
||||
(e: 'update:activeTab', activeTab: TabItem): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'close', item: TabItem): void;
|
||||
(e: 'change', item: TabItem): void;
|
||||
|
@ -120,8 +116,12 @@
|
|||
const { t } = useI18n();
|
||||
const { openModal } = useModal();
|
||||
|
||||
const innerActiveTab = useVModel(props, 'activeTab', emit);
|
||||
const innerTabs = useVModel(props, 'tabs', emit);
|
||||
const activeTab = defineModel<TabItem | undefined>('activeTab', {
|
||||
default: undefined,
|
||||
});
|
||||
const tabs = defineModel<TabItem[]>('tabs', {
|
||||
required: true,
|
||||
});
|
||||
const tabNav = ref<HTMLElement>();
|
||||
const { arrivedState } = useScroll(tabNav);
|
||||
const isNotOverflow = computed(() => arrivedState.left && arrivedState.right); // 内容是否溢出,用于判断左右滑动按钮是否展示
|
||||
|
@ -178,6 +178,9 @@
|
|||
const dl = props.atLeastOne
|
||||
? defaultMoreActionList.filter((e) => e.eventTag !== 'closeAll')
|
||||
: defaultMoreActionList;
|
||||
if (tabs.value.filter((item) => item.closable === true).length === 0) {
|
||||
return props.moreActionList ? [...props.moreActionList] : [];
|
||||
}
|
||||
return props.moreActionList ? [...dl, ...props.moreActionList] : dl;
|
||||
});
|
||||
|
||||
|
@ -185,9 +188,9 @@
|
|||
* 监听激活的tab变化,滚动到激活的tab
|
||||
*/
|
||||
watch(
|
||||
() => props.activeTab,
|
||||
() => activeTab.value,
|
||||
() => {
|
||||
useDraggable('.ms-editable-tab-nav', innerTabs, {
|
||||
useDraggable('.ms-editable-tab-nav', tabs, {
|
||||
ghostClass: 'ms-editable-tab-ghost',
|
||||
});
|
||||
nextTick(() => {
|
||||
|
@ -211,11 +214,11 @@
|
|||
* 关闭一个tab
|
||||
*/
|
||||
function closeOneTab(item: TabItem) {
|
||||
const index = innerTabs.value.findIndex((e) => e.id === item.id);
|
||||
innerTabs.value.splice(index, 1);
|
||||
if (innerActiveTab.value?.id === item.id && innerTabs.value[0]) {
|
||||
[innerActiveTab.value] = innerTabs.value;
|
||||
emit('change', innerTabs.value[0]);
|
||||
const index = tabs.value.findIndex((e) => e.id === item.id);
|
||||
tabs.value.splice(index, 1);
|
||||
if (activeTab.value?.id === item.id && tabs.value[0]) {
|
||||
[activeTab.value] = tabs.value;
|
||||
emit('change', tabs.value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,8 +244,8 @@
|
|||
}
|
||||
|
||||
function handleTabClick(item: TabItem) {
|
||||
if (innerActiveTab.value?.id !== item.id) {
|
||||
innerActiveTab.value = item;
|
||||
if (activeTab.value?.id !== item.id) {
|
||||
activeTab.value = item;
|
||||
nextTick(() => {
|
||||
tabNav.value?.querySelector('.tab.active')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
});
|
||||
|
@ -256,14 +259,12 @@
|
|||
function executeAction(event: ActionsItem) {
|
||||
switch (event.eventTag) {
|
||||
case 'closeAll':
|
||||
innerTabs.value = innerTabs.value.filter((item) => item.closable === false);
|
||||
[innerActiveTab.value] = innerTabs.value;
|
||||
emit('change', innerActiveTab.value);
|
||||
tabs.value = tabs.value.filter((item) => item.closable === false);
|
||||
[activeTab.value] = tabs.value;
|
||||
emit('change', activeTab.value);
|
||||
break;
|
||||
case 'closeOther':
|
||||
innerTabs.value = innerTabs.value.filter(
|
||||
(item) => item.id === innerActiveTab.value?.id || item.closable === false
|
||||
);
|
||||
tabs.value = tabs.value.filter((item) => item.id === activeTab.value?.id || item.closable === false);
|
||||
break;
|
||||
default:
|
||||
emit('moreActionSelect', event);
|
||||
|
@ -276,9 +277,8 @@
|
|||
*/
|
||||
function handleMoreActionSelect(event: ActionsItem) {
|
||||
if (
|
||||
(event.eventTag === 'closeAll' && innerTabs.value.some((item) => item.unSaved)) ||
|
||||
(event.eventTag === 'closeOther' &&
|
||||
innerTabs.value.some((item) => item.unSaved && item.id !== innerActiveTab.value?.id))
|
||||
(event.eventTag === 'closeAll' && tabs.value.some((item) => item.unSaved)) ||
|
||||
(event.eventTag === 'closeOther' && tabs.value.some((item) => item.unSaved && item.id !== activeTab.value?.id))
|
||||
) {
|
||||
openModal({
|
||||
title: t('common.tip'),
|
||||
|
|
|
@ -3,14 +3,14 @@ import { onBeforeRouteLeave } from 'vue-router';
|
|||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
|
||||
const isSave = ref(false);
|
||||
|
||||
// 离开页面确认提示
|
||||
export default function useLeaveUnSaveTip() {
|
||||
const { openModal } = useModal();
|
||||
const { t } = useI18n();
|
||||
|
||||
const setState = (flag: boolean) => {
|
||||
const isSave = ref(true);
|
||||
|
||||
const setIsSave = (flag: boolean) => {
|
||||
isSave.value = flag;
|
||||
};
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
|
@ -40,6 +40,6 @@ export default function useLeaveUnSaveTip() {
|
|||
}
|
||||
});
|
||||
return {
|
||||
setState,
|
||||
setIsSave,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
|
||||
import { includes } from 'lodash-es';
|
||||
|
||||
import { firstLevelMenu } from '@/config/permission';
|
||||
import { hasAnyPermission, topLevelMenuHasPermission } from '@/utils/permission';
|
||||
|
|
|
@ -151,4 +151,5 @@ export default {
|
|||
'The content of some tabs has not been saved. The unsaved content will be lost after leaving. Are you sure you want to leave?',
|
||||
'common.image': 'Image',
|
||||
'common.text': 'Text',
|
||||
'common.resourceDeleted': 'Resource has been deleted',
|
||||
};
|
||||
|
|
|
@ -151,4 +151,5 @@ export default {
|
|||
'common.unsavedLeave': '有标签页的内容未保存,离开后未保存的内容将丢失,确定要离开吗?',
|
||||
'common.image': '图片',
|
||||
'common.text': '文本',
|
||||
'common.resourceDeleted': '资源已被删除',
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ export interface ResourcePoolItem extends ResourcePoolInfo {
|
|||
|
||||
export type ResourcePoolDetail = Omit<ResourcePoolInfo, 'testResourceDTO'> & {
|
||||
id: string;
|
||||
deleted: boolean;
|
||||
testResourceReturnDTO: TestResourceDTO;
|
||||
};
|
||||
|
||||
|
|
|
@ -525,7 +525,6 @@
|
|||
const appStore = useAppStore();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data: ExecuteConditionProcessor;
|
||||
disabled?: boolean;
|
||||
response?: string; // 响应内容
|
||||
heightUsed?: number;
|
||||
|
@ -542,7 +541,6 @@
|
|||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:data', data: ExecuteConditionProcessor): void;
|
||||
(e: 'copy'): void;
|
||||
(e: 'delete', id: number): void;
|
||||
(e: 'change'): void;
|
||||
|
@ -551,11 +549,11 @@
|
|||
const { t } = useI18n();
|
||||
|
||||
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
||||
const condition = useVModel(props, 'data', emit);
|
||||
const condition = defineModel<ExecuteConditionProcessor>('data', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => currentEnvConfig?.value,
|
||||
() => {
|
||||
function filterDataSource() {
|
||||
if (condition.value.processorType === RequestConditionProcessor.SQL && condition.value.dataSourceId) {
|
||||
// 如果是SQL类型的条件且已选数据源,需要根据环境切换数据源
|
||||
const dataSourceItem = currentEnvConfig?.value.dataSources.find(
|
||||
|
@ -575,6 +573,12 @@
|
|||
condition.value.dataSourceId = currentEnvConfig.value.dataSources[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => currentEnvConfig?.value,
|
||||
() => {
|
||||
filterDataSource();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -582,6 +586,13 @@
|
|||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => condition.value.id,
|
||||
() => {
|
||||
filterDataSource();
|
||||
}
|
||||
);
|
||||
|
||||
// 是否显示脚本名称编辑框
|
||||
const isShowEditScriptNameInput = ref(false);
|
||||
const scriptNameInputRef = ref<InputInstance>();
|
||||
|
@ -1003,6 +1014,7 @@ if (!result){
|
|||
|
||||
const protocolList = ref<ProtocolItem[]>([]);
|
||||
onBeforeMount(async () => {
|
||||
if (props.showPrePostRequest) {
|
||||
try {
|
||||
// TODO:数据从外面传进来
|
||||
protocolList.value = await getProtocolList(appStore.currentOrgId);
|
||||
|
@ -1010,6 +1022,7 @@ if (!result){
|
|||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => condition.value.commonScriptInfo,
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
<slot name="titleRight"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="data.length > 0 && activeItem" class="flex h-[calc(100%-40px)] w-full gap-[8px]">
|
||||
<div v-if="list.length > 0 && activeItem" class="flex h-[calc(100%-40px)] w-full gap-[8px]">
|
||||
<div class="h-full w-[20%] min-w-[220px]">
|
||||
<conditionList
|
||||
v-model:list="data"
|
||||
v-model:list="list"
|
||||
:disabled="props.disabled"
|
||||
:active-id="activeItemId"
|
||||
:show-associated-scene="props.showAssociatedScene"
|
||||
|
@ -32,7 +32,7 @@
|
|||
<conditionContent
|
||||
v-model:data="activeItem"
|
||||
:disabled="props.disabled"
|
||||
:total-list="data"
|
||||
:total-list="list"
|
||||
:response="props.response"
|
||||
:height-used="props.heightUsed"
|
||||
:show-associated-scene="props.showAssociatedScene"
|
||||
|
@ -57,12 +57,11 @@
|
|||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ConditionType, ExecuteConditionProcessor, RegexExtract } from '@/models/apiTest/common';
|
||||
import { RequestConditionProcessor, RequestExtractExpressionEnum, RequestExtractScope } from '@/enums/apiEnum';
|
||||
import { RequestConditionProcessor, RequestExtractScope } from '@/enums/apiEnum';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
list: ExecuteConditionProcessor[];
|
||||
conditionTypes: Array<ConditionType>;
|
||||
addText: string;
|
||||
requestRadioTextProps?: Record<string, any>;
|
||||
|
@ -77,16 +76,15 @@
|
|||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:list', list: ExecuteConditionProcessor[]): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const data = defineModel<ExecuteConditionProcessor[]>('list', {
|
||||
const list = defineModel<ExecuteConditionProcessor[]>('list', {
|
||||
required: true,
|
||||
});
|
||||
const activeItem = ref<ExecuteConditionProcessor>(data.value[0]);
|
||||
const activeItem = ref<ExecuteConditionProcessor>(list.value[0]);
|
||||
const activeItemId = computed(() => activeItem.value?.id);
|
||||
|
||||
function handleListActiveChange(item: ExecuteConditionProcessor) {
|
||||
|
@ -101,8 +99,8 @@
|
|||
...cloneDeep(activeItem.value),
|
||||
id: new Date().getTime(),
|
||||
};
|
||||
data.value.push(copyItem as ExecuteConditionProcessor);
|
||||
activeItem.value = copyItem as ExecuteConditionProcessor;
|
||||
list.value.push(copyItem as ExecuteConditionProcessor);
|
||||
activeItem.value = list.value[list.value.length - 1];
|
||||
emit('change');
|
||||
}
|
||||
|
||||
|
@ -110,9 +108,9 @@
|
|||
* 删除列表项
|
||||
*/
|
||||
function deleteListItem(id: string | number) {
|
||||
data.value = data.value.filter((precondition) => precondition.id !== activeItemId.value);
|
||||
list.value = list.value.filter((precondition) => precondition.id !== activeItemId.value);
|
||||
if (activeItemId.value === id) {
|
||||
[activeItem.value] = data.value;
|
||||
[activeItem.value] = list.value;
|
||||
}
|
||||
emit('change');
|
||||
}
|
||||
|
@ -131,10 +129,10 @@
|
|||
} else if (props.showPrePostRequest) {
|
||||
type = RequestConditionProcessor.REQUEST_SCRIPT;
|
||||
}
|
||||
const isExistPre = data.value.filter(
|
||||
const isExistPre = list.value.filter(
|
||||
(item) => item.beforeStepScript && item.processorType === RequestConditionProcessor.REQUEST_SCRIPT
|
||||
).length;
|
||||
const isExistPost = data.value.filter(
|
||||
const isExistPost = list.value.filter(
|
||||
(item) => !item.beforeStepScript && item.processorType === RequestConditionProcessor.REQUEST_SCRIPT
|
||||
).length;
|
||||
// 如果是场景或者是请求类型的 需要限制前后脚本类型只能为一前一后
|
||||
|
@ -142,7 +140,7 @@
|
|||
if (isExistPre && isExistPost && props.showPrePostRequest) {
|
||||
return;
|
||||
}
|
||||
data.value.push({
|
||||
list.value.push({
|
||||
id,
|
||||
processorType: type,
|
||||
name: t('apiTestDebug.preconditionScriptName'),
|
||||
|
@ -165,12 +163,12 @@
|
|||
|
||||
break;
|
||||
case RequestConditionProcessor.SQL:
|
||||
const isSQL = data.value.find((item) => item.processorType === RequestConditionProcessor.SQL);
|
||||
const isSQL = list.value.find((item) => item.processorType === RequestConditionProcessor.SQL);
|
||||
|
||||
if (props.showPrePostRequest && isSQL) {
|
||||
return;
|
||||
}
|
||||
data.value.push({
|
||||
list.value.push({
|
||||
id,
|
||||
processorType: RequestConditionProcessor.SQL,
|
||||
enableCommonScript: false,
|
||||
|
@ -189,7 +187,7 @@
|
|||
|
||||
break;
|
||||
case RequestConditionProcessor.TIME_WAITING:
|
||||
data.value.push({
|
||||
list.value.push({
|
||||
id,
|
||||
processorType: RequestConditionProcessor.TIME_WAITING,
|
||||
associateScenarioResult: false,
|
||||
|
@ -200,11 +198,11 @@
|
|||
});
|
||||
break;
|
||||
case RequestConditionProcessor.EXTRACT:
|
||||
const isEXTRACT = data.value.find((item) => item.processorType === RequestConditionProcessor.EXTRACT);
|
||||
const isEXTRACT = list.value.find((item) => item.processorType === RequestConditionProcessor.EXTRACT);
|
||||
if (isEXTRACT) {
|
||||
return;
|
||||
}
|
||||
data.value.push({
|
||||
list.value.push({
|
||||
id,
|
||||
processorType: RequestConditionProcessor.EXTRACT,
|
||||
enableCommonScript: false,
|
||||
|
@ -218,14 +216,14 @@
|
|||
default:
|
||||
break;
|
||||
}
|
||||
activeItem.value = data.value[data.value.length - 1];
|
||||
activeItem.value = list.value[list.value.length - 1];
|
||||
emit('change');
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
// 后台存储无id,渲染时需要手动添加一次
|
||||
let hasNoIdItem = false;
|
||||
const tempArr = props.list.map((item, i) => {
|
||||
const tempArr = list.value.map((item, i) => {
|
||||
if (!item.id) {
|
||||
hasNoIdItem = true;
|
||||
return {
|
||||
|
@ -241,8 +239,8 @@
|
|||
return item;
|
||||
});
|
||||
if (hasNoIdItem) {
|
||||
data.value = tempArr.map((e) => e);
|
||||
[activeItem.value] = data.value;
|
||||
list.value = tempArr;
|
||||
[activeItem.value] = list.value;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<MsList
|
||||
v-model:active-item-key="activeItem.id"
|
||||
v-model:focus-item-key="focusItemKey"
|
||||
v-model:data="data"
|
||||
v-model:data="list"
|
||||
mode="static"
|
||||
item-key-field="id"
|
||||
:disabled="props.disabled"
|
||||
|
@ -59,7 +59,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import MsList from '@/components/pure/ms-list/index.vue';
|
||||
|
@ -74,20 +73,20 @@
|
|||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
list: ExecuteConditionProcessor[];
|
||||
activeId?: string | number;
|
||||
showAssociatedScene?: boolean;
|
||||
disabled?: boolean;
|
||||
showPrePostRequest?: boolean; // 是否展示前后置请求忽略选项
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:list', list: ExecuteConditionProcessor[]): void;
|
||||
(e: 'activeChange', item: ExecuteConditionProcessor): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const data = useVModel(props, 'list', emit);
|
||||
const list = defineModel<ExecuteConditionProcessor[]>('list', {
|
||||
required: true,
|
||||
});
|
||||
const { openModal } = useModal();
|
||||
|
||||
// 当前聚焦的列表项
|
||||
|
@ -98,11 +97,11 @@
|
|||
const hasPreAndPost = computed(() => {
|
||||
if (props.showPrePostRequest) {
|
||||
const hasPre =
|
||||
data.value.filter(
|
||||
list.value.filter(
|
||||
(item) => item.beforeStepScript && item.processorType === RequestConditionProcessor.REQUEST_SCRIPT
|
||||
).length > 0;
|
||||
const hasPost =
|
||||
data.value.filter(
|
||||
list.value.filter(
|
||||
(item) => !item.beforeStepScript && item.processorType === RequestConditionProcessor.REQUEST_SCRIPT
|
||||
).length > 0;
|
||||
if (hasPre && hasPost) {
|
||||
|
@ -114,12 +113,12 @@
|
|||
});
|
||||
|
||||
const hasEXTRACT = computed(() => {
|
||||
return data.value.filter((item: any) => item.processorType === RequestConditionProcessor.EXTRACT).length > 0;
|
||||
return list.value.filter((item: any) => item.processorType === RequestConditionProcessor.EXTRACT).length > 0;
|
||||
});
|
||||
|
||||
const hasSql = computed(
|
||||
() =>
|
||||
data.value.filter((item: any) => item.processorType === RequestConditionProcessor.SQL).length > 0 &&
|
||||
list.value.filter((item: any) => item.processorType === RequestConditionProcessor.SQL).length > 0 &&
|
||||
props.showPrePostRequest
|
||||
);
|
||||
|
||||
|
@ -139,7 +138,7 @@
|
|||
let moreActions: ActionsItem[] = [...itemMoreActions];
|
||||
|
||||
watchEffect(() => {
|
||||
activeItem.value = data.value.find((item) => item.id === props.activeId) || data.value[0] || {};
|
||||
activeItem.value = list.value.find((item) => item.id === props.activeId) || list.value[0] || {};
|
||||
emit('activeChange', activeItem.value);
|
||||
if (hasPreAndPost.value || hasEXTRACT.value || hasSql.value) {
|
||||
moreActions = itemMoreActions.slice(-1);
|
||||
|
@ -162,10 +161,10 @@
|
|||
...cloneDeep(item),
|
||||
id: new Date().getTime(),
|
||||
};
|
||||
const isExistPre = data.value.filter(
|
||||
const isExistPre = list.value.filter(
|
||||
(current) => current.beforeStepScript && current.processorType === RequestConditionProcessor.REQUEST_SCRIPT
|
||||
).length;
|
||||
const isExistPost = data.value.filter(
|
||||
const isExistPost = list.value.filter(
|
||||
(current) => !current.beforeStepScript && current.processorType === RequestConditionProcessor.REQUEST_SCRIPT
|
||||
).length;
|
||||
// 如果是场景或者是请求类型的 需要限制前后脚本类型只能为一前一后
|
||||
|
@ -180,9 +179,9 @@
|
|||
id: new Date().getTime(),
|
||||
};
|
||||
|
||||
const copyIndex = data.value.findIndex((e: ExecuteConditionProcessor) => e.id === item.id);
|
||||
const copyIndex = list.value.findIndex((e: ExecuteConditionProcessor) => e.id === item.id);
|
||||
if (copyIndex > -1) {
|
||||
data.value.splice(copyIndex, 0, copyItem);
|
||||
list.value.splice(copyIndex, 0, copyItem);
|
||||
activeItem.value = copyItem;
|
||||
emit('activeChange', activeItem.value);
|
||||
}
|
||||
|
@ -193,9 +192,9 @@
|
|||
* @param item 列表项
|
||||
*/
|
||||
function deleteListItem(item: ExecuteConditionProcessor) {
|
||||
data.value = data.value.filter((precondition) => precondition.id !== item.id);
|
||||
list.value = list.value.filter((precondition) => precondition.id !== item.id);
|
||||
if (activeItem.value.id === item.id) {
|
||||
[activeItem.value] = data.value;
|
||||
[activeItem.value] = list.value;
|
||||
}
|
||||
emit('activeChange', activeItem.value);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-select
|
||||
v-model:model-value="currentEnv"
|
||||
v-model:model-value="innerCurrentEnv"
|
||||
:options="envOptions"
|
||||
class="!w-[200px] pl-0 pr-[8px]"
|
||||
:loading="envLoading"
|
||||
allow-search
|
||||
@change="initEnvironment"
|
||||
@change="(val) => initEnvironment(val as string)"
|
||||
@popup-visible-change="popupVisibleChange"
|
||||
>
|
||||
<template #prefix>
|
||||
|
@ -28,23 +28,36 @@
|
|||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
currentEnv?: string;
|
||||
setDefaultEnv?: boolean; // 是否设置默认选中环境,当传入的currentEnv为空时根据此属性判断是否需要将currentEnv设置为第一个环境
|
||||
}>(),
|
||||
{
|
||||
setDefaultEnv: true,
|
||||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:currentEnv', val: string): void;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { openNewPage } = useOpenNewPage();
|
||||
|
||||
const currentEnv = defineModel<string>('currentEnv', { default: '' });
|
||||
const innerCurrentEnv = ref(props.currentEnv || '');
|
||||
const currentEnvConfig = defineModel<EnvConfig>('currentEnvConfig', {
|
||||
default: {},
|
||||
});
|
||||
const envLoading = ref(false);
|
||||
const envOptions = ref<SelectOptionData[]>([]);
|
||||
|
||||
async function initEnvironment() {
|
||||
async function initEnvironment(id: string) {
|
||||
try {
|
||||
const res = await getEnvironment(currentEnv.value);
|
||||
const res = await getEnvironment(id);
|
||||
currentEnvConfig.value = {
|
||||
...res,
|
||||
id: currentEnv.value,
|
||||
name: envOptions.value.find((item) => item.value === currentEnv.value)?.label || '',
|
||||
id,
|
||||
name: envOptions.value.find((item) => item.value === id)?.label || '',
|
||||
};
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -60,12 +73,10 @@
|
|||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
currentEnv.value = currentEnv.value.length ? currentEnv.value : res[0]?.id;
|
||||
nextTick(() => {
|
||||
if (currentEnv.value) {
|
||||
initEnvironment();
|
||||
if (!innerCurrentEnv.value) {
|
||||
innerCurrentEnv.value = res[0]?.id;
|
||||
}
|
||||
});
|
||||
initEnvironment(innerCurrentEnv.value || res[0]?.id);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
@ -85,14 +96,24 @@
|
|||
}
|
||||
|
||||
watch(
|
||||
() => currentEnv.value,
|
||||
() => props.currentEnv,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
currentEnv.value = (envOptions.value[0]?.value as string) || '';
|
||||
if (props.setDefaultEnv) {
|
||||
innerCurrentEnv.value = (envOptions.value[0]?.value as string) || '';
|
||||
initEnvironment((envOptions.value[0]?.value as string) || '');
|
||||
}
|
||||
nextTick(() => {
|
||||
initEnvironment();
|
||||
});
|
||||
} else {
|
||||
innerCurrentEnv.value = val;
|
||||
initEnvironment(val);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerCurrentEnv.value,
|
||||
(val) => {
|
||||
emit('update:currentEnv', val);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -838,10 +838,12 @@
|
|||
}
|
||||
return item;
|
||||
});
|
||||
const lastTwoIsSame =
|
||||
arr.length >= 2 && filterKeyValParams([arr[arr.length - 2]], arr[arr.length - 1]).lastDataIsDefault;
|
||||
if (
|
||||
!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault &&
|
||||
!props.isTreeTable &&
|
||||
!filterKeyValParams([arr[arr.length - 2], arr[arr.length - 1]], arr[arr.length - 1]).lastDataIsDefault // 为了判断最后俩行是否一致(因为下拉框切换会新增一行一样的数据,此时最后一条数据与默认数据是不一样的)
|
||||
!lastTwoIsSame // 为了判断最后俩行是否一致(因为下拉框切换会新增一行一样的数据,此时最后一条数据与默认数据是不一样的)
|
||||
) {
|
||||
addTableLine(arr.length - 1, false, true);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
title: 'apiTestDebug.status',
|
||||
dataIndex: 'pass',
|
||||
slotName: 'status',
|
||||
width: 100,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.reason',
|
||||
|
|
|
@ -277,13 +277,15 @@
|
|||
const { url, headers, queryParameters } = parseCurlScript(curlCode.value);
|
||||
addDebugTab({
|
||||
url,
|
||||
headers: headers?.map((e) => ({
|
||||
headers:
|
||||
headers?.map((e) => ({
|
||||
contentType: RequestContentTypeEnum.TEXT,
|
||||
description: '',
|
||||
enable: true,
|
||||
...e,
|
||||
})),
|
||||
query: queryParameters?.map((e) => ({
|
||||
})) || [],
|
||||
query:
|
||||
queryParameters?.map((e) => ({
|
||||
paramType: RequestParamsType.STRING,
|
||||
description: '',
|
||||
required: false,
|
||||
|
@ -292,7 +294,7 @@
|
|||
encode: false,
|
||||
enable: true,
|
||||
...e,
|
||||
})),
|
||||
})) || [],
|
||||
});
|
||||
curlCode.value = '';
|
||||
importDrawerVisible.value = false;
|
||||
|
|
|
@ -314,11 +314,12 @@
|
|||
{{ t('apiTestManagement.timeTaskList') }}
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('apiTestManagement.searchPlaceholder')"
|
||||
:placeholder="t('apiTestManagement.searchTaskPlaceholder')"
|
||||
allow-clear
|
||||
class="mr-[8px] w-[240px]"
|
||||
@search="loadTaskList"
|
||||
@press-enter="loadTaskList"
|
||||
@clear="loadTaskList"
|
||||
/>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<a-form-item
|
||||
field="name"
|
||||
:label="t('apiTestManagement.apiName')"
|
||||
class="mb-[16px] w-[80%]"
|
||||
class="mb-[16px] w-[60%]"
|
||||
:rules="[{ required: true, message: t('apiTestManagement.apiNameRequired') }]"
|
||||
>
|
||||
<a-input
|
||||
|
@ -16,7 +16,7 @@
|
|||
@change="handleActiveApiChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('common.desc')" class="mb-[16px] w-[80%]">
|
||||
<a-form-item :label="t('common.desc')" class="mb-[16px] w-[60%]">
|
||||
<a-textarea v-model:model-value="requestVModel.description" :max-length="1000" @change="handleActiveApiChange" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('apiTestManagement.belongModule')" class="mb-[16px] w-[436px]">
|
||||
|
|
|
@ -28,7 +28,11 @@
|
|||
</a-tooltip>
|
||||
</template>
|
||||
</MsEditableTab>
|
||||
<environmentSelect ref="environmentSelectRef" v-model:current-env="activeApiTab.environmentId" />
|
||||
<environmentSelect
|
||||
ref="environmentSelectRef"
|
||||
v-model:current-env="activeApiTab.environmentId"
|
||||
:set-default-env="false"
|
||||
/>
|
||||
</div>
|
||||
<api
|
||||
v-show="(activeApiTab.id === 'all' && currentTab === 'api') || activeApiTab.type === 'api'"
|
||||
|
@ -220,14 +224,14 @@
|
|||
);
|
||||
|
||||
// 切换到第一个tab
|
||||
function changeActiveApiTabTofirst() {
|
||||
function changeActiveApiTabToFirst() {
|
||||
activeApiTab.value = apiTabs.value[0] as RequestParam;
|
||||
}
|
||||
|
||||
// 下拉框切换
|
||||
function currentTabChange(val: any) {
|
||||
apiTabs.value[0].label = val === 'api' ? t('apiTestManagement.allApi') : t('case.allCase');
|
||||
changeActiveApiTabTofirst();
|
||||
changeActiveApiTabToFirst();
|
||||
}
|
||||
|
||||
watch(
|
||||
|
@ -327,7 +331,7 @@
|
|||
refreshApiTable,
|
||||
handleApiUpdateFromModuleTree,
|
||||
handleDeleteApiFromModuleTree,
|
||||
changeActiveApiTabTofirst,
|
||||
changeActiveApiTabToFirst,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
function handleNodeSelect(keys: string[], _offspringIds: string[]) {
|
||||
[activeModule.value] = keys;
|
||||
offspringIds.value = _offspringIds;
|
||||
managementRef.value?.changeActiveApiTabTofirst();
|
||||
managementRef.value?.changeActiveApiTabToFirst();
|
||||
}
|
||||
|
||||
function handleApiNodeClick(node: ModuleTreeNode) {
|
||||
|
|
|
@ -34,6 +34,7 @@ export default {
|
|||
'apiTestManagement.closeOther': 'Close other tabs',
|
||||
'apiTestManagement.showSubdirectory': 'Show subdirectory use case',
|
||||
'apiTestManagement.searchPlaceholder': 'Enter ID/name/api path search',
|
||||
'apiTestManagement.searchTaskPlaceholder': 'Enter resource Id/name search',
|
||||
'apiTestManagement.apiName': 'Api name',
|
||||
'apiTestManagement.apiType': 'Api type',
|
||||
'apiTestManagement.apiStatus': 'Status',
|
||||
|
|
|
@ -33,6 +33,7 @@ export default {
|
|||
'apiTestManagement.closeOther': '关闭其他tab',
|
||||
'apiTestManagement.showSubdirectory': '显示子目录用例',
|
||||
'apiTestManagement.searchPlaceholder': '输入 ID/名称/api路径搜索',
|
||||
'apiTestManagement.searchTaskPlaceholder': '输入资源ID/名称搜索',
|
||||
'apiTestManagement.apiName': '接口名称',
|
||||
'apiTestManagement.apiType': '请求类型',
|
||||
'apiTestManagement.apiStatus': '状态',
|
||||
|
|
|
@ -1209,6 +1209,7 @@
|
|||
requestVModel.value = cloneDeep({
|
||||
...defaultApiParams,
|
||||
...props.request,
|
||||
name: props.step?.name || props.request.name,
|
||||
url: props.request.path, // 后台字段是 path
|
||||
activeTab: contentTabList.value[0].value,
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
:page-size="1"
|
||||
:total="props.loopTotal"
|
||||
:show-jumper="props.loopTotal > 5"
|
||||
:base-size="Infinity"
|
||||
:base-size="0"
|
||||
show-total
|
||||
size="mini"
|
||||
class="loop-pagination"
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { WorkbenchRouteEnum } from '@/enums/routeEnum';
|
||||
import { getFirstRouteNameByPermission } from '@/utils/permission';
|
||||
|
||||
const router = useRouter();
|
||||
const back = () => {
|
||||
// warning: Go to the node that has the permission
|
||||
router.push({ name: WorkbenchRouteEnum.WORKBENCH });
|
||||
router.push({ name: getFirstRouteNameByPermission(router.getRoutes()) });
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -270,8 +270,8 @@
|
|||
import { convertToFileByBug } from './utils';
|
||||
|
||||
defineOptions({ name: 'BugEditPage' });
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
setState(false);
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
setIsSave(false);
|
||||
|
||||
const { t } = useI18n();
|
||||
interface TemplateOption {
|
||||
|
@ -609,7 +609,7 @@
|
|||
// 执行保存操作
|
||||
const res = await createOrUpdateBug({ request: tmpObj, fileList: localFiles as unknown as File[] });
|
||||
if (isEdit.value) {
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
|
@ -617,7 +617,7 @@
|
|||
} else {
|
||||
Message.success(t('common.createSuccess'));
|
||||
if (isContinue) {
|
||||
setState(false);
|
||||
setIsSave(false);
|
||||
// 如果是保存并继续创建
|
||||
const { templateId } = form.value;
|
||||
// 用当前模板初始化自定义字段
|
||||
|
@ -632,7 +632,7 @@
|
|||
// 清空文件列表
|
||||
fileList.value = [];
|
||||
} else {
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
// 否则跳转到成功页
|
||||
if (getIsVisited()) {
|
||||
router.push({
|
||||
|
|
|
@ -49,12 +49,12 @@
|
|||
|
||||
import Message from '@arco-design/web-vue/es/message';
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
setState(false);
|
||||
setIsSave(false);
|
||||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
|
||||
|
@ -87,7 +87,7 @@
|
|||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE,
|
||||
query: { orgId: route.query.orgId, pId: route.query.pId },
|
||||
});
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
// 创建用例
|
||||
} else {
|
||||
// 创建并关联
|
||||
|
@ -104,7 +104,7 @@
|
|||
isShowTip.value = !getIsVisited();
|
||||
if (isReview) {
|
||||
Message.success(t('caseManagement.featureCase.createAndLinkSuccess'));
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
router.back();
|
||||
return;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@
|
|||
},
|
||||
});
|
||||
}
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -430,7 +430,6 @@
|
|||
|
||||
async function getFetch() {
|
||||
if (!hasAnyPermission(['FUNCTIONAL_CASE:READ', 'FUNCTIONAL_CASE:READ+UPDATE', 'FUNCTIONAL_CASE:READ+DELETE'])) {
|
||||
Message.error(t('common.noPermission'));
|
||||
return;
|
||||
}
|
||||
if (showType.value === 'link') {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<MsCard :min-width="1100" simple has-breadcrumb hide-footer no-content-padding hide-divider show-full-screen>
|
||||
<MsCard :min-width="1100" has-breadcrumb hide-footer no-content-padding hide-divider show-full-screen>
|
||||
<template #headerLeft>
|
||||
<a-tooltip :content="reviewDetail.name">
|
||||
<div class="one-line-text mr-[8px] max-w-[260px] font-medium text-[var(--color-text-000)]">
|
||||
|
|
|
@ -147,7 +147,11 @@
|
|||
</template>
|
||||
<template v-if="keyword.trim() === ''" #empty>
|
||||
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
|
||||
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||
{{
|
||||
hasAllPermission(['CASE_REVIEW:READ+ADD'])
|
||||
? t('caseManagement.caseReview.tableNoData')
|
||||
: t('caseManagement.caseReview.tableNoDataNoPermission')
|
||||
}}
|
||||
<MsButton v-permission="['CASE_REVIEW:READ+ADD']" class="ml-[8px]" @click="() => emit('goCreate')">
|
||||
{{ t('caseManagement.caseReview.create') }}
|
||||
</MsButton>
|
||||
|
@ -214,7 +218,7 @@
|
|||
import useTableStore from '@/hooks/useTableStore';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import {
|
||||
ReviewDetailReviewersItem,
|
||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
|||
'caseManagement.caseReview.list.searchPlaceholder': 'Search by ID, name, or tag',
|
||||
'caseManagement.caseReview.archive': 'Archive',
|
||||
'caseManagement.caseReview.tableNoData': 'No data yet, please',
|
||||
'caseManagement.caseReview.tableNoDataNoPermission': 'No data yet',
|
||||
'caseManagement.caseReview.name': 'Review name',
|
||||
'caseManagement.caseReview.creator': 'Creator',
|
||||
'caseManagement.caseReview.reviewer': 'Reviewer',
|
||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
|||
'caseManagement.caseReview.list.searchPlaceholder': '通过ID、名称或标签搜索',
|
||||
'caseManagement.caseReview.archive': '归档',
|
||||
'caseManagement.caseReview.tableNoData': '暂无数据,请',
|
||||
'caseManagement.caseReview.tableNoDataNoPermission': '暂无数据',
|
||||
'caseManagement.caseReview.name': '评审名称',
|
||||
'caseManagement.caseReview.creator': '创建人',
|
||||
'caseManagement.caseReview.reviewer': '评审人',
|
||||
|
|
|
@ -33,7 +33,11 @@
|
|||
v-model="userInfo.username"
|
||||
:max-length="64"
|
||||
size="large"
|
||||
:placeholder="t('login.form.userName.placeholder')"
|
||||
:placeholder="
|
||||
userInfo.authenticate !== 'LOCAL'
|
||||
? t('login.form.userName.placeholderOther')
|
||||
: t('login.form.userName.placeholder')
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
|
@ -91,6 +95,7 @@
|
|||
import useLoading from '@/hooks/useLoading';
|
||||
import { NO_PROJECT_ROUTE_NAME, NO_RESOURCE_ROUTE_NAME } from '@/router/constants';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import useLicenseStore from '@/store/modules/setting/license';
|
||||
import { encrypted } from '@/utils';
|
||||
import { setLoginExpires } from '@/utils/auth';
|
||||
import { getFirstRouteNameByPermission, routerNameHasPermission } from '@/utils/permission';
|
||||
|
@ -103,6 +108,7 @@
|
|||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const licenseStore = useLicenseStore();
|
||||
|
||||
const props = defineProps<{
|
||||
isPreview?: boolean;
|
||||
|
@ -181,7 +187,7 @@
|
|||
![NO_RESOURCE_ROUTE_NAME, NO_PROJECT_ROUTE_NAME].includes(redirect as string) &&
|
||||
routerNameHasPermission(redirect as string, router.getRoutes());
|
||||
const currentRouteName = getFirstRouteNameByPermission(router.getRoutes());
|
||||
const res = await getProjectInfo(appStore.currentProjectId);
|
||||
const [res] = await Promise.all([getProjectInfo(appStore.currentProjectId), licenseStore.getValidateLicense()]); // 登录前校验 license 避免进入页面后无license状态
|
||||
if (!res || res.deleted) {
|
||||
router.push({
|
||||
name: NO_PROJECT_ROUTE_NAME,
|
||||
|
|
|
@ -4,8 +4,9 @@ export default {
|
|||
'login.form.password.errMsg': 'Password cannot be empty',
|
||||
'login.form.login.errMsg': 'Login error, refresh and try again',
|
||||
'login.form.login.success': 'welcome to use',
|
||||
'login.form.userName.placeholder': 'Username',
|
||||
'login.form.password.placeholder': 'Password',
|
||||
'login.form.userName.placeholder': 'Please enter your email',
|
||||
'login.form.userName.placeholderOther': 'Please enter your account',
|
||||
'login.form.password.placeholder': 'Please enter your password',
|
||||
'login.form.rememberPassword': 'Remember password',
|
||||
'login.form.forgetPassword': 'Forgot password',
|
||||
'login.form.login': 'login',
|
||||
|
|
|
@ -5,7 +5,8 @@ export default {
|
|||
'login.form.login.errMsg': '登录出错,请刷新重试',
|
||||
'login.form.login.success': '欢迎使用',
|
||||
'login.form.userName.placeholder': '请输入邮箱登录',
|
||||
'login.form.password.placeholder': '密码',
|
||||
'login.form.userName.placeholderOther': '请输入账号登录',
|
||||
'login.form.password.placeholder': '请输入密码',
|
||||
'login.form.rememberPassword': '记住密码',
|
||||
'login.form.forgetPassword': '忘记密码',
|
||||
'login.form.login': '登录',
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import AllPrams from './allParams/index.vue';
|
||||
import RequestHeader from './requestHeader/index.vue';
|
||||
|
||||
import { updateOrAddGlobalParam } from '@/api/modules/project-management/envManagement';
|
||||
|
@ -38,8 +37,8 @@
|
|||
const headerParams = ref<EnvConfigItem[]>([]);
|
||||
const GlobalVariable = ref<EnvConfigItem[]>([]);
|
||||
const { t } = useI18n();
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
setState(true);
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
setIsSave(true);
|
||||
const canSave = ref(false);
|
||||
|
||||
const loading = ref(false);
|
||||
|
@ -64,7 +63,7 @@
|
|||
|
||||
function change() {
|
||||
canSave.value = true;
|
||||
setState(false);
|
||||
setIsSave(false);
|
||||
}
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
|
@ -85,7 +84,7 @@
|
|||
},
|
||||
};
|
||||
await updateOrAddGlobalParam(params);
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
Message.success(t('common.saveSuccess'));
|
||||
canSave.value = false;
|
||||
initEnvDetail();
|
||||
|
|
|
@ -101,8 +101,8 @@
|
|||
import { defaultHeaderParamsItem } from '@/views/api-test/components/config';
|
||||
import { filterKeyValParams } from '@/views/api-test/components/utils';
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
setState(false);
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
setIsSave(false);
|
||||
const emit = defineEmits<{
|
||||
(e: 'ok', envId: string | undefined): void;
|
||||
(e: 'resetEnv'): void;
|
||||
|
@ -231,7 +231,7 @@
|
|||
loading.value = true;
|
||||
store.currentEnvDetailInfo.mock = true;
|
||||
await updateOrAddEnv({ fileList: [], request: getParameters() });
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
|
||||
Message.success(store.currentEnvDetailInfo.id ? t('common.updateSuccess') : t('common.saveSuccess'));
|
||||
emit('ok', store.currentEnvDetailInfo.id);
|
||||
|
|
|
@ -284,7 +284,7 @@
|
|||
NEW_ENV_PARAM,
|
||||
} from '@/store/modules/setting/useProjectEnvStore';
|
||||
import { downloadByteFile } from '@/utils';
|
||||
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { EnvListItem, PopVisible } from '@/models/projectManagement/environmental';
|
||||
import { EnvAuthScopeEnum, EnvAuthTypeEnum } from '@/enums/envEnum';
|
||||
|
@ -293,8 +293,8 @@
|
|||
|
||||
const { openModal } = useModal();
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
setState(false);
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
setIsSave(false);
|
||||
|
||||
const { t } = useI18n();
|
||||
const store = useProjectEnvStore();
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<a-input
|
||||
v-else
|
||||
v-model:model-value="form.field"
|
||||
:max-length="props.fieldConfig?.maxLength || 50"
|
||||
:max-length="props.fieldConfig?.maxLength || 255"
|
||||
:placeholder="props.fieldConfig?.placeholder || t('project.fileManagement.namePlaceholder')"
|
||||
class="w-[245px]"
|
||||
@press-enter="beforeConfirm(undefined)"
|
||||
|
|
|
@ -670,7 +670,6 @@
|
|||
}
|
||||
|
||||
if (!hasAuth) {
|
||||
Message.error(t('common.noPermission'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -773,7 +772,6 @@
|
|||
}
|
||||
|
||||
if (!hasAuth) {
|
||||
Message.error(t('common.noPermission'));
|
||||
return;
|
||||
}
|
||||
await postUpdateMenu(
|
||||
|
|
|
@ -267,9 +267,9 @@
|
|||
const currentOrgId = computed(() => appStore.currentOrgId);
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
|
||||
setState(false);
|
||||
setIsSave(false);
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
@ -405,7 +405,7 @@
|
|||
router.push({ name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT, query: route.query });
|
||||
}
|
||||
|
||||
setState(true);
|
||||
setIsSave(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
@ -834,7 +834,6 @@
|
|||
background: var(--color-text-n9);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-form-item-layout-vertical > .arco-form-item-label-col) {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import type { FormItemType } from '@/components/pure/ms-form-create/types';
|
||||
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||
import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types';
|
||||
import type { FormItemModel } from '@/components/business/ms-batch-form/types';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
@ -252,7 +252,7 @@
|
|||
},
|
||||
]);
|
||||
const optionsModels: Ref<FormItemModel[]> = ref([]);
|
||||
const batchFormRef = ref<MsBatchFormInstance | null>(null);
|
||||
const batchFormRef = ref<InstanceType<typeof MsBatchForm>>();
|
||||
|
||||
const resetForm = () => {
|
||||
fieldForm.value = { ...initFieldForm };
|
||||
|
|
|
@ -5,6 +5,15 @@
|
|||
<a-button v-permission="['SYSTEM_PARAMETER_SETTING_AUTH:READ+ADD']" type="primary" @click="createAuth">
|
||||
{{ t('system.config.auth.add') }}
|
||||
</a-button>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('system.config.auth.searchTip')"
|
||||
class="w-[230px]"
|
||||
allow-clear
|
||||
@search="searchAuth"
|
||||
@press-enter="searchAuth"
|
||||
@clear="searchAuth"
|
||||
/>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
|
@ -684,7 +693,7 @@
|
|||
},
|
||||
];
|
||||
const tableStore = useTableStore();
|
||||
const { propsRes, propsEvent, loadList } = useTable(getAuthList, {
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getAuthList, {
|
||||
tableKey: TableKeyEnum.SYSTEM_AUTH,
|
||||
columns,
|
||||
scroll: { y: 'auto' },
|
||||
|
@ -696,6 +705,15 @@
|
|||
loadList();
|
||||
});
|
||||
|
||||
const keyword = ref('');
|
||||
|
||||
function searchAuth() {
|
||||
setLoadListParams({
|
||||
keyword: keyword.value,
|
||||
});
|
||||
loadList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用认证源
|
||||
*/
|
||||
|
|
|
@ -50,10 +50,10 @@
|
|||
<div :class="['config-preview', currentLocale === 'en-US' ? 'config-preview--en' : '']">
|
||||
<div ref="loginPageFullRef" class="login-preview">
|
||||
<div :class="['config-preview-head', isLoginPageFullscreen ? 'full-preview-head' : '']">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center justify-between overflow-hidden">
|
||||
<img v-if="pageConfig.icon[0]?.url" :src="pageConfig.icon[0].url" class="h-[18px] w-[18px]" />
|
||||
<svg-icon v-else name="logo" class="h-[18px] w-[18px]"></svg-icon>
|
||||
<div class="ml-[4px] text-[10px]">{{ pageConfig.title }}</div>
|
||||
<div class="one-line-text ml-[4px] text-[10px]">{{ pageConfig.title }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="w-[96px] cursor-pointer text-right !text-[var(--color-text-4)]"
|
||||
|
|
|
@ -96,6 +96,7 @@ export default {
|
|||
'system.config.page.unsave': 'Unsaved',
|
||||
'system.config.page.saveSuccess': 'Saved successfully',
|
||||
'system.config.auth.add': 'Add authentication',
|
||||
'system.config.auth.searchTip': 'Search by name',
|
||||
'system.config.auth.enable': 'Enable',
|
||||
'system.config.auth.enableSuccess': 'Enabled successfully',
|
||||
'system.config.auth.enableTipTitle': 'Enable authentication {name}',
|
||||
|
|
|
@ -92,6 +92,7 @@ export default {
|
|||
'system.config.page.unsave': '未保存',
|
||||
'system.config.page.saveSuccess': '保存成功',
|
||||
'system.config.auth.add': '添加认证',
|
||||
'system.config.auth.searchTip': '输入名称搜索',
|
||||
'system.config.auth.enable': '启用',
|
||||
'system.config.auth.enableSuccess': '启用成功',
|
||||
'system.config.auth.enableTipTitle': '启用认证 {name}',
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
:loading="loading"
|
||||
:title="title"
|
||||
:is-edit="isEdit"
|
||||
:save-text="t('system.resourcePool.add')"
|
||||
:save-and-continue-text="t('system.resourcePool.addAndContinue')"
|
||||
:handle-back="handleBack"
|
||||
has-breadcrumb
|
||||
@save="beforeSave"
|
||||
@save-and-continue="beforeSave(true)"
|
||||
|
@ -19,6 +22,7 @@
|
|||
v-model:model-value="form.name"
|
||||
:placeholder="t('system.resourcePool.namePlaceholder')"
|
||||
:max-length="255"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('system.resourcePool.desc')" field="description" class="form-item">
|
||||
|
@ -26,6 +30,7 @@
|
|||
v-model:model-value="form.description"
|
||||
:placeholder="t('system.resourcePool.descPlaceholder')"
|
||||
:max-length="1000"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('system.resourcePool.serverUrl')" field="serverUrl" class="form-item">
|
||||
|
@ -33,10 +38,11 @@
|
|||
v-model:model-value="form.serverUrl"
|
||||
:placeholder="t('system.resourcePool.rootUrlPlaceholder')"
|
||||
:max-length="255"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('system.resourcePool.orgRange')" field="orgType" class="form-item">
|
||||
<a-radio-group v-model:model-value="form.orgType">
|
||||
<a-radio-group v-model:model-value="form.orgType" @change="() => setIsSave(false)">
|
||||
<a-radio value="allOrg">
|
||||
{{ t('system.resourcePool.orgAll') }}
|
||||
<a-tooltip :content="t('system.resourcePool.orgRangeTip')" position="top" mini>
|
||||
|
@ -59,6 +65,7 @@
|
|||
:placeholder="t('system.resourcePool.orgPlaceholder')"
|
||||
multiple
|
||||
allow-clear
|
||||
@change="() => setIsSave(false)"
|
||||
>
|
||||
<a-option v-for="org of orgOptions" :key="org.id" :value="org.id">{{ org.name }}</a-option>
|
||||
</a-select>
|
||||
|
@ -70,7 +77,7 @@
|
|||
:rules="[{ required: true, message: t('system.resourcePool.useRequired') }]"
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-checkbox-group v-model:model-value="form.use">
|
||||
<a-checkbox-group v-model:model-value="form.use" @change="() => setIsSave(false)">
|
||||
<a-checkbox v-for="use of useList" :key="use.value" :value="use.value">{{ t(use.label) }}</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
<MsFormItemSub
|
||||
|
@ -191,6 +198,7 @@
|
|||
:default-vals="defaultVals"
|
||||
:hide-add="!isXpack"
|
||||
max-height="250px"
|
||||
@change="() => setIsSave(false)"
|
||||
></MsBatchForm>
|
||||
<!-- TODO:代码编辑器懒加载 -->
|
||||
<div v-show="form.addType === 'multiple'">
|
||||
|
@ -200,6 +208,7 @@
|
|||
height="400px"
|
||||
theme="MS-text"
|
||||
:show-theme-change="false"
|
||||
@change="() => setIsSave(false)"
|
||||
>
|
||||
<template #leftTitle>
|
||||
<a-form-item
|
||||
|
@ -228,6 +237,7 @@
|
|||
v-model:model-value="form.testResourceDTO.ip"
|
||||
:placeholder="t('system.resourcePool.testResourceDTO.ipPlaceholder')"
|
||||
:max-length="255"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input>
|
||||
<div class="mt-[4px] text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
||||
{{ t('system.resourcePool.testResourceDTO.ipSubTip', { ip: '100.0.0.100', domain: 'example.com' }) }}
|
||||
|
@ -245,6 +255,7 @@
|
|||
:placeholder="t('system.resourcePool.testResourceDTO.tokenPlaceholder')"
|
||||
:max-length="1500"
|
||||
autocomplete="new-password"
|
||||
@change="() => setIsSave(false)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
|
@ -259,6 +270,7 @@
|
|||
:placeholder="t('system.resourcePool.testResourceDTO.nameSpacesPlaceholder')"
|
||||
:max-length="255"
|
||||
class="mr-[8px] flex-1"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input>
|
||||
<a-tooltip
|
||||
:content="t('system.resourcePool.testResourceDTO.downloadRoleYamlTip')"
|
||||
|
@ -284,6 +296,7 @@
|
|||
:placeholder="t('system.resourcePool.testResourceDTO.deployNamePlaceholder')"
|
||||
:max-length="255"
|
||||
class="mr-[8px] flex-1"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input>
|
||||
<a-tooltip
|
||||
:content="t('system.resourcePool.testResourceDTO.downloadDeployYamlTip')"
|
||||
|
@ -314,6 +327,8 @@
|
|||
:step="1"
|
||||
mode="button"
|
||||
class="w-[160px]"
|
||||
model-event="input"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
|
@ -328,6 +343,8 @@
|
|||
:step="1"
|
||||
mode="button"
|
||||
class="w-[160px]"
|
||||
model-event="input"
|
||||
@change="() => setIsSave(false)"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
@ -351,19 +368,20 @@
|
|||
import { computed, onBeforeMount, Ref, ref, watch, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { cloneDeep, isEmpty } from 'lodash-es';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||
import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types';
|
||||
import type { FormItemModel } from '@/components/business/ms-batch-form/types';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
import JobTemplateDrawer from './components/jobTemplateDrawer.vue';
|
||||
|
||||
import { getSystemOrgOption } from '@/api/modules/setting/organizationAndProject';
|
||||
import { addPool, getPoolInfo, updatePoolInfo } from '@/api/modules/setting/resourcePool';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useLicenseStore from '@/store/modules/setting/license';
|
||||
|
@ -371,6 +389,7 @@
|
|||
import { scrollIntoView } from '@/utils/dom';
|
||||
|
||||
import type { NodesListItem, UpdateResourcePoolParams } from '@/models/setting/resourcePool';
|
||||
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { getYaml, job, YamlType } from './template';
|
||||
|
||||
|
@ -380,6 +399,7 @@
|
|||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
|
||||
const title = ref('');
|
||||
const loading = ref(false);
|
||||
|
@ -390,7 +410,7 @@
|
|||
description: '',
|
||||
serverUrl: '',
|
||||
orgType: 'allOrg',
|
||||
use: ['performance', 'API'],
|
||||
use: ['API'],
|
||||
type: 'Node',
|
||||
addType: 'single',
|
||||
testResourceDTO: {
|
||||
|
@ -409,7 +429,7 @@
|
|||
orgIds: [] as string[],
|
||||
},
|
||||
};
|
||||
const form = ref({ ...defaultForm });
|
||||
const form = ref({ ...cloneDeep(defaultForm) });
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const orgOptions = ref<SelectOptionData>([]);
|
||||
// TODO:第一版只有接口测试
|
||||
|
@ -427,7 +447,7 @@
|
|||
// value: 'UI',
|
||||
// },
|
||||
]);
|
||||
const defaultGrid = 'http://selenium-hub:4444';
|
||||
// const defaultGrid = 'http://selenium-hub:4444';
|
||||
const maxConcurrentNumber = computed(() => {
|
||||
if (isXpack.value) {
|
||||
return 9999999;
|
||||
|
@ -482,10 +502,10 @@
|
|||
}
|
||||
});
|
||||
|
||||
const defaultHeap = '-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m';
|
||||
function fillHeapByDefault() {
|
||||
form.value.testResourceDTO.loadTestHeap = defaultHeap;
|
||||
}
|
||||
// const defaultHeap = '-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m';
|
||||
// function fillHeapByDefault() {
|
||||
// form.value.testResourceDTO.loadTestHeap = defaultHeap;
|
||||
// }
|
||||
|
||||
const visitedKey = 'changeAddResourceType';
|
||||
const { addVisited, getIsVisited } = useVisit(visitedKey);
|
||||
|
@ -534,7 +554,7 @@
|
|||
}
|
||||
);
|
||||
|
||||
const batchFormRef = ref<MsBatchFormInstance | null>(null);
|
||||
const batchFormRef = ref<InstanceType<typeof MsBatchForm>>();
|
||||
const batchFormModels: Ref<FormItemModel[]> = ref([
|
||||
{
|
||||
filed: 'ip',
|
||||
|
@ -580,7 +600,7 @@
|
|||
// 动态表单默认值
|
||||
const defaultVals = computed(() => {
|
||||
const { nodesList } = form.value.testResourceDTO;
|
||||
return nodesList.map((node) => node);
|
||||
return nodesList.map((node) => cloneDeep(node));
|
||||
});
|
||||
|
||||
// 代码编辑器内容
|
||||
|
@ -606,9 +626,9 @@
|
|||
* 提取动态表单项输入的内容
|
||||
*/
|
||||
function setBatchFormRes() {
|
||||
const res = batchFormRef.value?.getFormResult<NodesListItem>();
|
||||
const res = batchFormRef.value?.getFormResult();
|
||||
if (res?.length) {
|
||||
form.value.testResourceDTO.nodesList = res.map((e) => e);
|
||||
form.value.testResourceDTO.nodesList = res.map((e) => e) as NodesListItem[];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,12 +672,14 @@
|
|||
// 从单个添加切换到批量添加,需要先提取组件的输入框内容
|
||||
setBatchFormRes();
|
||||
}
|
||||
setIsSave(false);
|
||||
}
|
||||
|
||||
function changeResourceType(val: string | number | boolean) {
|
||||
if (val === 'Kubernetes') {
|
||||
setBatchFormRes();
|
||||
}
|
||||
setIsSave(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -699,7 +721,7 @@
|
|||
* 重置表单信息
|
||||
*/
|
||||
function resetForm() {
|
||||
form.value = { ...defaultForm };
|
||||
form.value = { ...cloneDeep(defaultForm) };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -841,6 +863,10 @@
|
|||
return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||
});
|
||||
}
|
||||
|
||||
function handleBack() {
|
||||
router.replace({ name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -287,6 +287,12 @@
|
|||
try {
|
||||
const res = await getPoolInfo(id);
|
||||
if (res) {
|
||||
if (res.deleted) {
|
||||
Message.warning(t('common.resourceDeleted'));
|
||||
drawerLoading.value = false;
|
||||
showDetailDrawer.value = false;
|
||||
return;
|
||||
}
|
||||
activePool.value = res;
|
||||
const poolUses = [
|
||||
activePool.value.apiTest ? t('system.resourcePool.useAPI') : '',
|
||||
|
|
|
@ -20,14 +20,14 @@ export default {
|
|||
'system.resourcePool.disablePoolSuccess': 'Disabled successfully',
|
||||
'system.resourcePool.deletePoolTip': 'Are you sure to delete the `{name}` resource?',
|
||||
'system.resourcePool.deletePoolContentUsed':
|
||||
'This resource pool has been used, and related tests will stop immediately after deletion, please operate with caution!',
|
||||
'If this resource pool has been used, and related tests will stop immediately after deletion, please operate with caution!',
|
||||
'system.resourcePool.deletePoolContentUnuse': 'This resource pool is not in use. Are you sure to delete it?',
|
||||
'system.resourcePool.deletePoolConfirm': 'Confirm',
|
||||
'system.resourcePool.deletePoolCancel': 'Cancel',
|
||||
'system.resourcePool.deletePoolSuccess': 'Deleted successfully',
|
||||
'system.resourcePool.detailDesc': 'Description',
|
||||
'system.resourcePool.detailUrl': 'Current site URL',
|
||||
'system.resourcePool.detailRange': 'Available range',
|
||||
'system.resourcePool.detailRange': 'Applied organization',
|
||||
'system.resourcePool.detailUse': 'Use',
|
||||
'system.resourcePool.detailMirror': 'Mirror',
|
||||
'system.resourcePool.detailJMHeap': 'JMeter HEAP',
|
||||
|
@ -43,7 +43,7 @@ export default {
|
|||
'system.resourcePool.descPlaceholder': 'Please describe the resource pool',
|
||||
'system.resourcePool.serverUrl': 'Current site URL',
|
||||
'system.resourcePool.rootUrlPlaceholder': 'MS deployment address',
|
||||
'system.resourcePool.orgRange': 'Application organization',
|
||||
'system.resourcePool.orgRange': 'Applied organization',
|
||||
'system.resourcePool.orgAll': 'All organization',
|
||||
'system.resourcePool.orgSetup': 'Specified organization',
|
||||
'system.resourcePool.orgSelect': 'specified organization',
|
||||
|
@ -122,4 +122,6 @@ export default {
|
|||
'system.resourcePool.addSuccess': 'Added resource pool successfully',
|
||||
'system.resourcePool.updateSuccess': 'Resource pool updated successfully',
|
||||
'system.resourcePool.atLeastOnePool': 'Reserve at least one resource pool',
|
||||
'system.resourcePool.add': 'Add',
|
||||
'system.resourcePool.addAndContinue': 'Save and continue adding',
|
||||
};
|
||||
|
|
|
@ -19,14 +19,14 @@ export default {
|
|||
'system.resourcePool.disablePoolCancel': '取消',
|
||||
'system.resourcePool.disablePoolSuccess': '禁用成功',
|
||||
'system.resourcePool.deletePoolTip': '确认删除 `{name}` 这个资源吗?',
|
||||
'system.resourcePool.deletePoolContentUsed': '该资源池已被使用,删除后相关测试会立即停止,请谨慎操作!',
|
||||
'system.resourcePool.deletePoolContentUsed': '若该资源池已被使用,删除后相关测试会立即停止,请谨慎操作!',
|
||||
'system.resourcePool.deletePoolContentUnuse': '该资源池未被使用,是否确认删除?',
|
||||
'system.resourcePool.deletePoolConfirm': '确认删除',
|
||||
'system.resourcePool.deletePoolCancel': '取消',
|
||||
'system.resourcePool.deletePoolSuccess': '删除成功',
|
||||
'system.resourcePool.detailDesc': '描述',
|
||||
'system.resourcePool.detailUrl': '当前站点 URL',
|
||||
'system.resourcePool.detailRange': '可用范围',
|
||||
'system.resourcePool.detailRange': '应用组织',
|
||||
'system.resourcePool.detailUse': '用途',
|
||||
'system.resourcePool.detailMirror': '镜像',
|
||||
'system.resourcePool.detailJMHeap': 'JMeter HEAP',
|
||||
|
@ -67,7 +67,7 @@ export default {
|
|||
'system.resourcePool.batchAdd': '批量添加',
|
||||
'system.resourcePool.batchAddTipConfirm': '知道了',
|
||||
'system.resourcePool.batchAddResource': '批量添加资源',
|
||||
'system.resourcePool.changeAddTypeTip': '切换后,已添加资源内容将继续现在 yaml 内;可批量修改已添加资源',
|
||||
'system.resourcePool.changeAddTypeTip': '切换后,已添加资源内容将继续显示在 yaml 内;可批量修改已添加资源',
|
||||
'system.resourcePool.changeAddTypePopTitle': '切换添加资源类型?',
|
||||
'system.resourcePool.allUseTip': '如果配置多个测试类型,会存在抢占资源的情况,建议一种测试类型配置一个资源池',
|
||||
'system.resourcePool.ip': 'IP',
|
||||
|
@ -117,4 +117,6 @@ export default {
|
|||
'system.resourcePool.addSuccess': '添加资源池成功',
|
||||
'system.resourcePool.updateSuccess': '更新资源池成功',
|
||||
'system.resourcePool.atLeastOnePool': '至少保留一个资源池',
|
||||
'system.resourcePool.add': '添加',
|
||||
'system.resourcePool.addAndContinue': '保存并继续添加',
|
||||
};
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button type="secondary" :disabled="loading" @click="cancelCreate">
|
||||
<a-button type="secondary" :disabled="loading" @click="handleBeforeClose">
|
||||
{{ t('system.user.editUserModalCancelCreate') }}
|
||||
</a-button>
|
||||
<a-button v-if="userFormMode === 'create'" type="secondary" :loading="loading" @click="saveAndContinue">
|
||||
|
@ -302,7 +302,7 @@
|
|||
import MsTagGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
|
||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||
import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types';
|
||||
import type { FormItemModel } from '@/components/business/ms-batch-form/types';
|
||||
import MsSelect from '@/components/business/ms-select';
|
||||
import batchModal from './components/batchModal.vue';
|
||||
import inviteModal from './components/inviteModal.vue';
|
||||
|
@ -816,7 +816,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
const batchFormRef = ref<MsBatchFormInstance | null>(null);
|
||||
const batchFormRef = ref<InstanceType<typeof MsBatchForm>>();
|
||||
const batchFormModels: Ref<FormItemModel[]> = ref([
|
||||
{
|
||||
filed: 'name',
|
||||
|
@ -933,7 +933,7 @@
|
|||
} else {
|
||||
Message.success(t('system.user.addUserSuccess'));
|
||||
if (!isContinue) {
|
||||
visible.value = false;
|
||||
cancelCreate();
|
||||
}
|
||||
loadList();
|
||||
}
|
||||
|
@ -981,6 +981,7 @@
|
|||
userFormValidate(async () => {
|
||||
await createUser(true);
|
||||
resetUserForm();
|
||||
batchFormRef.value?.resetForm();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const currentKeyword = ref('');
|
||||
const ugLeftRef = ref();
|
||||
const ugLeftRef = ref<InstanceType<typeof UserGroupLeft>>();
|
||||
|
||||
const currentUserGroupItem = ref<CurrentUserGroupItem>({
|
||||
id: '',
|
||||
|
@ -147,7 +147,7 @@
|
|||
}
|
||||
});
|
||||
onMounted(() => {
|
||||
ugLeftRef.value?.initData(router.currentRoute.value.query.id, true);
|
||||
ugLeftRef.value?.initData(router.currentRoute.value.query.id as string, true);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in New Issue