refactor(缺陷管理): 详情增加缺陷历史展示
This commit is contained in:
parent
4554dcec42
commit
52448dcb32
|
@ -211,3 +211,8 @@ export function batchAssociation(data: TableQueryParams) {
|
||||||
export function cancelAssociation(id: string) {
|
export function cancelAssociation(id: string) {
|
||||||
return MSR.get({ url: `${bugURL.getCancelDemandUrl}/${id}` });
|
return MSR.get({ url: `${bugURL.getCancelDemandUrl}/${id}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 缺陷管理-变更历史-列表
|
||||||
|
export function getChangeHistoryList(data: TableQueryParams) {
|
||||||
|
return MSR.post({ url: bugURL.getChangeHistoryListUrl, data });
|
||||||
|
}
|
||||||
|
|
|
@ -64,3 +64,6 @@ export const getUnrelatedDemandListUrl = '/bug/case/un-relate/page';
|
||||||
export const getUnrelatedModuleTreeUrl = '/bug/case/un-relate/module/tree';
|
export const getUnrelatedModuleTreeUrl = '/bug/case/un-relate/module/tree';
|
||||||
// 未关联的模块树 数量
|
// 未关联的模块树 数量
|
||||||
export const getUnrelatedModuleTreeCountUrl = '/bug/case/un-relate/module/count';
|
export const getUnrelatedModuleTreeCountUrl = '/bug/case/un-relate/module/count';
|
||||||
|
|
||||||
|
// 缺陷管理-变更历史-列表
|
||||||
|
export const getChangeHistoryListUrl = '/bug/history/page';
|
||||||
|
|
|
@ -0,0 +1,385 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div>
|
||||||
|
<a-radio-group v-model:model-value="activeTab" type="button" size="small">
|
||||||
|
<a-radio v-for="item of responseRadios" :key="item.value" :value="item.value">
|
||||||
|
{{ t(item.label) }}
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
<div v-if="activeTab === 'jsonPath'" class="mt-[16px]">
|
||||||
|
<paramsTable
|
||||||
|
v-model:params="innerParams.jsonPath"
|
||||||
|
:selectable="false"
|
||||||
|
:columns="jsonPathColumns"
|
||||||
|
:scroll="{ minWidth: '700px' }"
|
||||||
|
:default-param-item="jsonPathDefaultParamItem"
|
||||||
|
@change="handleJsonPathChange"
|
||||||
|
@more-action-select="(e,r)=> handleExtractParamMoreActionSelect(e,r as ExpressionConfig)"
|
||||||
|
>
|
||||||
|
<template #expression="{ record }">
|
||||||
|
<a-popover
|
||||||
|
position="tl"
|
||||||
|
:disabled="!record.expression || record.expression.trim() === ''"
|
||||||
|
class="ms-params-input-popover"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="param-popover-title">
|
||||||
|
{{ t('apiTestDebug.expression') }}
|
||||||
|
</div>
|
||||||
|
<div class="param-popover-value">
|
||||||
|
{{ record.expression }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="record.expression"
|
||||||
|
class="ms-params-input"
|
||||||
|
:max-length="255"
|
||||||
|
@input="handleExpressionChange"
|
||||||
|
@change="handleExpressionChange"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-tooltip :disabled="!disabledExpressionSuffix">
|
||||||
|
<template #content>
|
||||||
|
<div>{{ t('apiTestDebug.expressionTip1') }}</div>
|
||||||
|
<div>{{ t('apiTestDebug.expressionTip2') }}</div>
|
||||||
|
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
||||||
|
</template>
|
||||||
|
<MsIcon
|
||||||
|
type="icon-icon_flashlamp"
|
||||||
|
:size="15"
|
||||||
|
:class="
|
||||||
|
disabledExpressionSuffix ? 'ms-params-input-suffix-icon--disabled' : 'ms-params-input-suffix-icon'
|
||||||
|
"
|
||||||
|
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.JSON_PATH)"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template #operationPre="{ record }">
|
||||||
|
<a-popover
|
||||||
|
v-model:popupVisible="record.moreSettingPopoverVisible"
|
||||||
|
position="tl"
|
||||||
|
trigger="click"
|
||||||
|
:title="t('common.setting')"
|
||||||
|
:content-style="{ width: '480px' }"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<moreSetting v-model:config="activeRecord" is-popover class="mt-[12px]" />
|
||||||
|
<div class="flex items-center justify-end gap-[8px]">
|
||||||
|
<a-button type="secondary" size="mini" @click="record.moreSettingPopoverVisible = false">
|
||||||
|
{{ t('common.cancel') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" size="mini" @click="() => applyMoreSetting(record)">
|
||||||
|
{{ t('common.confirm') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span class="invisible relative"></span>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
</paramsTable>
|
||||||
|
</div>
|
||||||
|
<div v-if="activeTab === 'xPath'" class="mt-[16px]">
|
||||||
|
<div class="text-[var(--color-text-1)]">{{ t('ms.assertion.responseContentType') }}</div>
|
||||||
|
<a-radio-group
|
||||||
|
v-model:model-value="innerParams.xPath.responseFormat"
|
||||||
|
class="mt-[16px]"
|
||||||
|
type="button"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<a-radio value="XML">XML</a-radio>
|
||||||
|
<a-radio value="HTML">HTML</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
<paramsTable
|
||||||
|
v-model:params="innerParams.xPath.data"
|
||||||
|
class="mt-[16px]"
|
||||||
|
:selectable="false"
|
||||||
|
:columns="xPathColumns"
|
||||||
|
:scroll="{ minWidth: '700px' }"
|
||||||
|
:default-param-item="xPathDefaultParamItem"
|
||||||
|
@change="handleXPathChange"
|
||||||
|
@more-action-select="(e,r)=> handleExtractParamMoreActionSelect(e,r as ExpressionConfig)"
|
||||||
|
>
|
||||||
|
<template #expression="{ record }">
|
||||||
|
<a-popover
|
||||||
|
position="tl"
|
||||||
|
:disabled="!record.expression || record.expression.trim() === ''"
|
||||||
|
class="ms-params-input-popover"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="param-popover-title">
|
||||||
|
{{ t('apiTestDebug.expression') }}
|
||||||
|
</div>
|
||||||
|
<div class="param-popover-value">
|
||||||
|
{{ record.expression }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="record.expression"
|
||||||
|
class="ms-params-input"
|
||||||
|
:max-length="255"
|
||||||
|
@input="handleExpressionChange"
|
||||||
|
@change="handleExpressionChange"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-tooltip :disabled="!disabledExpressionSuffix">
|
||||||
|
<template #content>
|
||||||
|
<div>{{ t('apiTestDebug.expressionTip1') }}</div>
|
||||||
|
<div>{{ t('apiTestDebug.expressionTip2') }}</div>
|
||||||
|
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
||||||
|
</template>
|
||||||
|
<MsIcon
|
||||||
|
type="icon-icon_flashlamp"
|
||||||
|
:size="15"
|
||||||
|
:class="
|
||||||
|
disabledExpressionSuffix ? 'ms-params-input-suffix-icon--disabled' : 'ms-params-input-suffix-icon'
|
||||||
|
"
|
||||||
|
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.JSON_PATH)"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template #operationPre="{ record }">
|
||||||
|
<a-popover
|
||||||
|
v-model:popupVisible="record.moreSettingPopoverVisible"
|
||||||
|
position="tl"
|
||||||
|
trigger="click"
|
||||||
|
:title="t('common.setting')"
|
||||||
|
:content-style="{ width: '480px' }"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<moreSetting v-model:config="activeRecord" is-popover class="mt-[12px]" />
|
||||||
|
<div class="flex items-center justify-end gap-[8px]">
|
||||||
|
<a-button type="secondary" size="mini" @click="record.moreSettingPopoverVisible = false">
|
||||||
|
{{ t('common.cancel') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" size="mini" @click="() => applyMoreSetting(record)">
|
||||||
|
{{ t('common.confirm') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span class="invisible relative"></span>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
</paramsTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fastExtraction v-model:visible="fastExtractionVisible" :config="activeRecord" @apply="handleFastExtractionApply" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineModel } from 'vue';
|
||||||
|
|
||||||
|
import { statusCodeOptions } from '@/components/pure/ms-advance-filter';
|
||||||
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
import fastExtraction from '@/views/api-test/components/fastExtraction/index.vue';
|
||||||
|
import moreSetting from '@/views/api-test/components/fastExtraction/moreSetting.vue';
|
||||||
|
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ExecuteConditionProcessor,
|
||||||
|
ExpressionType,
|
||||||
|
JSONPathExtract,
|
||||||
|
RegexExtract,
|
||||||
|
XPathExtract,
|
||||||
|
} from '@/models/apiTest/debug';
|
||||||
|
import {
|
||||||
|
RequestExtractEnvType,
|
||||||
|
RequestExtractExpressionEnum,
|
||||||
|
RequestExtractExpressionRuleType,
|
||||||
|
RequestExtractResultMatchingRule,
|
||||||
|
RequestExtractScope,
|
||||||
|
ResponseBodyXPathAssertionFormat,
|
||||||
|
} from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
interface Param {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:data', data: ExecuteConditionProcessor): void;
|
||||||
|
(e: 'copy'): void;
|
||||||
|
(e: 'delete', id: number): void;
|
||||||
|
(e: 'change'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const innerParams = defineModel<Param>('modelValue', {
|
||||||
|
default: { jsonPath: [], xPath: { responseFormat: 'XML', data: [] } },
|
||||||
|
});
|
||||||
|
const activeTab = ref('jsonPath');
|
||||||
|
const extractParamsTableRef = ref<InstanceType<typeof paramsTable>>();
|
||||||
|
const fastExtractionVisible = ref(false);
|
||||||
|
const disabledExpressionSuffix = ref(false);
|
||||||
|
export type ExpressionConfig = (RegexExtract | JSONPathExtract | XPathExtract) & Record<string, any>;
|
||||||
|
|
||||||
|
const defaultExtractParamItem: ExpressionConfig = {
|
||||||
|
enable: true,
|
||||||
|
variableName: '',
|
||||||
|
variableType: RequestExtractEnvType.TEMPORARY,
|
||||||
|
extractScope: RequestExtractScope.BODY,
|
||||||
|
expression: '',
|
||||||
|
extractType: RequestExtractExpressionEnum.REGEX,
|
||||||
|
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
|
||||||
|
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
|
||||||
|
resultMatchingRuleNum: 1,
|
||||||
|
responseFormat: ResponseBodyXPathAssertionFormat.XML,
|
||||||
|
moreSettingPopoverVisible: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeRecord = ref({ ...defaultExtractParamItem }); // 用于暂存当前操作的提取参数表格项
|
||||||
|
|
||||||
|
const responseRadios = [
|
||||||
|
{ label: 'ms.assertion.jsonPath', value: 'jsonPath' },
|
||||||
|
{ label: 'ms.assertion.xpath', value: 'xPath' },
|
||||||
|
{ label: 'ms.assertion.document', value: 'document' },
|
||||||
|
{ label: 'ms.assertion.regular', value: 'regular' },
|
||||||
|
{ label: 'ms.assertion.script', value: 'script' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const jsonPathColumns: ParamTableColumn[] = [
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.expression',
|
||||||
|
dataIndex: 'expression',
|
||||||
|
slotName: 'expression',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.matchCondition',
|
||||||
|
dataIndex: 'matchCondition',
|
||||||
|
slotName: 'matchCondition',
|
||||||
|
options: statusCodeOptions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.matchValue',
|
||||||
|
dataIndex: 'matchValue',
|
||||||
|
slotName: 'matchValue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
slotName: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 130,
|
||||||
|
hasDisable: true,
|
||||||
|
moreAction: [
|
||||||
|
{
|
||||||
|
eventTag: 'copy',
|
||||||
|
label: 'common.copy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
eventTag: 'setting',
|
||||||
|
label: 'common.setting',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const jsonPathDefaultParamItem = {
|
||||||
|
expression: '',
|
||||||
|
matchCondition: '',
|
||||||
|
matchValue: '',
|
||||||
|
enable: true,
|
||||||
|
};
|
||||||
|
const handleJsonPathChange = () => {
|
||||||
|
console.log('jsonPath change');
|
||||||
|
};
|
||||||
|
function handleExpressionChange(val: string) {
|
||||||
|
extractParamsTableRef.value?.addTableLine(val, 'expression');
|
||||||
|
}
|
||||||
|
|
||||||
|
const xPathColumns: ParamTableColumn[] = [
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.expression',
|
||||||
|
dataIndex: 'expression',
|
||||||
|
slotName: 'expression',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
slotName: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 130,
|
||||||
|
hasDisable: true,
|
||||||
|
moreAction: [
|
||||||
|
{
|
||||||
|
eventTag: 'copy',
|
||||||
|
label: 'common.copy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
eventTag: 'setting',
|
||||||
|
label: 'common.setting',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const xPathDefaultParamItem = {
|
||||||
|
expression: '',
|
||||||
|
matchCondition: '',
|
||||||
|
matchValue: '',
|
||||||
|
enable: true,
|
||||||
|
};
|
||||||
|
const handleXPathChange = () => {
|
||||||
|
console.log('jsonPath change');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取参数表格-应用更多设置
|
||||||
|
*/
|
||||||
|
function applyMoreSetting(record: ExpressionConfig) {
|
||||||
|
// condition.value.extractParams = condition.value.extractParams?.map((e) => {
|
||||||
|
// if (e.id === activeRecord.value.id) {
|
||||||
|
// record.moreSettingPopoverVisible = false;
|
||||||
|
// return {
|
||||||
|
// ...activeRecord.value,
|
||||||
|
// moreSettingPopoverVisible: false,
|
||||||
|
// } as any; // TOOD: 这里的后台类型应该是不对的,需要修改
|
||||||
|
// }
|
||||||
|
// return e;
|
||||||
|
// });
|
||||||
|
// emit('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取参数表格-保存快速提取的配置
|
||||||
|
*/
|
||||||
|
function handleFastExtractionApply(config: RegexExtract | JSONPathExtract | XPathExtract) {
|
||||||
|
// condition.value.extractParams = condition.value.extractParams?.map((e) => {
|
||||||
|
// if (e.id === activeRecord.value.id) {
|
||||||
|
// return {
|
||||||
|
// ...e,
|
||||||
|
// ...config,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// return e;
|
||||||
|
// });
|
||||||
|
// fastExtractionVisible.value = false;
|
||||||
|
// nextTick(() => {
|
||||||
|
// extractParamsTableRef.value?.addTableLine();
|
||||||
|
// });
|
||||||
|
// emit('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理提取参数表格更多操作
|
||||||
|
*/
|
||||||
|
function handleExtractParamMoreActionSelect(event: ActionsItem, record: ExpressionConfig) {
|
||||||
|
activeRecord.value = { ...record };
|
||||||
|
if (event.eventTag === 'copy') {
|
||||||
|
emit('copy');
|
||||||
|
} else if (event.eventTag === 'setting') {
|
||||||
|
record.moreSettingPopoverVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showFastExtraction(record: ExpressionConfig, type: ExpressionType) {
|
||||||
|
activeRecord.value = { ...record, extractType: type };
|
||||||
|
fastExtractionVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
</script>
|
|
@ -1,18 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<paramsTable
|
||||||
<a-radio-group v-model:model-value="innerParams.type" type="button" size="small">
|
v-model:params="innerParams"
|
||||||
<a-radio v-for="item of responseRadios" :key="item.value" :value="item.value">
|
:selectable="false"
|
||||||
{{ t(item.label) }}
|
:columns="columns"
|
||||||
</a-radio>
|
:scroll="{ minWidth: '700px' }"
|
||||||
</a-radio-group>
|
:default-param-item="defaultParamItem"
|
||||||
</div>
|
@change="handleParamTableChange"
|
||||||
<div v-if="innerParams.type === 'jsonPath'">
|
/>
|
||||||
<MsJsonPathPicker data="" />
|
|
||||||
</div>
|
|
||||||
<div v-else-if="innerParams.type === 'xPath'">
|
|
||||||
<MsXPathPicker :xml-string="innerParams.response" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -20,28 +15,77 @@
|
||||||
import { defineModel } from 'vue';
|
import { defineModel } from 'vue';
|
||||||
|
|
||||||
import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index';
|
import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index';
|
||||||
import MsJsonPathPicker from '@/components/pure/ms-jsonpath-picker/index.vue';
|
|
||||||
import MsXPathPicker from '@/components/pure/ms-jsonpath-picker/xpath.vue';
|
|
||||||
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
|
||||||
|
|
||||||
interface Param {
|
interface Param {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const innerParams = defineModel<Param>('modelValue', { default: { type: 'jsonPath' } });
|
const innerParams = defineModel<Param[]>('modelValue', { default: [] });
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'change'): void; // 数据发生变化
|
(e: 'change'): void; // 数据发生变化
|
||||||
}>();
|
}>();
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const responseRadios = [
|
const defaultParamItem = {
|
||||||
{ label: 'ms.assertion.jsonPath', value: 'jsonPath' },
|
responseHeader: '',
|
||||||
{ label: 'ms.assertion.xpath', value: 'xPath' },
|
matchCondition: '',
|
||||||
{ label: 'ms.assertion.document', value: 'document' },
|
matchValue: '',
|
||||||
{ label: 'ms.assertion.regular', value: 'regular' },
|
enable: true,
|
||||||
{ label: 'ms.assertion.script', value: 'script' },
|
};
|
||||||
|
|
||||||
|
const responseHeaderOption = [
|
||||||
|
{ label: 'Accept', value: 'accept' },
|
||||||
|
{ label: 'Accept-Encoding', value: 'acceptEncoding' },
|
||||||
|
{ label: 'Accept-Language', value: 'acceptLanguage' },
|
||||||
|
{ label: 'Cache-Control', value: 'cacheControl' },
|
||||||
|
{ label: 'Content-Type', value: 'contentType' },
|
||||||
|
{ label: 'Content-Length', value: 'contentLength' },
|
||||||
|
{ label: 'User-Agent', value: 'userAgent' },
|
||||||
|
{ label: 'Referer', value: 'referer' },
|
||||||
|
{ label: 'Cookie', value: 'cookie' },
|
||||||
|
{ label: 'Authorization', value: 'authorization' },
|
||||||
|
{ label: 'If-None-Match', value: 'ifNoneMatch' },
|
||||||
|
{ label: 'If-Modified-Since', value: 'ifModifiedSince' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const columns: ParamTableColumn[] = [
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.responseHeader', // 响应头
|
||||||
|
dataIndex: 'responseHeader',
|
||||||
|
slotName: 'responseHeader',
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: true,
|
||||||
|
options: responseHeaderOption,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.matchCondition', // 匹配条件
|
||||||
|
dataIndex: 'matchCondition',
|
||||||
|
slotName: 'matchCondition',
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: true,
|
||||||
|
options: statusCodeOptions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ms.assertion.matchValue', // 匹配值
|
||||||
|
dataIndex: 'matchValue',
|
||||||
|
slotName: 'matchValue',
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
columnTitle: 'common.operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
width: 50,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
function handleParamTableChange(resultArr: any[], isInit?: boolean) {
|
||||||
|
innerParams.value = [...resultArr];
|
||||||
|
if (!isInit) {
|
||||||
|
emit('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
v-model:statusCode="codeTabState.statusCode"
|
v-model:statusCode="codeTabState.statusCode"
|
||||||
/>
|
/>
|
||||||
<ResponseHeaderTab v-if="valueKey === 'responseHeader'" />
|
<ResponseHeaderTab v-if="valueKey === 'responseHeader'" />
|
||||||
|
<ResponseBodyTab v-if="valueKey === 'responseBody'" />
|
||||||
<ResponseTimeTab v-if="valueKey === 'responseTime'" />
|
<ResponseTimeTab v-if="valueKey === 'responseTime'" />
|
||||||
<VariableTab v-if="valueKey === 'variable'" />
|
<VariableTab v-if="valueKey === 'variable'" />
|
||||||
<ScriptTab v-if="valueKey === 'script'" />
|
<ScriptTab v-if="valueKey === 'script'" />
|
||||||
|
@ -76,6 +77,7 @@
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
import ResponseBodyTab from './comp/ResponseBodyTab.vue';
|
||||||
import ResponseHeaderTab from './comp/ResponseHeaderTab.vue';
|
import ResponseHeaderTab from './comp/ResponseHeaderTab.vue';
|
||||||
import ResponseTimeTab from './comp/ResponseTimeTab.vue';
|
import ResponseTimeTab from './comp/ResponseTimeTab.vue';
|
||||||
import ScriptTab from './comp/ScriptTab.vue';
|
import ScriptTab from './comp/ScriptTab.vue';
|
||||||
|
|
|
@ -14,4 +14,6 @@ export default {
|
||||||
'ms.assertion.document': '文档',
|
'ms.assertion.document': '文档',
|
||||||
'ms.assertion.regular': '正则',
|
'ms.assertion.regular': '正则',
|
||||||
'ms.assertion.script': '脚本',
|
'ms.assertion.script': '脚本',
|
||||||
|
'ms.assertion.expression': '表达式',
|
||||||
|
'ms.assertion.responseContentType': '响应内容类型',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
export { default as FilterForm } from './FilterForm.vue';
|
export { default as FilterForm } from './FilterForm.vue';
|
||||||
export { default as MsAdvanceFilter } from './index.vue';
|
export { default as MsAdvanceFilter } from './index.vue';
|
||||||
|
|
||||||
const IN = { label: 'advanceFilter.operator.in', value: 'in' };
|
// const IN = { label: 'advanceFilter.operator.in', value: 'in' };
|
||||||
const NOT_IN = { label: 'advanceFilter.operator.not_in', value: 'not_in' };
|
// const NOT_IN = { label: 'advanceFilter.operator.not_in', value: 'not_in' };
|
||||||
const LIKE = { label: 'advanceFilter.operator.like', value: 'like' };
|
const LIKE = { label: 'advanceFilter.operator.like', value: 'like' };
|
||||||
const NOT_LIKE = { label: 'advanceFilter.operator.not_like', value: 'not_like' };
|
const NOT_LIKE = { label: 'advanceFilter.operator.not_like', value: 'not_like' };
|
||||||
const GT = { label: 'advanceFilter.operator.gt', value: 'gt' };
|
const GT = { label: 'advanceFilter.operator.gt', value: 'gt' };
|
||||||
|
@ -14,18 +14,16 @@ const NOT_EQUAL = { label: 'advanceFilter.operator.notEqual', value: 'not_equal'
|
||||||
const BETWEEN = { label: 'advanceFilter.operator.between', value: 'between' };
|
const BETWEEN = { label: 'advanceFilter.operator.between', value: 'between' };
|
||||||
|
|
||||||
export const OPERATOR_MAP = {
|
export const OPERATOR_MAP = {
|
||||||
string: [LIKE, NOT_LIKE, IN, NOT_IN, EQUAL, NOT_EQUAL],
|
string: [LIKE, NOT_LIKE, EQUAL, NOT_EQUAL],
|
||||||
number: [GT, GE, LT, LE, EQUAL, NOT_EQUAL, BETWEEN],
|
number: [GT, GE, LT, LE, EQUAL, NOT_EQUAL, BETWEEN],
|
||||||
date: [GT, GE, LT, LE, EQUAL, NOT_EQUAL, BETWEEN],
|
date: [GT, GE, LT, LE, EQUAL, NOT_EQUAL, BETWEEN],
|
||||||
array: [IN, NOT_IN],
|
array: [BETWEEN],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const timeSelectOptions = [GE, LE];
|
export const timeSelectOptions = [GE, LE];
|
||||||
|
|
||||||
export const statusCodeOptions = [
|
export const statusCodeOptions = [
|
||||||
{ label: 'ms.assertion.noValidation', value: 'none' },
|
{ label: 'ms.assertion.noValidation', value: 'none' },
|
||||||
IN,
|
|
||||||
NOT_IN,
|
|
||||||
EQUAL,
|
EQUAL,
|
||||||
NOT_EQUAL,
|
NOT_EQUAL,
|
||||||
GT,
|
GT,
|
||||||
|
@ -133,7 +131,7 @@ export const CustomTypeMaps: Record<string, any> = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MULTIPLE_OPERATOR_LIST = ['in', 'not_in', 'between'];
|
export const MULTIPLE_OPERATOR_LIST = ['between'];
|
||||||
|
|
||||||
export function isMutipleOperator(operator: string) {
|
export function isMutipleOperator(operator: string) {
|
||||||
return MULTIPLE_OPERATOR_LIST.includes(operator);
|
return MULTIPLE_OPERATOR_LIST.includes(operator);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
@search="emit('keywordSearch', innerKeyword, filterResult)"
|
@search="emit('keywordSearch', innerKeyword, filterResult)"
|
||||||
@clear="handleClear"
|
@clear="handleClear"
|
||||||
></a-input-search>
|
></a-input-search>
|
||||||
<MsTag
|
<!-- <MsTag
|
||||||
:type="visible ? 'primary' : 'default'"
|
:type="visible ? 'primary' : 'default'"
|
||||||
:theme="visible ? 'lightOutLine' : 'outline'"
|
:theme="visible ? 'lightOutLine' : 'outline'"
|
||||||
size="large"
|
size="large"
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
{{ t('common.filter') }}
|
{{ t('common.filter') }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</MsTag>
|
</MsTag> -->
|
||||||
<MsTag no-margin size="large" class="cursor-pointer" theme="outline" @click="handleRefresh">
|
<MsTag no-margin size="large" class="cursor-pointer" theme="outline" @click="handleRefresh">
|
||||||
<MsIcon class="text-[var(color-text-4)]" :size="16" type="icon-icon_reset_outlined" />
|
<MsIcon class="text-[var(color-text-4)]" :size="16" type="icon-icon_reset_outlined" />
|
||||||
</MsTag>
|
</MsTag>
|
||||||
|
|
|
@ -206,6 +206,14 @@
|
||||||
<a-checkbox v-model:model-value="record[columnConfig.dataIndex as string]" @change="(val) => addTableLine(val)" />
|
<a-checkbox v-model:model-value="record[columnConfig.dataIndex as string]" @change="(val) => addTableLine(val)" />
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record, rowIndex, columnConfig }">
|
<template #operation="{ record, rowIndex, columnConfig }">
|
||||||
|
<a-switch
|
||||||
|
v-if="columnConfig.hasDisable"
|
||||||
|
v-model:model-value="record.disable"
|
||||||
|
size="small"
|
||||||
|
type="line"
|
||||||
|
class="mr-[8px]"
|
||||||
|
@change="(val) => addTableLine(val, 'disable')"
|
||||||
|
/>
|
||||||
<slot name="operationPre" :record="record" :row-index="rowIndex" :column-config="columnConfig"></slot>
|
<slot name="operationPre" :record="record" :row-index="rowIndex" :column-config="columnConfig"></slot>
|
||||||
<MsTableMoreAction
|
<MsTableMoreAction
|
||||||
v-if="columnConfig.moreAction"
|
v-if="columnConfig.moreAction"
|
||||||
|
@ -226,19 +234,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-trigger>
|
</a-trigger>
|
||||||
<a-switch
|
<div>
|
||||||
v-if="columnConfig.hasDisable"
|
|
||||||
v-model:model-value="record.disable"
|
|
||||||
size="small"
|
|
||||||
type="line"
|
|
||||||
@change="(val) => addTableLine(val, 'disable')"
|
|
||||||
/>
|
|
||||||
<icon-minus-circle
|
<icon-minus-circle
|
||||||
v-if="paramsLength > 1 && rowIndex !== paramsLength - 1"
|
v-if="paramsLength > 1 && rowIndex !== paramsLength - 1"
|
||||||
class="cursor-pointer text-[var(--color-text-4)]"
|
class="ml-[8px] cursor-pointer text-[var(--color-text-4)]"
|
||||||
size="20"
|
size="20"
|
||||||
@click="deleteParam(rowIndex)"
|
@click="deleteParam(rowIndex)"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #responseHeader="{ record, columnConfig }">
|
<template #responseHeader="{ record, columnConfig }">
|
||||||
<a-select v-model="record.responseHeader" class="param-input" @change="(val) => addTableLine(val as string)">
|
<a-select v-model="record.responseHeader" class="param-input" @change="(val) => addTableLine(val as string)">
|
||||||
|
|
|
@ -100,6 +100,12 @@
|
||||||
</template>
|
</template>
|
||||||
<CommentTab ref="commentRef" :bug-id="detailInfo.id" />
|
<CommentTab ref="commentRef" :bug-id="detailInfo.id" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="history">
|
||||||
|
<template #title>
|
||||||
|
{{ t('bugManagement.detail.changeHistory') }}
|
||||||
|
</template>
|
||||||
|
<BugHistoryTab :bug-id="detailInfo.id" />
|
||||||
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -166,6 +172,7 @@
|
||||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||||
import BugCaseTab from './bugCaseTab.vue';
|
import BugCaseTab from './bugCaseTab.vue';
|
||||||
import BugDetailTab from './bugDetailTab.vue';
|
import BugDetailTab from './bugDetailTab.vue';
|
||||||
|
import BugHistoryTab from './bugHistoryTab.vue';
|
||||||
import CommentTab from './commentTab.vue';
|
import CommentTab from './commentTab.vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<ms-base-table class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
|
||||||
|
<template #changeNumber="{ record }">
|
||||||
|
<span>{{ record.id }}</span>
|
||||||
|
<!-- TODO: 先不上 -->
|
||||||
|
<!-- <a-tag size="small" class="ml-[4px]">{{ t('bugManagement.history.current') }}</a-tag> -->
|
||||||
|
</template>
|
||||||
|
</ms-base-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
|
||||||
|
import { getChangeHistoryList } from '@/api/modules/bug-management';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const props = defineProps<{
|
||||||
|
bugId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const columns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'bugManagement.history.changeNumber',
|
||||||
|
slotName: 'changeNumber',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'bugManagement.history.operationMan',
|
||||||
|
dataIndex: 'createUserName',
|
||||||
|
showTooltip: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'bugManagement.history.updateTime',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getChangeHistoryList, {
|
||||||
|
heightUsed: 240,
|
||||||
|
columns,
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
selectable: false,
|
||||||
|
noDisable: false,
|
||||||
|
pageSimple: true,
|
||||||
|
debug: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchData = async (id: string) => {
|
||||||
|
setLoadListParams({ sourceId: id, projectId: appStore.currentProjectId });
|
||||||
|
await loadList();
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
fetchData(props.bugId);
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -67,6 +67,7 @@ export default {
|
||||||
tag: '标签',
|
tag: '标签',
|
||||||
detail: '详情',
|
detail: '详情',
|
||||||
case: '用例',
|
case: '用例',
|
||||||
|
changeHistory: '变更历史',
|
||||||
comment: '评论',
|
comment: '评论',
|
||||||
shareTip: '分享链接已复制到剪贴板',
|
shareTip: '分享链接已复制到剪贴板',
|
||||||
deleteTitle: '确认删除 {name} 吗?',
|
deleteTitle: '确认删除 {name} 吗?',
|
||||||
|
@ -106,18 +107,12 @@ export default {
|
||||||
deleteTime: '删除时间',
|
deleteTime: '删除时间',
|
||||||
deleteMan: '删除人',
|
deleteMan: '删除人',
|
||||||
},
|
},
|
||||||
severityO: {
|
history: {
|
||||||
fatal: '致命',
|
changeNumber: '变更序号',
|
||||||
serious: '严重',
|
operationMan: '操作人',
|
||||||
general: '一般',
|
updateTime: '更新时间',
|
||||||
reminder: '提醒',
|
restore: '恢复',
|
||||||
},
|
current: '当前',
|
||||||
statusO: {
|
|
||||||
create: '新建',
|
|
||||||
processing: '处理中',
|
|
||||||
resolved: '已解决',
|
|
||||||
closed: '已关闭',
|
|
||||||
refused: '已拒绝',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue