refactor(接口场景): 场景执行状态优化&部分表格筛选状态初始化为空
This commit is contained in:
parent
7fe24a0d27
commit
9f67d9fb6b
|
@ -185,8 +185,8 @@ export function calculateMaxDepth(arr?: Node[], depth = 0) {
|
|||
}
|
||||
|
||||
export interface TreeNode<T> {
|
||||
[key: string]: any;
|
||||
children?: TreeNode<T>[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -504,9 +504,9 @@
|
|||
];
|
||||
|
||||
const methodFilterVisible = ref(false);
|
||||
const methodFilters = ref(Object.keys(RequestMethods));
|
||||
const methodFilters = ref<string[]>([]);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
|
||||
const tableStore = useTableStore();
|
||||
async function getModuleIds() {
|
||||
|
@ -529,9 +529,8 @@
|
|||
moduleIds,
|
||||
protocol: props.protocol,
|
||||
filter: {
|
||||
status:
|
||||
statusFilters.value.length === Object.keys(RequestDefinitionStatus).length ? undefined : statusFilters.value,
|
||||
method: methodFilters.value.length === Object.keys(RequestMethods).length ? undefined : methodFilters.value,
|
||||
status: statusFilters.value,
|
||||
method: methodFilters.value,
|
||||
},
|
||||
};
|
||||
setLoadListParams(params);
|
||||
|
|
|
@ -560,7 +560,7 @@
|
|||
];
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const caseLevelFields = ref<Record<string, any>>({});
|
||||
const caseFilterVisible = ref(false);
|
||||
const caseFilters = ref<string[]>([]);
|
||||
|
@ -571,7 +571,7 @@
|
|||
const lastReportStatusList = computed(() => {
|
||||
return Object.keys(ReportStatus[ReportEnum.API_REPORT]);
|
||||
});
|
||||
const lastReportStatusFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_REPORT]));
|
||||
const lastReportStatusFilters = ref<string[]>([]);
|
||||
|
||||
async function getModuleIds() {
|
||||
let moduleIds: string[] = [];
|
||||
|
|
|
@ -118,10 +118,10 @@
|
|||
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management';
|
||||
import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
|
||||
|
||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
||||
const triggerModeListFilters = ref<string[]>();
|
||||
const triggerModeFilterVisible = ref(false);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_REPORT]));
|
||||
const statusFilters = ref<string[]>();
|
||||
const statusList = computed(() => {
|
||||
return Object.keys(ReportStatus[ReportEnum.API_REPORT]);
|
||||
});
|
||||
|
|
|
@ -256,9 +256,9 @@
|
|||
};
|
||||
|
||||
const methodFilterVisible = ref(false);
|
||||
const methodFilters = ref(Object.keys(RequestMethods));
|
||||
const methodFilters = ref<string[]>([]);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const moduleIds = computed(() => {
|
||||
if (props.activeModule === 'all') {
|
||||
return [];
|
||||
|
|
|
@ -366,7 +366,7 @@
|
|||
};
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const caseLevelFields = ref<Record<string, any>>({});
|
||||
const caseFilterVisible = ref(false);
|
||||
const caseFilters = ref<string[]>([]);
|
||||
|
@ -375,7 +375,7 @@
|
|||
});
|
||||
const lastReportStatusFilterVisible = ref(false);
|
||||
const lastReportStatusList = ['error', 'FakeError', 'success'];
|
||||
const lastReportStatusFilters = ref<string[]>([...lastReportStatusList]);
|
||||
const lastReportStatusFilters = ref<string[]>([]);
|
||||
|
||||
const moduleIds = computed(() => {
|
||||
return props.activeModule === 'all' ? [] : [props.activeModule];
|
||||
|
|
|
@ -174,8 +174,8 @@
|
|||
const statusFilterVisible = ref(false);
|
||||
const triggerModeFilterVisible = ref(false);
|
||||
|
||||
const statusListFilters = ref<string[]>(Object.keys(ReportStatus[props.moduleType]));
|
||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
||||
const statusListFilters = ref<string[]>([]);
|
||||
const triggerModeListFilters = ref<string[]>([]);
|
||||
|
||||
type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED';
|
||||
const showType = ref<ReportShowType>('All');
|
||||
|
@ -287,7 +287,7 @@
|
|||
moduleType: props.moduleType,
|
||||
filter: {
|
||||
status: statusListFilters.value,
|
||||
integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()),
|
||||
integrated: Array.of((showType.value === 'INTEGRATED').toString()),
|
||||
triggerMode: triggerModeListFilters.value,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
@close="handleClose"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<stepTypeVue
|
||||
v-if="props.step"
|
||||
v-show="props.step.stepType !== ScenarioStepType.CUSTOM_REQUEST"
|
||||
:step="props.step"
|
||||
/>
|
||||
{{ title }}
|
||||
<div class="flex max-w-[60%] items-center gap-[8px]">
|
||||
<stepTypeVue v-if="props.step" :step="props.step" />
|
||||
<a-tooltip :content="title" position="bottom">
|
||||
<div class="one-line-text">
|
||||
{{ title }}
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div
|
||||
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
||||
|
@ -475,7 +475,7 @@
|
|||
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
|
||||
return props.step?.name;
|
||||
}
|
||||
return t('apiScenario.customApi');
|
||||
return props.step?.name || t('apiScenario.customApi');
|
||||
});
|
||||
const showEnvPrefix = computed(
|
||||
() =>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-tooltip :content="activeStep?.name">
|
||||
<span> {{ characterLimit(activeStep?.name) }}</span>
|
||||
<div class="one-line-text max-w-[300px]"> {{ characterLimit(activeStep?.name) }}</div>
|
||||
</a-tooltip>
|
||||
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" />
|
||||
</div>
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
text: 'common.success',
|
||||
},
|
||||
[ScenarioExecuteStatus.UN_EXECUTE]: {
|
||||
bgColor: 'var(--color-text-4)',
|
||||
color: 'var(--color-text-n9)',
|
||||
bgColor: 'var(--color-text-n8)',
|
||||
color: 'var(--color-text-1)',
|
||||
text: 'apiScenario.unExecute',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -259,9 +259,9 @@
|
|||
});
|
||||
|
||||
const methodFilterVisible = ref(false);
|
||||
const methodFilters = ref(Object.keys(RequestMethods));
|
||||
const methodFilters = ref<string[]>([]);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const tableSelectedData = ref<MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]>([]);
|
||||
const tableSelectedKeys = computed(() => {
|
||||
return tableSelectedData.value.map((e) => e.id);
|
||||
|
@ -332,11 +332,8 @@
|
|||
moduleIds: ids || props.moduleIds,
|
||||
protocol: props.protocol,
|
||||
filter: {
|
||||
status:
|
||||
statusFilters.value.length === Object.keys(RequestDefinitionStatus).length
|
||||
? undefined
|
||||
: statusFilters.value,
|
||||
method: methodFilters.value.length === Object.keys(RequestMethods).length ? undefined : methodFilters.value,
|
||||
status: statusFilters.value,
|
||||
method: methodFilters.value,
|
||||
},
|
||||
});
|
||||
currentTable.value.loadList();
|
||||
|
@ -377,8 +374,8 @@
|
|||
function resetTable() {
|
||||
currentTable.value.resetSelector();
|
||||
keyword.value = '';
|
||||
methodFilters.value = Object.keys(RequestMethods);
|
||||
statusFilters.value = Object.keys(RequestDefinitionStatus);
|
||||
methodFilters.value = [];
|
||||
statusFilters.value = [];
|
||||
loadPage();
|
||||
}
|
||||
|
||||
|
|
|
@ -101,10 +101,10 @@
|
|||
import { ExecuteStatusFilters } from '@/enums/apiEnum';
|
||||
import { TriggerModeLabel } from '@/enums/reportEnum';
|
||||
|
||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
||||
const triggerModeListFilters = ref<string[]>([]);
|
||||
const triggerModeFilterVisible = ref(false);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(ExecuteStatusFilters));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const tableQueryParams = ref<any>();
|
||||
|
||||
const keyword = ref('');
|
||||
|
|
|
@ -368,7 +368,7 @@
|
|||
}>();
|
||||
|
||||
const lastReportStatusFilterVisible = ref(false);
|
||||
const lastReportStatusListFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]));
|
||||
const lastReportStatusListFilters = ref<string[]>([]);
|
||||
const lastReportStatusFilters = computed(() => {
|
||||
return Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]);
|
||||
});
|
||||
|
@ -586,7 +586,7 @@
|
|||
];
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(ApiScenarioStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const tableStore = useTableStore();
|
||||
|
||||
const activeModules = computed(() => {
|
||||
|
@ -621,7 +621,7 @@
|
|||
moduleIds,
|
||||
filter: {
|
||||
lastReportStatus: lastReportStatusListFilters.value,
|
||||
status: statusFilters.value.length === Object.keys(ApiScenarioStatus).length ? undefined : statusFilters.value,
|
||||
status: statusFilters.value,
|
||||
},
|
||||
};
|
||||
setLoadListParams(params);
|
||||
|
|
|
@ -168,18 +168,30 @@
|
|||
stepTreeRef.value?.checkAll(checkedAll.value);
|
||||
}
|
||||
|
||||
watch(checkedKeys, (val) => {
|
||||
if (val.length === 0) {
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = false;
|
||||
} else if (val.length === totalStepCount.value) {
|
||||
checkedAll.value = true;
|
||||
indeterminate.value = false;
|
||||
} else {
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = true;
|
||||
watch(
|
||||
() => checkedKeys.value,
|
||||
(val) => {
|
||||
if (val.length === 0) {
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = false;
|
||||
} else if (val.length === totalStepCount.value) {
|
||||
checkedAll.value = true;
|
||||
indeterminate.value = false;
|
||||
} else {
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
watch(
|
||||
() => scenario.value.steps.length,
|
||||
() => {
|
||||
checkedKeys.value = [];
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = false;
|
||||
}
|
||||
);
|
||||
|
||||
function expandAllStep() {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
|
@ -259,14 +271,19 @@
|
|||
if (!node.enable) {
|
||||
// 如果步骤未开启,则删除已选 id,方便下面waitingDebugStepDetails详情判断是否携带
|
||||
checkedKeysSet.delete(node.id);
|
||||
node.executeStatus = undefined;
|
||||
} else if (
|
||||
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
||||
) {
|
||||
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||
} else {
|
||||
// 其他类型步骤不显示执行状态
|
||||
node.executeStatus = undefined;
|
||||
}
|
||||
return !!node.enable;
|
||||
}
|
||||
node.executeStatus = undefined; // 未选中的步骤不显示执行状态
|
||||
return false;
|
||||
});
|
||||
const waitingDebugStepDetails = {};
|
||||
|
|
|
@ -179,8 +179,14 @@
|
|||
<template #extraEnd="step">
|
||||
<a-popover
|
||||
v-if="
|
||||
getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
|
||||
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED
|
||||
![
|
||||
ScenarioStepType.LOOP_CONTROLLER,
|
||||
ScenarioStepType.IF_CONTROLLER,
|
||||
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
||||
ScenarioStepType.CONSTANT_TIMER,
|
||||
].includes(step.stepType) &&
|
||||
(getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
|
||||
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED)
|
||||
"
|
||||
position="br"
|
||||
content-class="scenario-step-response-popover"
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import { RequestResult } from '@/models/apiTest/common';
|
||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||
import { ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||
|
||||
/**
|
||||
* 调试或执行结束后,调用本方法更新步骤的执行状态
|
||||
* @param steps 响应式的步骤列表
|
||||
*/
|
||||
export default function updateStepStatus(
|
||||
steps: ScenarioStepItem[],
|
||||
stepResponses: Record<string | number, RequestResult>
|
||||
) {
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
const node = steps[i];
|
||||
if (
|
||||
[
|
||||
ScenarioStepType.LOOP_CONTROLLER,
|
||||
ScenarioStepType.IF_CONTROLLER,
|
||||
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
||||
].includes(node.stepType)
|
||||
) {
|
||||
// 逻辑控制器内部可以放入任意步骤,所以它的最终执行结果是根据内部步骤的执行结果来判断的
|
||||
let hasNotExecuted = false;
|
||||
let hasFailure = false;
|
||||
if (!node.children || node.children.length === 0) {
|
||||
// 逻辑控制器内无步骤,则直接是未执行
|
||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||
} else {
|
||||
for (let j = 0; j < node.children.length; j++) {
|
||||
const childNode = node.children[j];
|
||||
updateStepStatus([childNode], stepResponses);
|
||||
if (
|
||||
childNode.executeStatus &&
|
||||
[ScenarioExecuteStatus.EXECUTING, ScenarioExecuteStatus.UN_EXECUTE].includes(childNode.executeStatus)
|
||||
) {
|
||||
// 子节点未执行或正在执行,则逻辑控制器也是未执行
|
||||
hasNotExecuted = true;
|
||||
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAILED) {
|
||||
// 子节点有一个失败,逻辑控制器就是失败
|
||||
hasFailure = true;
|
||||
}
|
||||
}
|
||||
// 递归完子节点后,判断当前逻辑控制器的状态
|
||||
if (hasFailure) {
|
||||
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
||||
} else if (hasNotExecuted) {
|
||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||
} else {
|
||||
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||
}
|
||||
}
|
||||
} else if (node.stepType === ScenarioStepType.CONSTANT_TIMER) {
|
||||
// 等待时间直接设置为成功
|
||||
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||
// 非逻辑控制器直接更改本身状态
|
||||
if (stepResponses[node.id]) {
|
||||
node.executeStatus = stepResponses[node.id].isSuccessful
|
||||
? ScenarioExecuteStatus.SUCCESS
|
||||
: ScenarioExecuteStatus.FAILED;
|
||||
} else {
|
||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
</a-tooltip>
|
||||
</template>
|
||||
</MsEditableTab>
|
||||
<div v-if="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
|
||||
<div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
|
||||
<environmentSelect v-model:current-env-config="currentEnvConfig" />
|
||||
<a-button type="primary" :loading="saveLoading" @click="saveScenario">
|
||||
{{ t('common.save') }}
|
||||
|
@ -124,10 +124,11 @@
|
|||
} from '@/models/apiTest/scenario';
|
||||
import { ModuleTreeNode } from '@/models/common';
|
||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||
import { ScenarioExecuteStatus, ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
||||
import { ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { defaultScenario } from './components/config';
|
||||
import updateStepStatus from './components/utils';
|
||||
|
||||
// 异步导入
|
||||
const detail = defineAsyncComponent(() => import('./detail/index.vue'));
|
||||
|
@ -147,11 +148,215 @@
|
|||
} as ScenarioParams,
|
||||
]);
|
||||
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
||||
const currentEnvConfig = ref<EnvConfig>();
|
||||
const executeButtonRef = ref<InstanceType<typeof executeButton>>();
|
||||
|
||||
const websocket = ref<WebSocket>();
|
||||
const temporaryScenarioReportMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||
|
||||
function setStepExecuteStatus() {
|
||||
updateStepStatus(activeScenarioTab.value.steps, activeScenarioTab.value.stepResponses);
|
||||
// activeScenarioTab.value.steps = mapTree<ScenarioStepItem>(activeScenarioTab.value.steps, (step) => {
|
||||
// if (step.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||
// // 如果结束执行时还有步骤是执行中状态,则设置为未执行
|
||||
// step.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||
// }
|
||||
// return step;
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启websocket监听,接收执行结果
|
||||
*/
|
||||
function debugSocket(reportId?: string | number, executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
||||
websocket.value = getSocket(
|
||||
reportId || '',
|
||||
executeType === 'localExec' ? '/ws/debug' : '',
|
||||
executeType === 'localExec' ? localExecuteUrl : ''
|
||||
);
|
||||
websocket.value.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.msgType === 'EXEC_RESULT') {
|
||||
if (activeScenarioTab.value.reportId === data.reportId) {
|
||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||
data.taskResult.requestResults.forEach((result) => {
|
||||
activeScenarioTab.value.stepResponses[result.stepId] = {
|
||||
...result,
|
||||
console: data.taskResult.console,
|
||||
};
|
||||
if (result.isSuccessful) {
|
||||
activeScenarioTab.value.executeSuccessCount += 1;
|
||||
} else {
|
||||
activeScenarioTab.value.executeFailCount += 1;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||
data.taskResult.requestResults.forEach((result) => {
|
||||
if (activeScenarioTab.value.reportId) {
|
||||
if (temporaryScenarioReportMap[activeScenarioTab.value.reportId] === undefined) {
|
||||
temporaryScenarioReportMap[activeScenarioTab.value.reportId] = {};
|
||||
}
|
||||
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId] = {
|
||||
...result,
|
||||
console: data.taskResult.console,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (data.msgType === 'EXEC_END') {
|
||||
// 执行结束,关闭websocket
|
||||
websocket.value?.close();
|
||||
if (activeScenarioTab.value.reportId === data.reportId) {
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
activeScenarioTab.value.isExecute = false;
|
||||
setStepExecuteStatus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际执行函数
|
||||
* @param executeParams 执行参数
|
||||
* @param isExecute 是否执行,否则是调试
|
||||
* @param executeType 执行类型
|
||||
* @param localExecuteUrl 本地执行地址
|
||||
*/
|
||||
async function realExecute(
|
||||
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>,
|
||||
isExecute?: boolean,
|
||||
executeType?: 'localExec' | 'serverExec',
|
||||
localExecuteUrl?: string
|
||||
) {
|
||||
try {
|
||||
activeScenarioTab.value.executeLoading = true;
|
||||
debugSocket(executeParams.reportId, executeType, localExecuteUrl); // 开启websocket
|
||||
// 重置执行结果
|
||||
activeScenarioTab.value.executeTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
activeScenarioTab.value.executeSuccessCount = 0;
|
||||
activeScenarioTab.value.executeFailCount = 0;
|
||||
activeScenarioTab.value.stepResponses = {};
|
||||
activeScenarioTab.value.reportId = executeParams.reportId; // 存储报告ID
|
||||
activeScenarioTab.value.isDebug = !isExecute;
|
||||
let res;
|
||||
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
|
||||
// 执行场景且非本地执行且非未保存场景
|
||||
res = await executeScenario({
|
||||
id: activeScenarioTab.value.id,
|
||||
grouped: false,
|
||||
environmentId: currentEnvConfig.value?.id || '',
|
||||
projectId: appStore.currentProjectId,
|
||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
||||
linkFileIds: activeScenarioTab.value.linkFileIds,
|
||||
...executeParams,
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
return {
|
||||
...node,
|
||||
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||
};
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
res = await debugScenario({
|
||||
id: activeScenarioTab.value.id,
|
||||
grouped: false,
|
||||
environmentId: currentEnvConfig.value?.id || '',
|
||||
projectId: appStore.currentProjectId,
|
||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
||||
linkFileIds: activeScenarioTab.value.linkFileIds,
|
||||
frontendDebug: executeType === 'localExec',
|
||||
...executeParams,
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
return {
|
||||
...node,
|
||||
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (executeType === 'localExec' && localExecuteUrl) {
|
||||
// 本地执行需要调 debug 接口获取响应结果,然后再调本地执行接口
|
||||
await localExecuteApiDebug(localExecuteUrl, res);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
websocket.value?.close();
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
setStepExecuteStatus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行场景
|
||||
* @param executeType 执行类型
|
||||
* @param localExecuteUrl 本地执行地址
|
||||
*/
|
||||
function handleExecute(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
||||
const waitingDebugStepDetails = {};
|
||||
const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => {
|
||||
if (node.enable) {
|
||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
|
||||
if (
|
||||
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
||||
) {
|
||||
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||
}
|
||||
}
|
||||
return !!node.enable;
|
||||
});
|
||||
realExecute(
|
||||
{
|
||||
steps: waitTingDebugSteps,
|
||||
stepDetails: waitingDebugStepDetails,
|
||||
reportId: getGenerateId(),
|
||||
},
|
||||
true,
|
||||
executeType,
|
||||
localExecuteUrl
|
||||
);
|
||||
}
|
||||
|
||||
function handleStopExecute() {
|
||||
websocket.value?.close();
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
setStepExecuteStatus();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => activeScenarioTab.value.id,
|
||||
(val) => {
|
||||
if (val !== 'all' && activeScenarioTab.value.reportId && !activeScenarioTab.value.executeLoading) {
|
||||
// 当前查看的 tab 非全部场景 tab 页,且当前场景有报告ID,且不是正在执行中,则读取缓存报告
|
||||
const cacheReport = temporaryScenarioReportMap[activeScenarioTab.value.reportId];
|
||||
if (cacheReport) {
|
||||
// 如果有缓存的报告未读取,则直接赋值
|
||||
Object.keys(cacheReport).forEach((stepId) => {
|
||||
const result = cacheReport[stepId];
|
||||
activeScenarioTab.value.stepResponses[stepId] = result;
|
||||
if (result.isSuccessful) {
|
||||
activeScenarioTab.value.executeSuccessCount += 1;
|
||||
} else {
|
||||
activeScenarioTab.value.executeFailCount += 1;
|
||||
}
|
||||
});
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
delete temporaryScenarioReportMap[activeScenarioTab.value.reportId]; // 取完释放缓存
|
||||
setStepExecuteStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function newTab(defaultScenarioInfo?: Scenario, action?: 'copy' | 'execute') {
|
||||
if (defaultScenarioInfo) {
|
||||
const isCopy = action === 'copy';
|
||||
let copySteps = defaultScenarioInfo.steps;
|
||||
let copySteps: ScenarioStepItem[] = [];
|
||||
if (isCopy) {
|
||||
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
|
||||
return {
|
||||
|
@ -160,6 +365,8 @@
|
|||
id: getGenerateId(),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
copySteps = mapTree(defaultScenarioInfo.steps);
|
||||
}
|
||||
scenarioTabs.value.push({
|
||||
...defaultScenarioInfo,
|
||||
|
@ -168,9 +375,14 @@
|
|||
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||
name: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||
isNew: isCopy,
|
||||
isExecute: action === 'execute',
|
||||
stepResponses: {},
|
||||
});
|
||||
if (action === 'execute') {
|
||||
nextTick(() => {
|
||||
// 等待激活 tab 设置完毕后执行
|
||||
handleExecute(executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
scenarioTabs.value.push({
|
||||
...cloneDeep(defaultScenario),
|
||||
|
@ -190,8 +402,6 @@
|
|||
const activeFolder = ref<string>('all');
|
||||
const offspringIds = ref<string[]>([]);
|
||||
const isShowScenario = ref(false);
|
||||
const executeButtonRef = ref<InstanceType<typeof executeButton>>();
|
||||
const currentEnvConfig = ref<EnvConfig>();
|
||||
|
||||
// 获取激活用例类型样式
|
||||
const getActiveClass = (type: string) => {
|
||||
|
@ -320,181 +530,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
const websocket = ref<WebSocket>();
|
||||
const temporaryScenarioReportMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||
|
||||
/**
|
||||
* 开启websocket监听,接收执行结果
|
||||
*/
|
||||
function debugSocket(reportId?: string | number, executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
||||
websocket.value = getSocket(
|
||||
reportId || '',
|
||||
executeType === 'localExec' ? '/ws/debug' : '',
|
||||
executeType === 'localExec' ? localExecuteUrl : ''
|
||||
);
|
||||
websocket.value.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.msgType === 'EXEC_RESULT') {
|
||||
if (activeScenarioTab.value.reportId === data.reportId) {
|
||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||
data.taskResult.requestResults.forEach((result) => {
|
||||
activeScenarioTab.value.stepResponses[result.stepId] = {
|
||||
...result,
|
||||
console: data.taskResult.console,
|
||||
};
|
||||
if (result.isSuccessful) {
|
||||
activeScenarioTab.value.executeSuccessCount += 1;
|
||||
} else {
|
||||
activeScenarioTab.value.executeFailCount += 1;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||
data.taskResult.requestResults.forEach((result) => {
|
||||
if (activeScenarioTab.value.reportId) {
|
||||
if (temporaryScenarioReportMap[activeScenarioTab.value.reportId] === undefined) {
|
||||
temporaryScenarioReportMap[activeScenarioTab.value.reportId] = {};
|
||||
}
|
||||
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId] = {
|
||||
...result,
|
||||
console: data.taskResult.console,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (data.msgType === 'EXEC_END') {
|
||||
// 执行结束,关闭websocket
|
||||
websocket.value?.close();
|
||||
if (activeScenarioTab.value.reportId === data.reportId) {
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
activeScenarioTab.value.isExecute = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function realExecute(
|
||||
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>,
|
||||
isExecute?: boolean,
|
||||
executeType?: 'localExec' | 'serverExec',
|
||||
localExecuteUrl?: string
|
||||
) {
|
||||
try {
|
||||
activeScenarioTab.value.executeLoading = true;
|
||||
debugSocket(executeParams.reportId, executeType, localExecuteUrl); // 开启websocket
|
||||
// 重置执行结果
|
||||
activeScenarioTab.value.executeTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
activeScenarioTab.value.executeSuccessCount = 0;
|
||||
activeScenarioTab.value.executeFailCount = 0;
|
||||
activeScenarioTab.value.stepResponses = {};
|
||||
activeScenarioTab.value.reportId = executeParams.reportId; // 存储报告ID
|
||||
activeScenarioTab.value.isDebug = !isExecute;
|
||||
let res;
|
||||
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
|
||||
// 执行场景且非本地执行且非未保存场景
|
||||
res = await executeScenario({
|
||||
id: activeScenarioTab.value.id,
|
||||
grouped: false,
|
||||
environmentId: currentEnvConfig.value?.id || '',
|
||||
projectId: appStore.currentProjectId,
|
||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
||||
linkFileIds: activeScenarioTab.value.linkFileIds,
|
||||
...executeParams,
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
return {
|
||||
...node,
|
||||
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||
};
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
res = await debugScenario({
|
||||
id: activeScenarioTab.value.id,
|
||||
grouped: false,
|
||||
environmentId: currentEnvConfig.value?.id || '',
|
||||
projectId: appStore.currentProjectId,
|
||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
||||
linkFileIds: activeScenarioTab.value.linkFileIds,
|
||||
frontendDebug: executeType === 'localExec',
|
||||
...executeParams,
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
return {
|
||||
...node,
|
||||
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (executeType === 'localExec' && localExecuteUrl) {
|
||||
// 本地执行需要调 debug 接口获取响应结果,然后再调本地执行接口
|
||||
await localExecuteApiDebug(localExecuteUrl, res);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
websocket.value?.close();
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleExecute(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
||||
const waitingDebugStepDetails = {};
|
||||
const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => {
|
||||
if (node.enable) {
|
||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
|
||||
if (
|
||||
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
||||
) {
|
||||
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||
}
|
||||
}
|
||||
return !!node.enable;
|
||||
});
|
||||
realExecute(
|
||||
{
|
||||
steps: waitTingDebugSteps,
|
||||
stepDetails: waitingDebugStepDetails,
|
||||
reportId: getGenerateId(),
|
||||
},
|
||||
true,
|
||||
executeType,
|
||||
localExecuteUrl
|
||||
);
|
||||
}
|
||||
|
||||
function handleStopExecute() {
|
||||
websocket.value?.close();
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => activeScenarioTab.value.id,
|
||||
(val) => {
|
||||
if (val !== 'all' && activeScenarioTab.value.reportId && !activeScenarioTab.value.executeLoading) {
|
||||
// 当前查看的 tab 非全部场景 tab 页,且当前场景有报告ID,且不是正在执行中,则读取缓存报告
|
||||
const cacheReport = temporaryScenarioReportMap[activeScenarioTab.value.reportId];
|
||||
if (cacheReport) {
|
||||
// 如果有缓存的报告未读取,则直接赋值
|
||||
Object.keys(cacheReport).forEach((stepId) => {
|
||||
const result = cacheReport[stepId];
|
||||
activeScenarioTab.value.stepResponses[stepId] = result;
|
||||
if (result.isSuccessful) {
|
||||
activeScenarioTab.value.executeSuccessCount += 1;
|
||||
} else {
|
||||
activeScenarioTab.value.executeFailCount += 1;
|
||||
}
|
||||
});
|
||||
activeScenarioTab.value.executeLoading = false;
|
||||
delete temporaryScenarioReportMap[activeScenarioTab.value.reportId]; // 取完释放缓存
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
|
||||
const scenarioId = computed(() => activeScenarioTab.value.id);
|
||||
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
|
||||
|
|
|
@ -162,7 +162,7 @@
|
|||
}>();
|
||||
|
||||
const lastReportStatusFilterVisible = ref(false);
|
||||
const lastReportStatusListFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]));
|
||||
const lastReportStatusListFilters = ref<string[]>([]);
|
||||
const lastReportStatusFilters = computed(() => {
|
||||
return Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]);
|
||||
});
|
||||
|
@ -335,7 +335,7 @@
|
|||
};
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref(Object.keys(ApiScenarioStatus));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const tableStore = useTableStore();
|
||||
|
||||
async function loadScenarioList(refreshTreeCount?: boolean) {
|
||||
|
@ -354,7 +354,7 @@
|
|||
moduleIds,
|
||||
filter: {
|
||||
lastReportStatus: lastReportStatusListFilters.value,
|
||||
status: statusFilters.value.length === Object.keys(ApiScenarioStatus).length ? undefined : statusFilters.value,
|
||||
status: statusFilters.value,
|
||||
},
|
||||
};
|
||||
setLoadListParams(params);
|
||||
|
|
|
@ -876,9 +876,9 @@
|
|||
excludeIds: [],
|
||||
currentSelectCount: 0,
|
||||
});
|
||||
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const caseFilters = ref<string[]>([]);
|
||||
const executeResultFilters = ref(Object.keys(executionResultMap));
|
||||
const executeResultFilters = ref<string[]>([]);
|
||||
|
||||
async function initTableParams() {
|
||||
let moduleIds: string[] = [];
|
||||
|
|
|
@ -633,14 +633,14 @@
|
|||
});
|
||||
|
||||
// 用例等级表头检索
|
||||
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const caseLevelFields = ref<Record<string, any>>({});
|
||||
const caseFilterVisible = ref(false);
|
||||
const caseLevelList = computed(() => {
|
||||
return caseLevelFields.value?.options || [];
|
||||
});
|
||||
const caseFilters = ref<string[]>([]);
|
||||
const executeResultFilters = ref(Object.keys(executionResultMap));
|
||||
const executeResultFilters = ref<string[]>([]);
|
||||
const updateUserFilters = ref<string[]>([]);
|
||||
const createUserFilters = ref<string[]>([]);
|
||||
const deleteUserFilters = ref<string[]>([]);
|
||||
|
|
|
@ -240,9 +240,8 @@
|
|||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { BugListItem, BugOptionItem } from '@/models/bug-management';
|
||||
import { BugOptionItem } from '@/models/bug-management';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { CommonList } from '@/models/common';
|
||||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
|
||||
|
|
|
@ -473,7 +473,7 @@
|
|||
async function initFilter() {
|
||||
await featureStore.getDefaultTemplate();
|
||||
caseLevelList.value = featureStore.getSystemCaseLevelFields();
|
||||
caseFilters.value = caseLevelList.value.map((item) => item.value);
|
||||
caseFilters.value = [];
|
||||
}
|
||||
|
||||
watch(
|
||||
|
|
|
@ -350,7 +350,7 @@
|
|||
const tableParams = ref<Record<string, any>>({});
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref<string[]>(Object.keys(reviewResultMap));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
|
||||
const hasOperationPermission = computed(() =>
|
||||
hasAnyPermission(['CASE_REVIEW:READ+REVIEW', 'CASE_REVIEW:READ+RELEVANCE'])
|
||||
|
|
|
@ -495,7 +495,7 @@
|
|||
};
|
||||
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref<string[]>(Object.keys(reviewStatusMap));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
const tableQueryParams = ref<any>();
|
||||
async function searchReview(filter?: FilterResult) {
|
||||
let moduleIds: string[] = [];
|
||||
|
|
|
@ -107,7 +107,6 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import AddScriptDrawer from '@/components/business/ms-common-script/ms-addScriptDrawer.vue';
|
||||
import commonScriptStatus from './components/commonScriptStatus.vue';
|
||||
import ScriptDetailDrawer from './components/scriptDetailDrawer.vue';
|
||||
|
@ -129,7 +128,7 @@
|
|||
ParamsRequestType,
|
||||
} from '@/models/projectManagement/commonScript';
|
||||
import { CommonScriptStatusEnum } from '@/enums/commonScriptStatusEnum';
|
||||
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
|
@ -141,7 +140,7 @@
|
|||
const { t } = useI18n();
|
||||
const statusFilterVisible = ref(false);
|
||||
const keyword = ref<string>('');
|
||||
const statusFilters = ref<string[]>(Object.keys(CommonScriptStatusEnum));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
|
||||
const hasOperationPermission = computed(() =>
|
||||
hasAnyPermission(['PROJECT_CUSTOM_FUNCTION:READ+UPDATE', 'PROJECT_CUSTOM_FUNCTION:READ+DELETE'])
|
||||
|
|
|
@ -112,7 +112,6 @@
|
|||
} from '@/api/modules/project-management/taskCenter';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
||||
import { useTableStore } from '@/store';
|
||||
import { characterLimit } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
@ -135,16 +134,7 @@
|
|||
}>();
|
||||
const keyword = ref<string>('');
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusListFilters = ref<string[]>(Object.keys(TaskStatus[props.moduleType]));
|
||||
const { openNewPage } = useOpenNewPage();
|
||||
const filterOptions = computed(() => {
|
||||
return statusListFilters.value.map((item) => {
|
||||
return {
|
||||
label: item,
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
});
|
||||
const statusListFilters = ref<string[]>([]);
|
||||
|
||||
const permissionsMap = {
|
||||
organization: {
|
||||
|
|
|
@ -218,7 +218,7 @@
|
|||
const keyword = ref<string>('');
|
||||
const scrollWidth = ref<number>(3400);
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusFilters = ref<string[]>(Object.keys(reviewStatusMap));
|
||||
const statusFilters = ref<string[]>([]);
|
||||
|
||||
const tableBatchActions = {
|
||||
baseAction: [
|
||||
|
|
Loading…
Reference in New Issue