fix(全局): 部分 bug 修复

This commit is contained in:
baiqi 2024-06-21 16:47:59 +08:00 committed by Craftsman
parent e4b1c29dc8
commit 413c7fc7b3
44 changed files with 542 additions and 208 deletions

View File

@ -188,8 +188,8 @@ export function saveCaseMinder(data: FeatureCaseMinderUpdateParams) {
}
// 获取脑图
export function getCaseMinder(data: { projectId: string; moduleId: string }) {
return MSR.post<MinderJsonNode[]>({ url: `${GetCaseMinderUrl}`, data });
export function getCaseMinder(data: { projectId: string; moduleId: string; current: number }) {
return MSR.post<CommonList<MinderJsonNode>>({ url: `${GetCaseMinderUrl}`, data });
}
// 获取脑图模块树(包含文本节点)

View File

@ -370,6 +370,7 @@
.ms-scroll-bar();
padding-right: 16px;
min-height: 30px;
}
.arco-textarea-wrapper {
resize: vertical !important;

View File

@ -138,7 +138,6 @@
</template>
<script lang="ts" setup>
import { defineModel } from 'vue';
import { useVModel } from '@vueuse/core';
import { cloneDeep } from 'lodash-es';
import { VueDraggable } from 'vue-draggable-plus';

View File

@ -7,14 +7,14 @@ export default {
'ms.case.associate.version': '版本',
'ms.case.associate.versionPlaceholder': '默认最新版本',
'ms.case.associate.tags': '标签',
'ms.case.associate.searchPlaceholder': '通过 ID名称搜索',
'ms.case.associate.searchPlaceholder': '通过 ID/名称搜索',
'ms.case.associate.associateSuccess': '关联成功',
'ms.case.associate.functionalCase': '功能用例',
'ms.case.associate.apiCase': '接口用例',
'ms.case.associate.apiScenarioCase': '接口场景用例',
'ms.case.associate.UIScenario': 'UI场景用例',
'ms.case.associate.performanceCase': '性能用例',
'ms.case.associate.testSet': '测试',
'ms.case.associate.testSet': '测试',
'ms.case.associate.addAssociatedCase': '添加已关联用例',
'ms.case.associate.automaticallyAddApiCase': '自动添加已关联的接口用例',
'ms.case.associate.project': '项目',

View File

@ -16,7 +16,7 @@
>
<div
v-for="(element, index) in form.list"
:key="`${element.filed}${index}`"
:key="`${element.field}${index}`"
class="draggableElement gap-[8px] py-[6px] pr-[8px]"
:class="[props.isShowDrag ? 'cursor-move' : '']"
>
@ -25,14 +25,14 @@
/></div>
<a-form-item
v-for="model of props.models"
:key="`${model.filed}${index}`"
:field="`list[${index}].${model.filed}`"
:key="`${model.field}${index}`"
:field="`list[${index}].${model.field}`"
:class="index > 0 ? 'hidden-item' : 'mb-0 flex-1'"
:rules="
model.rules?.map((e) => {
if (e.notRepeat === true) {
return {
validator: (val, callback) => fieldNotRepeat(val, callback, index, model.filed, e.message),
validator: (val, callback) => fieldNotRepeat(val, callback, index, model.field, e.message),
};
}
return e;
@ -59,17 +59,30 @@
</template>
<a-input
v-if="model.type === 'input'"
v-model:model-value="element[model.filed]"
v-model:model-value="element[model.field]"
class="flex-1"
:placeholder="t(model.placeholder || '')"
:max-length="model.maxLength || 255"
allow-clear
@change="emit('change')"
/>
<MsQuickInput
v-else-if="model.type === 'textarea'"
v-model:model-value="element[model.field]"
class="flex-1"
type="textarea"
@change="
() => {
formRef?.validateField(`list[${index}].${model.field}`);
emit('change');
}
"
>
</MsQuickInput>
<a-tooltip v-else-if="model.type === 'inputNumber'" position="tl" mini :disabled="!model.tooltip">
<a-input-number
v-if="model.type === 'inputNumber'"
v-model:model-value="element[model.filed]"
v-model:model-value="element[model.field]"
class="flex-1"
:placeholder="t(model.placeholder || '')"
:min="model.min"
@ -89,7 +102,7 @@
</a-tooltip>
<MsTagsInput
v-else-if="model.type === 'tagInput'"
v-model:model-value="element[model.filed]"
v-model:model-value="element[model.field]"
class="flex-1"
:placeholder="t(model.placeholder || 'common.tagPlaceholder')"
allow-clear
@ -100,7 +113,7 @@
/>
<a-select
v-else-if="model.type === 'select'"
v-model="element[model.filed]"
v-model="element[model.field]"
class="flex-1"
:placeholder="t(model.placeholder || '')"
:options="model.options"
@ -110,8 +123,8 @@
<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}`"
:field="`list[${index}].${child.filed}`"
:key="`${child.field}${childIndex}${index}`"
:field="`list[${index}].${child.field}`"
:label="child.label ? t(child.label) : ''"
asterisk-position="end"
:hide-asterisk="child.hideAsterisk"
@ -121,7 +134,7 @@
>
<a-input
v-if="child.type === 'input'"
v-model="element[child.filed]"
v-model="element[child.field]"
:class="child.className"
:placeholder="t(child.placeholder || '')"
:max-length="child.maxLength || 255"
@ -130,13 +143,29 @@
/>
<a-select
v-else-if="child.type === 'select'"
v-model="element[child.filed]"
v-model="element[child.field]"
:class="child.className"
:placeholder="t(child.placeholder || '')"
:options="child.options"
:field-names="child.filedNames"
@change="emit('change')"
/>
<MsQuickInput
v-else-if="child.type === 'textarea'"
v-model="element[child.field]"
type="textarea"
:class="child.className"
:placeholder="t(child.placeholder || '')"
:max-length="child.maxLength || 255"
:title="child.title"
allow-clear
@change="
() => {
formRef?.validateField(`list[${index}].${child.field}`);
emit('change');
}
"
/>
</a-form-item>
</div>
</a-form-item>
@ -199,6 +228,7 @@
import { VueDraggable } from 'vue-draggable-plus';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import MsQuickInput from '@/components/business/ms-quick-input/index.vue';
import { useI18n } from '@/hooks/useI18n';
import { scrollIntoView } from '@/utils/dom';
@ -253,19 +283,19 @@
} else {
value = e.defaultValue;
}
formItem[e.filed] = value;
formItem[e.field] = value;
if (props.showEnable) {
//
formItem.enable = false;
}
//
e.children?.forEach((child) => {
formItem[child.filed] = child.type === 'inputNumber' ? null : child.defaultValue;
formItem[child.field] = child.type === 'inputNumber' ? null : child.defaultValue;
});
});
form.value.list = [{ ...formItem }];
if (props.defaultVals?.length) {
// defaultVals filed
// defaultVals field
form.value.list = props.defaultVals.map((e) => e);
}
});

View File

@ -1,6 +1,6 @@
import { FieldRule, SelectFieldNames, SelectOptionData, SelectOptionGroup } from '@arco-design/web-vue';
export type FormItemType = 'input' | 'select' | 'inputNumber' | 'tagInput' | 'multiple' | 'switch';
export type FormItemType = 'input' | 'select' | 'inputNumber' | 'tagInput' | 'multiple' | 'switch' | 'textarea';
export type FormMode = 'create' | 'edit';
export type ValueType = 'Array' | 'string';
// 自定义检验器,为了传入动态渲染的表单项下标
@ -9,7 +9,7 @@ export interface CustomValidator {
}
export interface FormItemModel {
filed: string;
field: string;
type: FormItemType;
rules?: (FieldRule & CustomValidator)[];
label?: string;
@ -26,4 +26,5 @@ export interface FormItemModel {
defaultValue?: string | string[] | number | number[] | boolean; // 默认值
hasRedStar?: boolean; // 是否有红星
tooltip?: string;
[key: string]: any;
}

View File

@ -7,6 +7,6 @@ export default {
'ms.case.associate.version': '版本',
'ms.case.associate.versionPlaceholder': '默认最新版本',
'ms.case.associate.tags': '标签',
'ms.case.associate.searchPlaceholder': '通过 ID名称搜索',
'ms.case.associate.searchPlaceholder': '通过 ID/名称搜索',
'ms.case.associate.associateSuccess': '关联成功',
};

View File

@ -34,7 +34,6 @@
</template>
<script lang="ts" setup>
import { defineModel, ref } from 'vue';
import dayjs from 'dayjs';
import MsAvatar from '@/components/pure/ms-avatar/index.vue';

View File

@ -41,8 +41,6 @@
</template>
<script lang="ts" setup>
import { defineModel, ref } from 'vue';
import MsAvatar from '@/components/pure/ms-avatar/index.vue';
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';

View File

@ -147,8 +147,6 @@
</template>
<script setup lang="ts">
import { defineModel, ref } from 'vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
import { Language } from '@/components/pure/ms-code-editor/types';

View File

@ -353,47 +353,25 @@
}
}
const showDetailMenu = ref(false);
const canShowEnterNode = ref(false);
/**
* 处理脑图节点激活/点击
* @param node 被激活/点击的节点
* 初始化模块节点下的用例节点
* @param node 选中节点
*/
async function handleNodeSelect(node: MinderJsonNode) {
checkNodeCanShowMenu(node);
const { data } = node;
if (
data?.resource?.includes(moduleTag) &&
(node.children || []).length > 0 &&
node.type !== 'root' &&
!data.isNew
) {
//
canShowEnterNode.value = true;
} else {
canShowEnterNode.value = false;
}
if (data?.resource && data.resource.includes(caseTag)) {
//
showDetailMenu.value = true;
if (extraVisible.value) {
toggleDetail(true);
}
} else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) {
//
async function initNodeCases(node: MinderJsonNode) {
try {
const { data } = node;
if (!data) return;
loading.value = true;
showDetailMenu.value = false;
extraVisible.value = false;
const res = await getCaseMinder({
const { list, total } = await getCaseMinder({
projectId: appStore.currentProjectId,
moduleId: data.id,
current: 1,
});
const fakeNode = node.children?.find((e) => e.data?.id === 'fakeNode'); //
if (fakeNode) {
window.minder.removeNode(fakeNode);
}
if ((!res || res.length === 0) && node.children?.length) {
if ((!list || list.length === 0) && node.children?.length) {
//
node.expand();
window.minder.renderNodeBatch(node.children);
@ -403,7 +381,7 @@
}
// TODO:
let waitingRenderNodes: MinderJsonNode[] = [];
res.forEach((e) => {
list.forEach((e) => {
//
const child = window.minder.createNode(
{
@ -448,6 +426,20 @@
if (node.children && node.children.length > 0) {
waitingRenderNodes = waitingRenderNodes.concat(node.children);
}
if (total > list.length) {
//
const moreNode = window.minder.createNode(
{
id: `tmp-${data.id}`,
text: '...',
type: 'tmp',
expandState: 'collapse',
current: 1,
},
node
);
waitingRenderNodes.push(moreNode);
}
window.minder.renderNodeBatch(waitingRenderNodes);
node.layout();
data.isLoaded = true;
@ -459,6 +451,138 @@
} finally {
loading.value = false;
}
}
/**
* 加载模块下更多用例
* @param node 模块节点
* @param current 当前页码
*/
async function loadMoreCases(node: MinderJsonNode, current: number) {
try {
const { data } = node;
if (!data) return;
loading.value = true;
const { list, total } = await getCaseMinder({
projectId: appStore.currentProjectId,
moduleId: data.id,
current: current + 1,
});
const fakeNode = node.children?.find((e) => e.data?.id === `tmp-${data.id}`); //
if (fakeNode) {
window.minder.removeNode(fakeNode);
}
// TODO:
let waitingRenderNodes: MinderJsonNode[] = [];
list.forEach((e) => {
//
const child = window.minder.createNode(
{
...e.data,
expandState: 'collapse',
isNew: false,
},
node
);
waitingRenderNodes.push(child);
const grandChildren: MinderJsonNode[] = [];
e.children?.forEach((item) => {
// //
const grandChild = window.minder.createNode(
{
...item.data,
expandState: 'collapse',
isNew: false,
},
child
);
grandChildren.push(grandChild);
const greatGrandChildren: MinderJsonNode[] = [];
item.children?.forEach((subItem) => {
//
const greatGrandChild = window.minder.createNode(
{
...subItem.data,
expandState: 'collapse',
isNew: false,
},
grandChild
);
greatGrandChildren.push(greatGrandChild);
});
window.minder.renderNodeBatch(greatGrandChildren);
});
window.minder.renderNodeBatch(grandChildren);
});
node.expand();
// node.renderTree();
if (node.children && node.children.length > 0) {
waitingRenderNodes = waitingRenderNodes.concat(node.children);
}
if (total > list.length * current) {
//
const moreNode = window.minder.createNode(
{
id: `tmp-${data.id}`,
text: '...',
type: 'tmp',
expandState: 'collapse',
current: current + 1,
},
node
);
waitingRenderNodes.push(moreNode);
}
window.minder.renderNodeBatch(waitingRenderNodes);
node.layout();
data.isLoaded = true;
// importJson
replaceNodeInTree([importJson.value.root], node.data?.id || '', window.minder.exportNode(node), 'data', 'id');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
const showDetailMenu = ref(false);
const canShowEnterNode = ref(false);
/**
* 处理脑图节点激活/点击
* @param node 被激活/点击的节点
*/
async function handleNodeSelect(node: MinderJsonNode) {
const { data } = node;
checkNodeCanShowMenu(node);
if (data?.type === 'tmp' && node.parent?.data?.resource?.includes(moduleTag)) {
//
await loadMoreCases(node.parent, data.current);
setPriorityView(true, 'P');
return;
}
if (
data?.resource?.includes(moduleTag) &&
(node.children || []).length > 0 &&
node.type !== 'root' &&
!data.isNew
) {
//
canShowEnterNode.value = true;
} else {
canShowEnterNode.value = false;
}
if (data?.resource && data.resource.includes(caseTag)) {
//
showDetailMenu.value = true;
if (extraVisible.value) {
toggleDetail(true);
}
} else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) {
//
await initNodeCases(node);
showDetailMenu.value = false;
extraVisible.value = false;
} else {
//
extraVisible.value = false;

View File

@ -35,6 +35,10 @@ export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermiss
function canShowFloatMenu() {
if (window.minder) {
const node: MinderJsonNode = window.minder.getSelectedNode();
if (node.data?.type === 'tmp') {
// 临时节点不展示浮动菜单
return false;
}
if (!hasEditPermission) {
if (node?.data?.resource?.includes(caseTag)) {
// 没有编辑权限情况下,用例节点可展示浮动菜单(需要展示详情按钮)

View File

@ -379,6 +379,7 @@
}
}
const inInsertingNode = ref(false);
/**
* 执行插入节点
* @param command 插入命令
@ -447,6 +448,7 @@
};
}
if (child) {
inInsertingNode.value = true;
execInert(type, child);
nextTick(() => {
execInert('AppendChildNode', caseCountNodeData);
@ -454,6 +456,9 @@
//
execInert('AppendSiblingNode', envNodeData);
execInert('AppendSiblingNode', resourcePoolNodeData);
setTimeout(() => {
inInsertingNode.value = false;
}, 0);
}
});
}
@ -669,7 +674,10 @@
if (node.data?.level === 3 && node.data?.resource?.[0] === caseCountTag) {
window.minder.toggleSelect(node);
window.minder.selectById(node.parent?.data?.id);
if (!inInsertingNode.value && hasEditPermission && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION'])) {
//
associateCase();
}
} else if (
node.data?.level === 3 &&
(node.data?.resource?.[0] === resourcePoolTag || node.data?.resource?.[0] === envTag)

View File

@ -0,0 +1,148 @@
<template>
<a-popover position="tl" :disabled="!modelValue || modelValue.trim() === ''" class="ms-params-input-popover">
<template #content>
<div v-if="props.title" class="param-popover-title">
{{ props.title }}
</div>
<div class="param-popover-value">
{{ modelValue }}
</div>
</template>
<a-input
v-if="props.type === 'input'"
ref="inputRef"
v-model:model-value="modelValue"
:class="props.class"
:disabled="props.disabled"
:size="props.size"
:max-length="props.maxLength"
:placeholder="props.placeholder"
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
:allow-clear="props.allowClear"
@input="(val) => emit('input', val)"
@change="(val) => emit('change', val)"
/>
<a-textarea
v-else
ref="inputRef"
v-model:model-value="modelValue"
:class="props.class"
:disabled="props.disabled"
:size="props.size"
:placeholder="props.placeholder"
:max-length="props.maxLength"
:auto-size="{ minRows: 1, maxRows: 1 }"
:allow-clear="props.allowClear"
@input="(val) => emit('input', val)"
@change="(val) => emit('change', val)"
/>
</a-popover>
<a-modal
v-model:visible="showQuickInput"
:title="props.title"
:ok-text="t('common.save')"
:ok-button-props="{ disabled: !quickInputValue || quickInputValue.trim() === '' }"
class="ms-modal-form"
body-class="!p-0"
:width="480"
title-align="start"
@ok="applyQuickInputDesc"
@close="clearQuickInputDesc"
>
<a-textarea
v-model:model-value="quickInputValue"
:placeholder="props.placeholder"
:auto-size="{ minRows: 2 }"
:max-length="1000"
></a-textarea>
</a-modal>
</template>
<script setup lang="ts">
import { useEventListener } from '@vueuse/core';
import { useI18n } from '@/hooks/useI18n';
const props = withDefaults(
defineProps<{
type?: 'input' | 'textarea';
title?: string;
placeholder?: string;
disabled?: boolean;
size?: 'small' | 'large' | 'medium' | 'mini';
maxLength?: number;
class?: string;
allowClear?: boolean;
}>(),
{
type: 'input',
title: '',
disabled: false,
size: 'medium',
maxLength: 255,
}
);
const emit = defineEmits<{
(e: 'input', val: string): void;
(e: 'change', val: string): void;
(e: 'dblclick'): void;
}>();
const { t } = useI18n();
const modelValue = defineModel<string>('modelValue', {
default: '',
});
const inputRef = ref<HTMLElement>();
const showQuickInput = ref(false);
const quickInputValue = ref('');
function quickInputDesc() {
showQuickInput.value = true;
quickInputValue.value = modelValue.value;
}
function clearQuickInputDesc() {
quickInputValue.value = '';
}
function applyQuickInputDesc() {
modelValue.value = quickInputValue.value;
emit('change', quickInputValue.value);
clearQuickInputDesc();
showQuickInput.value = false;
}
onMounted(() => {
useEventListener(inputRef.value, 'dblclick', () => {
quickInputDesc();
});
});
</script>
<style lang="less" scoped>
.param-input:not(.arco-input-focus) {
&:not(:hover) {
@apply bg-transparent;
border-color: transparent;
}
}
.param-popover-title {
@apply font-medium;
margin-bottom: 4px;
font-size: 12px;
font-weight: 500;
line-height: 16px;
color: var(--color-text-1);
}
.param-popover-value {
min-width: 100px;
max-width: 280px;
font-size: 12px;
line-height: 16px;
color: var(--color-text-1);
}
</style>

View File

@ -399,6 +399,10 @@
(val) => {
if (typeof val === 'boolean') {
treeRef.value?.expandAll(val);
filterTreeData.value = mapTree(filterTreeData.value, (node) => {
node.expanded = val;
return node;
});
}
}
);

View File

@ -72,8 +72,6 @@
</template>
<script setup lang="ts">
import { defineModel } from 'vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsTag from '../ms-tag/ms-tag.vue';
import FilterForm from './FilterForm.vue';

View File

@ -23,7 +23,6 @@
</template>
<script setup lang="ts">
import { defineModel } from 'vue';
import { Message } from '@arco-design/web-vue';
import { validateJIRAKey } from '@/api/modules/project-management/menuManagement';

View File

@ -293,7 +293,6 @@
</template>
<script lang="ts" setup>
import { computed, defineModel, nextTick, onMounted, ref, useAttrs, watch } from 'vue';
import { Message } from '@arco-design/web-vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';

View File

@ -4,7 +4,9 @@
<template #content>
<div class="mb-2 flex items-center justify-between">
<div class="font-medium text-[var(--color-text-1)]">{{ t('msTable.columnSetting.display') }}</div>
<MsButton :disabled="!hasChange" @click="handleReset">{{ t('msTable.columnSetting.resetDefault') }}</MsButton>
<MsButton v-if="!props.onlyPageSize" :disabled="!hasChange" @click="handleReset">
{{ t('msTable.columnSetting.resetDefault') }}
</MsButton>
</div>
<template v-if="props.showPagination">
<div class="font-medium text-[var(--color-text-4)]">{{ t('msTable.columnSetting.pageSize') }} </div>

View File

@ -165,8 +165,8 @@ export default {
'common.resourceDeleted': '资源已被删除',
'common.resourceExpired': '链接已失效,请重新获取',
'common.refresh': '刷新',
'common.searchByIdName': '通过ID 或名称搜索',
'common.searchByIDNameTag': '通过ID、名称或标签搜索',
'common.searchByIdName': '通过 ID/名称搜索',
'common.searchByIDNameTag': '通过 ID/名称/标签搜索',
'common.archive': '归档',
'common.running': '执行中',
'common.unExecute': '未执行',

View File

@ -595,13 +595,13 @@
body-class="!p-0"
:width="480"
title-align="start"
:auto-size="{ minRows: 2 }"
@ok="applyQuickInputDesc"
@close="clearQuickInputDesc"
>
<a-textarea
v-model:model-value="quickInputDescValue"
:placeholder="t('apiTestDebug.descPlaceholder')"
:auto-size="{ minRows: 2 }"
:max-length="1000"
></a-textarea>
</a-modal>

View File

@ -1,7 +1,7 @@
export default {
'apiTestDebug.newApi': '新建请求',
'apiTestDebug.importApi': '导入请求',
'apiTestDebug.urlPlaceholder': '请输入包含 httphttps 的完整URL',
'apiTestDebug.urlPlaceholder': '请输入包含 http/https 的完整URL',
'apiTestDebug.definitionUrlPlaceholder': '输入接口URL以“/”开始',
'apiTestDebug.serverExec': '服务端执行',
'apiTestDebug.localExec': '本地执行',

View File

@ -146,7 +146,7 @@ export default {
'apiTestManagement.quoteType': '引用类型',
'apiTestManagement.belongOrg': '所属组织',
'apiTestManagement.belongProject': '所属项目',
'apiTestManagement.quoteSearchPlaceholder': '输入 ID名称搜索',
'apiTestManagement.quoteSearchPlaceholder': '输入 ID/名称搜索',
'apiTestManagement.click': '点击',
'apiTestManagement.getResponse': '获取响应内容',
'apiTestManagement.tableNoDataAndPlease': '暂无数据,请',

View File

@ -41,7 +41,7 @@ export default {
'apiScenario.params.csvQuoteAllow': '允许带引号',
'apiScenario.params.csvRecycle': '遇到文件结束符再次循环',
'apiScenario.params.csvStop': '遇到文件结束符停止线程',
'apiScenario.params.searchPlaceholder': '通过名称标签搜索',
'apiScenario.params.searchPlaceholder': '通过名称/标签搜索',
'apiScenario.params.priority':
'变量优先级:临时参数>场景参数 >环境参数>全局参数;注: 避免使用同名变量,同名变量时场景级 CSV 优先级最高',
'apiScenario.params.name': '变量名称',

View File

@ -168,7 +168,6 @@
</template>
<script setup lang="ts">
import { defineModel, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useClipboard } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';

View File

@ -119,7 +119,7 @@ export default {
permanentlyDeleteTip: '是否彻底删除 {name} 缺陷?',
deleteContent: '删除后,缺陷无法恢复,请谨慎操作!',
batchDelete: '是否彻底删除{count}条缺陷?',
searchPlaceholder: '通过 ID名称搜索',
searchPlaceholder: '通过 ID/名称搜索',
deleteTime: '删除时间',
deleteMan: '删除人',
},

View File

@ -13,8 +13,8 @@ export default {
'caseManagement.featureCase.rename': '重命名',
'caseManagement.featureCase.recycle': '回收站',
'caseManagement.featureCase.versionPlaceholder': '默认为最新版本',
'caseManagement.featureCase.searchByNameAndId': '通过ID、名称或标签搜索',
'caseManagement.featureCase.searchByIdAndName': '通过ID 或名称搜索',
'caseManagement.featureCase.searchByNameAndId': '通过 ID/名称/标签搜索',
'caseManagement.featureCase.searchByIdAndName': '通过 ID/名称搜索',
'caseManagement.featureCase.searchByName': '通过名称搜索',
'caseManagement.featureCase.filter': '筛选',
'caseManagement.featureCase.setFilterCondition': '设置筛选条件',

View File

@ -2,8 +2,8 @@ export default {
'caseManagement.caseReview.create': '创建评审',
'caseManagement.caseReview.waitMyReview': '我评审的',
'caseManagement.caseReview.myCreate': '我创建的',
'caseManagement.caseReview.searchPlaceholder': '通过 ID名称搜索',
'caseManagement.caseReview.list.searchPlaceholder': '通过ID、名称或标签搜索',
'caseManagement.caseReview.searchPlaceholder': '通过 ID/名称搜索',
'caseManagement.caseReview.list.searchPlaceholder': '通过 ID/名称/标签搜索',
'caseManagement.caseReview.archive': '归档',
'caseManagement.caseReview.tableNoData': '暂无数据,请',
'caseManagement.caseReview.tableNoDataNoPermission': '暂无数据',

View File

@ -44,7 +44,7 @@ export default {
'project.commonScript.commonScriptList': '公共脚本列表',
'project.commonScript.folderSearchPlaceholder': '请输入模块名称',
'project.commonScript.allApis': '全部接口',
'project.commonScript.searchPlaceholder': '通过 ID名称搜索',
'project.commonScript.searchPlaceholder': '通过 ID/名称搜索',
'project.commonScript.noTreeData': '暂无接口数据',
'project.commonScript.apiName': '接口名称',
'project.commonScript.requestType': '请求类型',

View File

@ -67,7 +67,6 @@
</template>
<script lang="ts" setup>
import { defineModel, ref } from 'vue';
import { type FileItem, Message } from '@arco-design/web-vue';
import MsUpload from '@/components/pure/ms-upload/index.vue';

View File

@ -42,7 +42,6 @@
</template>
<script lang="ts" setup>
import { defineModel, onBeforeMount, ref } from 'vue';
import { VueDraggable } from 'vue-draggable-plus';
import MsButton from '@/components/pure/ms-button/index.vue';

View File

@ -74,7 +74,7 @@
type UserModalMode = 'create' | 'edit';
const batchFormModels: Ref<FormItemModel[]> = ref([
{
filed: 'ip',
field: 'ip',
type: 'input',
label: 'project.environmental.host.ip',
placeholder: 'project.environmental.host.ipPlaceholder',
@ -84,14 +84,14 @@
],
},
{
filed: 'domain',
field: 'domain',
type: 'input',
label: 'project.environmental.host.hostName',
placeholder: 'project.environmental.host.hostNamePlaceholder',
rules: [{ required: true, message: t('project.environmental.host.hostNameIsRequire') }],
},
{
filed: 'description',
field: 'description',
type: 'input',
label: 'common.desc',
placeholder: 'project.environmental.host.descPlaceholder',

View File

@ -224,12 +224,9 @@
</template>
<script lang="ts" setup>
import { defineModel } from 'vue';
import { Message, ValidatedError } from '@arco-design/web-vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
import { getEnvModules } from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';

View File

@ -15,7 +15,7 @@ export default {
'project.environmental.supportFormat': '仅支持MeterSphere导出的Json文件单个大小不超过 50M',
'project.environmental.importTile': '注意!导入后会覆盖原全局参数',
'project.environmental.mustContain': '必含',
'project.environmental.searchParamsHolder': '通过名称标签搜索',
'project.environmental.searchParamsHolder': '通过名称/标签搜索',
'project.environmental.paramName': '参数名称',
'project.environmental.paramNamePlaceholder': '请输入参数名称',
'project.environmental.paramType': '类型',

View File

@ -6,9 +6,9 @@
</div>
</div>
<div class="mb-4 flex items-center justify-between">
<a-button v-permission="['PROJECT_APPLICATION_API:UPDATE']" type="primary" @click="showAddRule(undefined)">{{
t('project.menu.addFalseAlertRules')
}}</a-button>
<a-button v-permission="['PROJECT_APPLICATION_API:UPDATE']" type="primary" @click="showAddRule(undefined)">
{{ t('project.menu.addFalseAlertRules') }}
</a-button>
<a-input-search
v-model="keyword"
:placeholder="t('project.menu.nameSearch')"
@ -29,9 +29,9 @@
<template v-if="!record.enable">
<div class="flex flex-row">
<span v-permission="['PROJECT_APPLICATION_API:UPDATE']" class="flex flex-row">
<MsButton class="!mr-0" @click="handleEnableOrDisableProject(record.id)">{{
t('common.enable')
}}</MsButton>
<MsButton class="!mr-0" @click="handleEnableOrDisableProject(record.id)">
{{ t('common.enable') }}
</MsButton>
<a-divider direction="vertical" />
</span>
<span>
@ -50,9 +50,9 @@
<a-divider class="h-[16px]" direction="vertical" />
</span>
<span v-permission="['PROJECT_APPLICATION_API:UPDATE']" class="flex flex-row items-center">
<MsButton class="!mr-0" @click="handleEnableOrDisableProject(record.id, false)">{{
t('common.disable')
}}</MsButton>
<MsButton class="!mr-0" @click="handleEnableOrDisableProject(record.id, false)">
{{ t('common.disable') }}
</MsButton>
<a-divider class="h-[16px]" direction="vertical" />
</span>
<MsTableMoreAction
@ -174,7 +174,7 @@
};
const initBatchFormModels: FormItemModel[] = [
{
filed: 'name',
field: 'name',
type: 'input',
label: 'project.menu.rule.ruleName',
rules: [
@ -183,34 +183,35 @@
],
},
{
filed: 'type',
field: 'type',
type: 'tagInput',
label: 'project.menu.rule.label',
},
{
filed: 'rule',
field: 'rule',
type: 'multiple',
label: 'project.menu.rule.rule',
hasRedStar: true,
children: [
{
filed: 'respType', // -/header/data/body
field: 'respType', // -/header/data/body
type: 'select',
options: headerOptions.value,
className: 'w-[205px]',
defaultValue: 'RESPONSE_HEADERS',
},
{
filed: 'relation', // -
field: 'relation', // -
type: 'select',
options: relationOptions.value,
defaultValue: 'CONTAINS',
className: 'w-[120px]',
},
{
filed: 'expression', // -
type: 'input',
field: 'expression', // -
type: 'textarea',
rules: [{ required: true, message: t('project.menu.rule.expressionNotNull') }],
title: t('project.menu.rule.ruleExpression'),
className: 'w-[301px]',
},
],
@ -410,7 +411,7 @@
addVisible.value = true;
};
const handleCancel = (shouldSearch: boolean, isClose = true) => {
const handleCancel = (shouldSearch: boolean) => {
if (shouldSearch) {
fetchData();
}
@ -441,7 +442,7 @@
Message.success(t('common.updateSuccess'));
}
handleCancel(true, false);
handleCancel(true);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -1,10 +1,10 @@
export default {
'project.menu.management': 'Application Setting',
'project.menu.management': 'Application Settings',
'project.menu.manageTip':
'You can configure the switch of each function according to the usage scenario. After closing, the function entry will be hidden, and members cannot access this function and data; the data already generated will not be affected by this rule; when it is turned on again, it will be restored to the state before closing',
'You can configure various feature switches based on usage scenarios. After closing them, the corresponding feature entry will be hidden and members will not be able to access the feature and its data. The rule does not affect existing data. When you reopen it, it will restore to the state before closing.',
'project.menu.name': 'Menu Name',
'project.menu.pleaseConfig': 'Please Configure',
'project.menu.count': 'Items',
'project.menu.pleaseConfig': 'Please configure',
'project.menu.count': 'Count',
'project.menu.WORKSTATION_SYNC_RULE': 'Interface Test Update Synchronization Rule',
'project.menu.TEST_PLAN_CLEAN_REPORT': 'Report Retention Time Range',
@ -12,69 +12,95 @@ export default {
'project.menu.UI_CLEAN_REPORT': 'Report Retention Time Range',
'project.menu.UI_SHARE_REPORT': 'Report Link Validity Period',
'project.menu.UI_RESOURCE_POOL': 'Execution Resource Pool',
'project.menu.UI_RESOURCE_POOL_TIP': 'The current resource pool is: {name}; you can switch resource pools',
'project.menu.UI_RESOURCE_POOL_TIP': 'The current resource pool in use is: {name}; you can switch resource pools',
'project.menu.PERFORMANCE_TEST_CLEAN_REPORT': 'Report Retention Time Range',
'project.menu.PERFORMANCE_TEST_SHARE_REPORT': 'Report Link Validity Period',
'project.menu.PERFORMANCE_TEST_SCRIPT_REVIEWER': 'Script Review',
'project.menu.PERFORMANCE_TEST_SCRIPT_REVIEWER': 'Script Reviewer',
'project.menu.PERFORMANCE_TEST_SCRIPT_REVIEWER_TIP':
'The interface case contains script steps that need to be reviewed by a designated user; the reviewer can be changed',
'project.menu.API_URL_REPEATABLE': 'Interface API URL Can Be Repeated',
'Specify a user to review script steps in interface test cases; you can change the reviewer',
'project.menu.API_URL_REPEATABLE': 'API Definition URL Repeatability',
'project.menu.API_CLEAN_REPORT': 'Report Retention Time Range',
'project.menu.API_SHARE_REPORT': 'Report Link Validity Period',
'project.menu.API_RESOURCE_POOL': 'Execution Resource Pool',
'project.menu.API_RESOURCE_POOL_TIP': 'Execution machine for interface testing',
'project.menu.API_SCRIPT_REVIEWER': 'Script Review',
'project.menu.API_SCRIPT_REVIEWER': 'Script Reviewer',
'project.menu.API_SCRIPT_REVIEWER_TIP':
'The interface case contains script steps that need to be reviewed by a designated user; the reviewer can be changed',
'project.menu.API_ERROR_REPORT_RULE': 'Fake Error Rule',
'Specify a user to review script steps in interface test cases; you can change the reviewer',
'project.menu.API_ERROR_REPORT_RULE': 'False Alert Rule',
'project.menu.API_ERROR_REPORT_RULE_TIP':
'When the interface return result matches the fake error rule, the interface result will be treated as a false report',
'project.menu.API_SYNC_CASE': 'Change Sync CASE',
'When the interface returns a result that matches the false alert rule, the interface result will be treated as a false alert',
'project.menu.API_SYNC_CASE': 'Change Synchronized CASE',
'project.menu.CASE_PUBLIC': 'Public Case Library',
'project.menu.CASE_RE_REVIEW': 'Resubmit for Review',
'project.menu.CASE_PUBLIC': 'Public Test Case Library',
'project.menu.CASE_RE_REVIEW': 'Re-review',
'project.menu.CASE_RELATED': 'Related Requirements',
'project.menu.BUG_SYNC': 'Sync Defects',
'project.menu.BUG_SYNC': 'Defect Synchronization',
'project.menu.SYNC_ENABLE': 'Status',
'project.menu.MECHANISM': 'Interface Test Update Synchronization Rule',
'project.menu.row1': 'The system displays the data that meets the set rules in my to-do list-to be updated',
'project.menu.row2': 'Sync the defects created by the platform to the third-party project management platform',
'project.menu.row3': 'You can add cases to the public case library for shared use',
'project.menu.row4': 'You can associate cases with third-party project management platforms',
'project.menu.row1': 'The system displays data that meets the rules in My To-Do List - To Be Updated',
'project.menu.row2': 'Synchronize defects created on the platform to third-party project management platforms',
'project.menu.row3': 'Add test cases to the public test case library for sharing',
'project.menu.row4': 'Associate test cases with third-party project management platforms',
'project.menu.row5':
'When the case changes during the review activity, the case status automatically switches to resubmit for review',
'project.menu.row6': 'After turning on, the interface definition module repetitiveness check will not check the URL',
'project.menu.row7': 'When the interface definition changes, the interface CASE is automatically synchronized',
'project.menu.notConfig': 'Third-party information is not configured, click',
'When changes occur in test cases during the review process, the test case status automatically switches to re-review',
'project.menu.row6': 'When enabled, the interface definition module will not validate URL duplication',
'project.menu.row7': 'Automatically synchronize interface CASE when the interface definition changes',
'project.menu.notConfig': 'Third-party information not configured, click',
'project.menu.configure': 'to configure',
'project.menu.status': 'Status',
'project.menu.incrementalSync': 'Incremental Sync',
'project.menu.incrementalSyncTip': 'Only the contents of the three-party bug existing in MS are changed',
'project.menu.fullSync': 'Full Sync',
'project.menu.fullSyncTip': 'Fully sync bug of third-party platforms to MS',
'project.menu.incrementalSync': 'Incremental Synchronization',
'project.menu.incrementalSyncTip': 'Only make content changes to existing third-party defects in MS',
'project.menu.fullSync': 'Full Synchronization',
'project.menu.fullSyncTip': 'Fully synchronize defects from third-party platforms to MS',
'project.menu.platformPlaceholder':
'No third-party platform integrated yet, please contact the organization administrator to integrate',
'Third-party platform not integrated yet, please contact the organization administrator for integration',
'project.menu.platformLabel': 'Third-party Project Management Platform',
'project.menu.syncMechanism': 'Sync Mechanism',
'project.menu.CRON_EXPRESSION': 'Sync Frequency',
'project.menu.syncMechanism': 'Synchronization Mechanism',
'project.menu.CRON_EXPRESSION': 'Synchronization Frequency',
'project.menu.projectKey': 'Project Key',
'project.menu.projectId': 'Project ID',
'project.menu.organizationId': 'Organization ID',
'project.menu.azureId': 'Azure Filter ID',
'project.menu.defectType': 'Defect Type',
'project.menu.demandType': 'Demand Type',
'project.menu.howGetJiraKey': 'How to get JIRA project key',
'project.menu.demandType': 'Requirement Type',
'project.menu.howGetJiraKey': 'How to Get JIRA Project Key',
'project.menu.preview': 'Preview',
'project.menu.pleaseInputJiraKey': 'Please enter JIRA project key',
'project.menu.addFalseAlertRules': 'Add False Alert Rules',
'project.menu.updateFalseAlertRules': 'update False Alert Rules',
'project.menu.addFalseAlertRules': 'Add False Alert Rule',
'project.menu.updateFalseAlertRules': 'Update False Alert Rule',
'project.menu.nameSearch': 'Search by Name',
// Sync defects
// Defect Synchronization
'project.menu.defect.enableTip':
'Turn on: The defects created by the platform are synced to the third-party project management platform',
'Enable: Synchronize defects created on the platform to third-party project management platforms',
'project.menu.defect.closeTip':
'Turn off: The defects created by the platform cannot be synced to the third-party project management platform',
'project.menu.demand.enableTip': 'On: functional cases can relate to third-party demands',
'project.menu.demand.closeTip': 'Off: functional cases cannot relate to third party demands',
'Disable: Defects created on the platform will not be synchronized to third-party project management platforms',
'project.menu.demand.enableTip':
'Enable: Test cases created on the platform can be associated with third-party requirements',
'project.menu.demand.closeTip':
'Disable: Test cases created on the platform cannot be associated with third-party requirements',
'project.menu.defect.customLabel': 'Custom Frequency',
'project.menu.defect.enableAfterConfig': 'Enable after configuring third-party information',
// False Alert Rule
'project.menu.rule.name': 'Name',
'project.menu.rule.enable': 'Status',
'project.menu.rule.label': 'Label',
'project.menu.rule.rule': 'Rule',
'project.menu.rule.creator': 'Creator',
'project.menu.rule.updateTime': 'Update Time',
'project.menu.rule.operation': 'Operation',
'project.menu.rule.ruleName': 'Rule Name',
'project.menu.rule.ruleExpression': 'Rule condition',
'project.menu.rule.ruleNameNotNull': 'Rule name cannot be empty',
'project.menu.rule.ruleNameRepeat': 'Please modify the duplicate name',
'project.menu.rule.expressionNotNull': 'Limiting condition cannot be empty',
'project.menu.rule.addRule': 'Add Rule',
'project.menu.rule.disableRule': 'Disable Rule',
'project.menu.rule.disableRuleTip':
'After disabling, the interface result will no longer match this false alert rule.',
'project.menu.rule.enableRule': 'Enable Rule',
'project.menu.rule.enableRuleTip':
'After enabling, the interface result will be matched against the false alert rule first.',
'project.menu.rule.deleteRule': 'Confirm to delete {size} false alert rules?',
'project.menu.rule.deleteRuleTip':
'After deletion, it only takes effect on newly executed test reports. Please be cautious!',
};

View File

@ -80,6 +80,7 @@ export default {
'project.menu.rule.updateTime': '更新时间',
'project.menu.rule.operation': '操作',
'project.menu.rule.ruleName': '规则名称',
'project.menu.rule.ruleExpression': '限制条件',
'project.menu.rule.ruleNameNotNull': '规则名称不能为空',
'project.menu.rule.ruleNameRepeat': '名称重复请修改',
'project.menu.rule.expressionNotNull': '限制条件不能为空',

View File

@ -212,7 +212,7 @@
// -1.
const onlyOptions: Ref<FormItemModel> = ref({
filed: 'text',
field: 'text',
type: 'input',
label: '',
rules: [
@ -227,7 +227,7 @@
// -2
const bugBatchFormRules = ref<FormItemModel[]>([
{
filed: 'text',
field: 'text',
type: 'input',
label: '',
rules: [
@ -239,7 +239,7 @@
hideLabel: true,
},
{
filed: 'value',
field: 'value',
type: 'input',
label: '',
rules: [

View File

@ -73,7 +73,7 @@ export default {
'system.organization.projectIsDisabled': '项目已结束,可在 项目列表 启用',
'system.project.deleteName': '确认删除 {name} 这个项目吗?',
'system.project.deleteTip': '删除后,系统会在 30天 后执行删除项目 (含项目下所有业务数据),请谨慎操作!',
'system.project.searchPlaceholder': '通过ID或项目名称搜索',
'system.project.searchPlaceholder': '通过 ID/项目名称搜索',
'system.project.afterModule': '取消模块后用户将无法进入指定模块,已存在的数据会继续保留',
'system.project.projectAdminIsNotNull': '项目管理员不能为空',
'system.project.pleaseSelectAdmin': '请选择项目管理员',

View File

@ -574,21 +574,21 @@
const batchFormRef = ref<InstanceType<typeof MsBatchForm>>();
const batchFormModels: Ref<FormItemModel[]> = ref([
{
filed: 'ip',
field: 'ip',
type: 'input',
label: 'system.resourcePool.ip',
rules: [{ required: true, message: t('system.resourcePool.ipRequired') }],
placeholder: 'system.resourcePool.ipPlaceholder',
},
{
filed: 'port',
field: 'port',
type: 'input',
label: 'system.resourcePool.port',
rules: [{ required: true, message: t('system.resourcePool.portRequired') }],
placeholder: 'system.resourcePool.portPlaceholder',
},
{
filed: 'concurrentNumber',
field: 'concurrentNumber',
type: 'inputNumber',
label: 'system.resourcePool.concurrentNumber',
rules: [

View File

@ -834,14 +834,14 @@
const batchFormRef = ref<InstanceType<typeof MsBatchForm>>();
const batchFormModels: Ref<FormItemModel[]> = ref([
{
filed: 'name',
field: 'name',
type: 'input',
label: 'system.user.createUserName',
rules: [{ required: true, message: t('system.user.createUserNameNotNull') }, { validator: checkUerName }],
placeholder: 'system.user.createUserNamePlaceholder',
},
{
filed: 'email',
field: 'email',
type: 'input',
label: 'system.user.createUserEmail',
rules: [
@ -852,7 +852,7 @@
placeholder: 'system.user.createUserEmailPlaceholder',
},
{
filed: 'phone',
field: 'phone',
type: 'input',
label: 'system.user.createUserPhone',
rules: [{ validator: checkUerPhone }],

View File

@ -111,7 +111,7 @@ export default {
'testPlan.featureCase.richTextDblclickPlaceholder': '双击可全屏输入',
'testPlan.featureCase.autoNextTip1': '开启:提交结果后,跳转至下一条用例',
'testPlan.featureCase.autoNextTip2': '关闭:提交结果后,还在当前',
'testPlan.api.testSetRequired': '测试不能为空',
'testPlan.api.testSetRequired': '测试不能为空',
'testPlan.executeHistory.executionStartAndEndTime': '执行起止时间',
'testPlan.executeHistory.testPlanHasTimedOut': '测试计划已超时',
'testPlan.testPlanGroup.seeArchived': '只看已归档',
@ -134,6 +134,6 @@ export default {
'testPlan.testPlanGroup.enableScheduleTaskSuccess': '开启定时任务成功',
'testPlan.testPlanGroup.closeScheduleTaskSuccess': '关闭定时任务成功',
'testPlan.plan': '测试规划',
'testPlan.planTip': '1.创建测试集进行业务分类测试2.选择测试集关联用例',
'testPlan.planTip': '1.创建测试点进行业务分类测试2.选择测试点关联用例',
'testPlan.planStartToEndTimeTip': '测试计划已超时',
};