diff --git a/frontend/config/vite.config.dev.ts b/frontend/config/vite.config.dev.ts index 394eda156e..48a0203933 100644 --- a/frontend/config/vite.config.dev.ts +++ b/frontend/config/vite.config.dev.ts @@ -22,6 +22,11 @@ export default mergeConfig( changeOrigin: true, rewrite: (path: string) => path.replace(/^\/front\/file/, ''), }, + '/attachment': { + target: 'http://172.16.200.18:8081/', + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''), + }, '/plugin/image': { target: 'http://172.16.200.18:8081/', changeOrigin: true, diff --git a/frontend/src/api/modules/case-management/featureCase.ts b/frontend/src/api/modules/case-management/featureCase.ts index 33be95ee75..bf5dd89623 100644 --- a/frontend/src/api/modules/case-management/featureCase.ts +++ b/frontend/src/api/modules/case-management/featureCase.ts @@ -1,3 +1,4 @@ +import { MsFileItem } from '@/components/pure/ms-upload/types'; import { CommentItem, CommentParams } from '@/components/business/ms-comment/types'; import MSR from '@/api/http/index'; @@ -27,6 +28,7 @@ import { DownloadExcelTemplateUrl, DownloadFileUrl, EditorUploadFileUrl, + exportExcelCheckUrl, FollowerCaseUrl, GetAssociatedCaseIdsUrl, GetAssociatedDebuggerUrl, @@ -80,6 +82,7 @@ import type { DeleteCaseType, DemandItem, DetailCase, + ImportExcelType, ModulesTreeType, OperationFile, PreviewImages, @@ -378,6 +381,12 @@ export function downloadTemplate(projectId: string, type: 'Excel' | 'Xmind') { { isTransformResponse: false } ); } + +// 导入excel文件检查 +export function importExcelChecked(data: { request: ImportExcelType; fileList: File[] }) { + return MSR.uploadFile({ url: exportExcelCheckUrl }, { request: data.request, fileList: data.fileList }, ''); +} + // 富文本编辑器上传图片文件 export function editorUploadFile(data: { fileList: File[] }) { return MSR.uploadFile({ url: EditorUploadFileUrl }, { fileList: data.fileList }, '', false); diff --git a/frontend/src/api/modules/project-management/commonScript.ts b/frontend/src/api/modules/project-management/commonScript.ts index a7bbe303b7..2947525bad 100644 --- a/frontend/src/api/modules/project-management/commonScript.ts +++ b/frontend/src/api/modules/project-management/commonScript.ts @@ -1,6 +1,7 @@ import MSR from '@/api/http/index'; import { AddCommonScriptUrl, + ConnectionWebsocketUrl, DeleteCommonScriptUrl, GetCommonScriptDetailUrl, GetCommonScriptPageUrl, @@ -9,12 +10,17 @@ import { GetFormApiImportPageListUrl, GetFormApiImportUrl, GetInsertCommonScriptPageUrl, + TestScriptUrl, UpdateCommonScriptUrl, } from '@/api/requrls/project-management/commonScript'; import type { ModulesTreeType } from '@/models/caseManagement/featureCase'; import { CommonList, TableQueryParams } from '@/models/common'; -import type { AddOrUpdateCommonScript, CommonScriptItem } from '@/models/projectManagement/commonScript'; +import type { + AddOrUpdateCommonScript, + CommonScriptItem, + TestScriptType, +} from '@/models/projectManagement/commonScript'; // 获取公共脚本列表 export function getCommonScriptPage(data: TableQueryParams) { @@ -59,3 +65,21 @@ export function getFormApiImportPageList(data: TableQueryParams) { export function getFormApiImportModuleCount(data: TableQueryParams) { return MSR.post>({ url: GetFormApiImportModuleCountUrl, data }); } + +// 测试脚本 +export function testCommonScript(data: TestScriptType) { + return MSR.post({ url: TestScriptUrl, data }); +} +// apiSocket 建立连接 +export const apiSocket = (url: string) => { + let protocol = 'ws://'; + if (window.location.protocol === 'https:') { + protocol = 'wss://'; + } + const uri = protocol + window.location.host + url; + return new WebSocket(uri); +}; + +export function getSocket(reportId: string) { + return apiSocket(`${ConnectionWebsocketUrl}/${reportId}`); +} diff --git a/frontend/src/api/modules/test-plan/testPlan.ts b/frontend/src/api/modules/test-plan/testPlan.ts new file mode 100644 index 0000000000..c4581211b0 --- /dev/null +++ b/frontend/src/api/modules/test-plan/testPlan.ts @@ -0,0 +1,41 @@ +import MSR from '@/api/http/index'; +import { + addTestPlanModuleUrl, + DeleteTestPlanModuleUrl, + GetTestPlanModuleCountUrl, + GetTestPlanModuleUrl, + MoveTestPlanModuleUrl, + updateTestPlanModuleUrl, +} from '@/api/requrls/test-plan/testPlan'; + +import type { CreateOrUpdateModule, ModulesTreeType, UpdateModule } from '@/models/caseManagement/featureCase'; +import type { CommonList, MoveModules, TableQueryParams } from '@/models/common'; +// 获取模块树 +export function getTestPlanModule(params: TableQueryParams) { + return MSR.get({ url: `${GetTestPlanModuleUrl}/${params.projectId}` }); +} + +// 创建模块树 +export function createPlanModuleTree(data: CreateOrUpdateModule) { + return MSR.post({ url: addTestPlanModuleUrl, data }); +} + +// 更新模块树 +export function updatePlanModuleTree(data: UpdateModule) { + return MSR.post({ url: updateTestPlanModuleUrl, data }); +} + +// 移动模块树 +export function moveTestPlanModuleTree(data: MoveModules) { + return MSR.post({ url: MoveTestPlanModuleUrl, data }); +} + +// 删除模块 +export function deletePlanModuleTree(id: string) { + return MSR.get({ url: `${DeleteTestPlanModuleUrl}/${id}` }); +} + +// 获取模块数量 +export function getPlanModulesCounts(data: TableQueryParams) { + return MSR.post({ url: GetTestPlanModuleCountUrl, data }); +} diff --git a/frontend/src/api/requrls/case-management/featureCase.ts b/frontend/src/api/requrls/case-management/featureCase.ts index 17627287a5..0c489eb60d 100644 --- a/frontend/src/api/requrls/case-management/featureCase.ts +++ b/frontend/src/api/requrls/case-management/featureCase.ts @@ -138,6 +138,6 @@ export const DownloadExcelTemplateUrl = '/functional/case/download/excel/templat // 富文本所需资源上传 export const EditorUploadFileUrl = '/attachment/upload/temp/file'; // 富文本资源详情预览压缩图 -export const EditorPreviewImagesUrl = '/attachment/preview/compressed'; -// 预览压缩图 -export const PreviewEditorImageUrl = '/attachment/preview'; +export const PreviewEditorImageUrl = '/attachment/download/file'; +// 导入excel文件检查 +export const exportExcelCheckUrl = '/functional/case/pre-check/excel'; diff --git a/frontend/src/api/requrls/project-management/commonScript.ts b/frontend/src/api/requrls/project-management/commonScript.ts index 92d4a762fa..00879ffb2a 100644 --- a/frontend/src/api/requrls/project-management/commonScript.ts +++ b/frontend/src/api/requrls/project-management/commonScript.ts @@ -18,3 +18,7 @@ export const GetFormApiImportUrl = '/api/definition/module/tree'; export const GetFormApiImportPageListUrl = '/api/definition/page'; // 获取公共脚本从api导入模块数量 export const GetFormApiImportModuleCountUrl = '/api/definition/module/count'; +// 脚本测试 +export const TestScriptUrl = '/api/test/custom/func/run'; +// websoket连接 +export const ConnectionWebsocketUrl = '/ws/api'; diff --git a/frontend/src/api/requrls/test-plan/testPlan.ts b/frontend/src/api/requrls/test-plan/testPlan.ts new file mode 100644 index 0000000000..bdcdf71f00 --- /dev/null +++ b/frontend/src/api/requrls/test-plan/testPlan.ts @@ -0,0 +1,12 @@ +// 测试计划模块树 +export const GetTestPlanModuleUrl = `/test-plan/module/tree`; +// 添加测试计划模块树 +export const addTestPlanModuleUrl = `/test-plan/module/add`; +// 更新计划测试模块树 +export const updateTestPlanModuleUrl = '/test-plan/module/update'; +// 移动计划测试模块树 +export const MoveTestPlanModuleUrl = '/test-plan/module/move'; +// 删除计划测试模块树 +export const DeleteTestPlanModuleUrl = '/test-plan/module/delete'; +// 测试计划模块树数量 +export const GetTestPlanModuleCountUrl = '/test-plan/module/count'; diff --git a/frontend/src/components/business/ms-case-associate/caseLevel.vue b/frontend/src/components/business/ms-case-associate/caseLevel.vue index 7aa107037c..ef81fd80a0 100644 --- a/frontend/src/components/business/ms-case-associate/caseLevel.vue +++ b/frontend/src/components/business/ms-case-associate/caseLevel.vue @@ -17,23 +17,23 @@ }>(); const caseLevelMap = { - P1: { - label: 'P1', + P0: { + label: 'P0', bgColor: 'rgb(var(--danger-2))', borderColor: 'rgb(var(--danger-6))', }, - P2: { - label: 'P2', + P1: { + label: 'P1', bgColor: 'rgb(var(--warning-2))', borderColor: 'rgb(var(--warning-6))', }, - P3: { - label: 'P3', + P2: { + label: 'P2', bgColor: 'rgb(var(--link-2))', borderColor: 'rgb(var(--link-5))', }, - P4: { - label: 'P4', + P3: { + label: 'P3', bgColor: 'var(--color-text-n8)', borderColor: 'var(--color-text-brand)', }, diff --git a/frontend/src/components/business/ms-case-associate/types.ts b/frontend/src/components/business/ms-case-associate/types.ts index b84a8d2e4f..21927fc851 100644 --- a/frontend/src/components/business/ms-case-associate/types.ts +++ b/frontend/src/components/business/ms-case-associate/types.ts @@ -1 +1 @@ -export type CaseLevel = 'P1' | 'P2' | 'P3' | 'P4'; +export type CaseLevel = 'P0' | 'P1' | 'P2' | 'P3'; diff --git a/frontend/src/components/business/ms-common-script/ms-addScriptDrawer.vue b/frontend/src/components/business/ms-common-script/ms-addScriptDrawer.vue index e785375e1d..cf8c60dbed 100644 --- a/frontend/src/components/business/ms-common-script/ms-addScriptDrawer.vue +++ b/frontend/src/components/business/ms-common-script/ms-addScriptDrawer.vue @@ -55,7 +55,9 @@ {{ t('project.commonScript.commonScript') }} {{ t('project.commonScript.executionResult') }} - {{ t('project.commonScript.scriptTest') }} + {{ + t('project.commonScript.scriptTest') + }} (undefined); const formRef = ref(); const { t } = useI18n(); @@ -120,7 +125,7 @@ projectId: '', params: '', script: '', - type: 'beanshellJSR223', + type: 'beanshell-jsr233', result: '', }; @@ -140,7 +145,7 @@ { title: 'project.commonScript.description', slotName: 'desc', - dataIndex: 'description', + dataIndex: 'desc', }, { title: 'project.commonScript.isRequired', @@ -219,6 +224,69 @@ } } ); + + const loading = ref(false); + const websocket = ref(); + + function onDebugMessage(e: any) { + if (e.data && e.data.startsWith('result_')) { + try { + const data = e.data.substring(7); + websocket.value.close(); + console.log(data, 'datadatadatadatadata'); + } catch (error) { + websocket.value.close(); + } + } + } + // 测试脚本 + async function testScript() { + try { + loading.value = true; + const { type, script } = form.value; + const parameters = innerParams.value + .filter((item: any) => item.name && item.value) + .map((item) => { + return { + key: item.name, + value: item.value, + valid: item.mustContain, + }; + }); + const params = { + type, + script, + params: parameters, + projectId: appStore.currentProjectId, + }; + + const reportId = await testCommonScript(params); + if (reportId) { + websocket.value = getSocket(reportId); + // TODO 接口需要调整 + // websocket.value.addEventListener('open', (event) => { + // console.log('WebSocket连接已打开:', event); + // websocket.value.send('Hello, WebSocket Server!'); + // }); + + // websocket.value.addEventListener('message', (event) => { + // console.log('接收到消息:', event.data); + // }); + + // websocket.value.addEventListener('close', (event) => { + // console.log('WebSocket连接已关闭:', event); + // }); + + // websocket.value.addEventListener('error', (event) => { + // console.error('WebSocket连接发生错误:', event); + // }); + } + } catch (error) { + console.log(error); + } finally { + loading.value = false; + } + } diff --git a/frontend/src/components/business/ms-common-script/ms-script-menu.vue b/frontend/src/components/business/ms-common-script/ms-script-menu.vue index 308b74fcda..99818832b8 100644 --- a/frontend/src/components/business/ms-common-script/ms-script-menu.vue +++ b/frontend/src/components/business/ms-common-script/ms-script-menu.vue @@ -70,7 +70,7 @@ const innerLanguageType = useVModel(props, 'languagesType', emit); const languages = [ - { text: 'beanshellJSR223', value: 'beanshellJSR223' }, + { text: 'beanshellJSR223', value: 'beanshell-jsr233' }, { text: 'beanshell', value: 'beanshell' }, { text: 'python', value: 'python' }, { text: 'groovy', value: 'groovy' }, diff --git a/frontend/src/components/business/ms-common-script/utils.ts b/frontend/src/components/business/ms-common-script/utils.ts index 2d9dfb87e2..adc3d09144 100644 --- a/frontend/src/components/business/ms-common-script/utils.ts +++ b/frontend/src/components/business/ms-common-script/utils.ts @@ -5,7 +5,7 @@ import type { CommonScriptMenu } from '@/models/projectManagement/commonScript'; const { t } = useI18n(); export type Languages = - | 'beanshellJSR223' + | 'beanshell-jsr233' | 'groovy' | 'python' | 'beanshell' @@ -518,7 +518,7 @@ export function getCodeTemplate(language: Languages, requestObj: any) { return jsCode(requestObj); case 'javascript': return jsCode(requestObj); - case 'beanshellJSR223': + case 'beanshell-jsr233': return javaCode(requestObj); default: return ''; diff --git a/frontend/src/components/pure/ms-rich-text/MsRichText.vue b/frontend/src/components/pure/ms-rich-text/MsRichText.vue index 3c90f89ba6..dee799fc32 100644 --- a/frontend/src/components/pure/ms-rich-text/MsRichText.vue +++ b/frontend/src/components/pure/ms-rich-text/MsRichText.vue @@ -13,19 +13,22 @@ * return unified().use(rehypeParse).use(rehypeFormat).use(rehypeStringify).processSync(content.value); */ import { useDebounceFn, useLocalStorage } from '@vueuse/core'; + import { useVModel } from '@vueuse/core'; import type { MsFileItem } from '@/components/pure/ms-upload/types'; import AttachmentSelectorModal from './attachmentSelectorModal.vue'; import { editorUploadFile } from '@/api/modules/case-management/featureCase'; + import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase'; import { useI18n } from '@/hooks/useI18n'; import useLocale from '@/locale/useLocale'; + import { useAppStore } from '@/store'; import '@halo-dev/richtext-editor/dist/style.css'; import ExtensionImage from './extensions/image/index'; import suggestion from './extensions/mention/suggestion'; import { - type AnyExtension, + DecorationSet, Editor, Extension, ExtensionAudio, @@ -66,6 +69,8 @@ ExtensionUnderline, ExtensionVideo, lowlight, + Plugin, + PluginKey, RichTextEditor, ToolbarItem, ToolboxItem, @@ -74,12 +79,13 @@ import type { queueAsPromised } from 'fastq'; import * as fastq from 'fastq'; + const appStore = useAppStore(); const { t } = useI18n(); // image drag and paste upload type Task = { file: File; - process: (compressUrl: string, permalink: string, fileId: string) => void; + process: (permalink: string, fileId: string) => void; }; const props = withDefaults( @@ -87,6 +93,7 @@ raw?: string; uploadImage?: (file: File) => Promise; maxHeight?: string; + filedIds?: string[]; }>(), { raw: '', @@ -98,100 +105,20 @@ const emit = defineEmits<{ (event: 'update:raw', value: string): void; + (event: 'update:filedIds', value: string[]): void; (event: 'update', value: string): void; }>(); - /** - * 图片压缩 - * @param {*} img 图片对象 - * @param {*} type 图片类型 - * @param {*} maxWidth 图片最大宽度 - * @param {*} flag - */ - - function compress(img, type, maxWidth, flag) { - let canvas: HTMLCanvasElement | null = document.createElement('canvas'); - let ctx2: any = canvas.getContext('2d'); - - const ratio = img.width / img.height; - let { width } = img; - let { height } = img; - // 根据flag判断是否压缩图片 - if (flag && maxWidth <= width) { - width = maxWidth; - height = maxWidth / ratio; // 维持图片宽高比 - } - canvas.width = width; - canvas.height = height; - - ctx2.fillStyle = '#fff'; - ctx2.fillRect(0, 0, canvas.width, canvas.height); - ctx2.drawImage(img, 0, 0, width, height); - - let base64Data = canvas.toDataURL(type, 0.75); - - if (type === 'image/gif') { - const regx = /(?<=data:image).*?(?=;base64)/; // 正则表示时在用于replace时,根据浏览器的不同,有的需要为字符串 - base64Data = base64Data.replace(regx, '/gif'); - } - canvas = null; - ctx2 = null; - return base64Data; - } - - function handleFile(file: File, callback: any, maxWidth = 600) { - if (!file || !/\/(?:png|jpg|jpeg|gif)/i.test(file.type)) { - return; - } - const reader = new FileReader(); - // eslint-disable-next-line func-names - reader.onload = function () { - const { result } = this; - let img: HTMLImageElement | null = new Image(); - img.onload = () => { - const compressedDataUrl = compress(img, file.type, maxWidth, true); - const url = compress(img, file.type, maxWidth, false); - img = null; - callback({ - data: file, - compressedDataUrl, - url, - type: 'image', - }); - }; - img.src = result as any; - }; - reader.readAsDataURL(file); - } - - function onPaste(file: File) { - return new Promise((resovle, reject) => { - handleFile(file, (data) => { - resovle(data); - }); - }); - } - - const imageMap = {}; + const imagesNodes = useVModel(props, 'filedIds', emit); async function asyncWorker(arg: Task): Promise { if (!props.uploadImage) { return; } - const uploadFileId = await props.uploadImage(arg.file); - const result: any = await onPaste(arg.file); - // 如果上传成功 if (uploadFileId) { - // eslint-disable-next-line no-prototype-builtins - if (!imageMap.hasOwnProperty(uploadFileId)) { - imageMap[uploadFileId] = { - compressedUrl: result.compressedDataUrl, - permanentUrl: '', - fileId: uploadFileId, - }; - } - arg.process(result.compressedDataUrl, imageMap[uploadFileId].permanentUrl, uploadFileId); + const permanentUrl = `${PreviewEditorImageUrl}/${appStore.currentProjectId}/${uploadFileId}/${true}`; + arg.process(permanentUrl, uploadFileId); } } @@ -214,6 +141,7 @@ const showSidebar = useLocalStorage('halo:editor:show-sidebar', true); const attachmentSelectorModal = ref(false); + const selectedimagesNode = ref(); onMounted(() => { const debounceOnUpdate = useDebounceFn(() => { @@ -252,6 +180,32 @@ loading: 'lazy', }, }), + Extension.create({ + addProseMirrorPlugins() { + return [ + new Plugin({ + key: new PluginKey('imageBubbleMenu'), + props: { + decorations: (state) => { + const images: string[] = []; + const { doc } = state; + doc.descendants((node) => { + if (node.type.name === 'image') { + images.push(node.attrs.fileId); + } + }); + imagesNodes.value = images; + if (!selectedimagesNode.value) { + // eslint-disable-next-line prefer-destructuring + selectedimagesNode.value = images[0]; + } + return DecorationSet.empty; + }, + }, + }), + ]; + }, + }), ExtensionTaskList, ExtensionLink.configure({ autolink: false, @@ -419,7 +373,7 @@ uploadQueue.push({ file, // 压缩url 永久url 文件id - process: (compressUrl: string, permalink: string, fileId: string) => { + process: (permalink: string, fileId: string) => { editor.value ?.chain() .focus() @@ -427,9 +381,8 @@ { type: 'image', attrs: { + src: permalink, fileId, - src: compressUrl, - permalinkSrc: permalink, }, }, ]) diff --git a/frontend/src/components/pure/ms-rich-text/extensions/image/ImageView.vue b/frontend/src/components/pure/ms-rich-text/extensions/image/ImageView.vue index 1ad2cdd9aa..5b46c15be2 100644 --- a/frontend/src/components/pure/ms-rich-text/extensions/image/ImageView.vue +++ b/frontend/src/components/pure/ms-rich-text/extensions/image/ImageView.vue @@ -1,7 +1,6 @@ diff --git a/frontend/src/views/case-management/caseManagementFeature/components/export/validateModal.vue b/frontend/src/views/case-management/caseManagementFeature/components/export/validateModal.vue index a2b355fa06..4e9617dadf 100644 --- a/frontend/src/views/case-management/caseManagementFeature/components/export/validateModal.vue +++ b/frontend/src/views/case-management/caseManagementFeature/components/export/validateModal.vue @@ -17,7 +17,7 @@
{{ t('caseManagement.featureCase.verifyingTemplate') }} - +