Multipart uploading spec

本文深入探讨了信息技术领域的多个细分技术领域,包括前端开发、后端开发、移动开发、游戏开发等,提供了丰富的技术知识和实践经验。

Upload File: abc.txt
Content:
abc
(without typing Enter, just only 'abc', but gedit might append a line feed to the end of the file.)

receive:
undefined-----------------------------17582750441148265109875267035\r\nContent-Disposition: form-data; name="kfm_file"; filename="abc.txt"\r\nContent-Type: text/plain\r\n\r\nabc\n\r\n-----------------------------17582750441148265109875267035\r\nContent-Disposition: form-data; name="MAX_FILE_SIZE"\r\n\r\n9999999999\r\n-----------------------------17582750441148265109875267035\r\nContent-Disposition: form-data; name="upload"\r\n\r\nUpload\r\n-----------------------------17582750441148265109875267035\r\nContent-Disposition: form-data; name="upload_cwd"\r\n\r\n/\r\n-----------------------------17582750441148265109875267035--\r\n

At the beginning, "undefined" is two spaces, since this content was cut from whole request string.

<template> <div class="app-container"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px" > <el-form-item label="seasonNo" prop="seasonNo" label-width="140"> <el-input v-model="queryParams.seasonNo" placeholder="please input" clearable @keyup.enter="handleQueryTest" /> </el-form-item> <el-form-item label="colorwayid" prop="colorwayid" label-width="140"> <el-input v-model="queryParams.colorwayid" placeholder="please input" clearable @keyup.enter="handleQueryTest" /> </el-form-item> <el-form-item label="modelNo" prop="modelNo" label-width="140"> <el-input v-model="queryParams.modelNo" placeholder="please input" clearable @keyup.enter="handleQueryTest" /> </el-form-item> <el-form-item> <el-button type="primary" icon="Search" @click="handleQuery" >搜索</el-button > <el-button icon="Refresh" @click="resetQuery">重置</el-button> </el-form-item> <el-form-item> <el-button type="primary" plain @click="handleTest">测试</el-button> </el-form-item> </el-form> <el-table v-loading="loading" :data="qsList"> <el-table-column label="seasonNo" align="center" prop="seasonNo"> </el-table-column> <el-table-column label="stage" align="center" prop="stage"> </el-table-column> <el-table-column label="modelNo" align="center" prop="modelNo"> </el-table-column> <el-table-column label="Colorway Name" align="center" prop="colorwayid"> </el-table-column> <el-table-column label="pattern" align="center" prop="pattern"> </el-table-column> <el-table-column label="firstsource" align="center" prop="firstsource"> </el-table-column> <el-table-column label="Operate" align="center" class-name="small-padding fixed-width" > <template #default="scope"> <el-button link type="primary" icon="Plus" @click="handleCreate(scope.row)" ></el-button> </template> </el-table-column> </el-table> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> </div> <el-dialog title="测试" v-model="testDialog" width="1600px" append-to-body> <div> <el-form :model="queryParams" ref="testRef" :inline="true" label-width="108px" > <el-form-item label="seasonNo" prop="seasonNo" label-width="140"> <el-input v-model="queryParams.seasonNo" placeholder="please input" clearable @keyup.enter="handleQueryTest" /> </el-form-item> <el-form-item label="colorwayid" prop="colorwayid" label-width="140"> <el-input v-model="queryParams.colorwayid" placeholder="please input" clearable @keyup.enter="handleQueryTest" /> </el-form-item> <el-form-item label="modelNo" prop="modelNo" label-width="140"> <el-input v-model="queryParams.modelNo" placeholder="please input" clearable @keyup.enter="handleQueryTest" /> </el-form-item> <el-form-item> <el-button type="primary" icon="Search" @click="handleQueryTest" >Search</el-button > <el-button icon="Refresh" @click="resetQueryTest">Reset</el-button> </el-form-item> </el-form> <el-table v-loading="loading" :data="ptrList"> <el-table-column label="seasonNo" align="center" prop="seasonNo"> </el-table-column> <el-table-column label="stage" align="center" prop="stage"> </el-table-column> <el-table-column label="modelNo" align="center" prop="modelNo"> </el-table-column> <el-table-column label="Colorway Name" align="center" prop="colorwayid"> </el-table-column> <el-table-column label="pattern" align="center" prop="pattern"> </el-table-column> <el-table-column label="firstsource" align="center" prop="firstsource"> </el-table-column> <el-table-column label="Operate" align="center" class-name="small-padding fixed-width" > <template #default="scope"> <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" ></el-button> </template> </el-table-column> </el-table> <pagination v-show="totalTest > 0" :total="totalTest" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getTestList" /> </div> </el-dialog> <el-dialog title="创建" v-model="createDialog" width="1800px" append-to-body> <div> <el-tabs v-model="activeTab" type="card"> <el-tab-pane v-for="(item, index) in mainList" :key="index" :label="getTabLabel(item)" :name="index.toString()" > <!-- 每个标签页使用独立的表单和数据结构 --> <div v-if="currentTabData[index]"> <!-- 基本信息区域(只读) --> <el-card header="基本信息" style="margin-bottom: 20px"> <el-form :model="currentTabData[index].baseInfo" label-width="120px" :inline="true" > <el-form-item label="Dev Style Name"> <el-input v-model="currentTabData[index].baseInfo.modelNo" readonly style="width: 100px" class="readonly-input" /> <el-input v-model="currentTabData[index].baseInfo.modelName" readonly style="width: 150px" class="readonly-input" /> </el-form-item> <el-form-item label="Color Way Name" label-width="125"> <el-input v-model="currentTabData[index].baseInfo.colorwayid" readonly style="width: 100px" class="readonly-input" /> <el-input v-model="currentTabData[index].baseInfo.colorwayname" readonly style="width: 400px" class="readonly-input" /> <el-input v-model="currentTabData[index].baseInfo.bomid" readonly style="width: 100px" class="readonly-input" /> </el-form-item> <el-form-item label="Stage" label-width="60"> <el-input v-model="currentTabData[index].baseInfo.stage" readonly class="readonly-input" style="width: 120px" /> </el-form-item> <el-form-item label="DPA/STYLE" prop="prodCd" label-width="100"> <el-input v-model="currentTabData[index].baseInfo.prodCd" readonly class="readonly-input" /> </el-form-item> <el-form-item label="Season"> <el-input v-model="currentTabData[index].baseInfo.seasonNo" readonly class="readonly-input" style="width: 100px" /> </el-form-item> <el-form-item label="Version" prop="pfcVersion" label-width="80" > <el-input v-model="currentTabData[index].baseInfo.pfcVersion" readonly style="width: 120px" class="readonly-input" /> </el-form-item> <el-form-item label="Tool Code" prop="pattern" label-width="100" > <el-input v-model="currentTabData[index].baseInfo.pattern" readonly class="readonly-input" /> </el-form-item> <el-form-item label="PFC手册号" prop="pfcNo" label-width="100"> <el-input v-model="currentTabData[index].baseInfo.pfcNo" readonly style="width: 100px" class="readonly-input" /> </el-form-item> <el-form-item label="Page Number"> <el-input v-model="currentTabData[index].baseInfo.pageNumber" readonly class="readonly-input" style="width: 80px" /> <el-input v-model="currentTabData[index].baseInfo.stepno" readonly class="readonly-input" style="width: 80px" /> </el-form-item> <el-form-item label="鞋码类型" prop="sizeType" label-width="100" > <el-input v-model="currentTabData[index].baseInfo.sizeType" readonly class="readonly-input" /> </el-form-item> </el-form> </el-card> <!-- 图片上传区域 --> <el-card header="图片上传" style="margin-bottom: 20px"> </el-card> <!-- 可编辑的表单区域 --> <el-card header="编辑信息"> <!-- <el-form :model="currentTabData[index].formData" :rules="getTabRules(index)" label-width="120px" > <el-row> <el-col :span="8"> <el-form-item label="STYLE NO" prop="styleNo"> <el-input v-model="currentTabData[index].formData.styleNo" placeholder="Please input" /> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="DPA" prop="dpa"> <el-input v-model="currentTabData[index].formData.dpa" placeholder="Please input" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="TD CODE" prop="tdCode"> <el-input v-model="currentTabData[index].formData.tdCode" placeholder="Please input" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="FACTORY" prop="factory"> <el-input v-model="currentTabData[index].formData.factory" placeholder="Please input" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="8"> <el-form-item label="LAB TEST" prop="labTest"> <el-input v-model="currentTabData[index].formData.labTest" placeholder="Please input" /> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="APPROVED BY" prop="approvedBy" label-width="130" > <el-input v-model="currentTabData[index].formData.approvedBy" placeholder="Please input" /> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="APPROVED DATE" prop="approvedDate" label-width="180" > <el-date-picker v-model="currentTabData[index].formData.approvedDate" type="date" placeholder="选择日期" style="width: 50%" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="16"> <el-form-item label="具体描述" prop="description"> <el-input v-model="currentTabData[index].formData.description" placeholder="Please input" type="textarea" :rows="2" /> </el-form-item> </el-col> </el-row> </el-form> --> <!-- 动态数据行区域(根据标签页类型显示不同的字段) --> <div style="margin-top: 20px"> <!-- 将模板选择器和列表标题放在同一行 --> <div style=" display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; " > <!-- 左侧:模板选择器 --> <div style="display: flex; align-items: center; flex: 1"> <span style=" font-weight: bold; margin-right: 10px; min-width: 100px; " >测试用例列表</span > <el-cascader v-model="currentTabData[index].selectedTemplate" :options="currentTabData[index].templateOptions" :props="getCascaderProps(index)" placeholder="请选择测试用例模板" clearable filterable style="width: 350px; margin-right: 10px" @change="handleTemplateSelect(index)" /> </div> <!-- 右侧:按钮和标题 --> <!-- <div style="display: flex; align-items: center;"> <el-button type="primary" size="small" @click="addDataRow(index)" >添加空白用例</el-button> </div> --> </div> <el-table :data="currentTabData[index].dataRows" border v-loading="currentTabData[index].loading" > <!-- 根据页面类型动态显示列 --> <el-table-column v-for="column in getTableColumns(index)" :key="column.prop" :label="column.label" :prop="column.prop" :width="column.width" > <template #default="scope"> <el-input v-model="scope.row[column.prop]" size="small" /> </template> </el-table-column> <el-table-column label="Operate" width="80"> <template #default="scope"> <el-button link type="danger" size="small" @click="removeDataRow(index, scope.$index)" >delete</el-button > </template> </el-table-column> </el-table> </div> </el-card> </div> </el-tab-pane> </el-tabs> <!-- 底部操作按钮 --> <div style="text-align: center; margin-top: 20px"> <!-- <el-button type="primary" @click="handleSaveAll">保存所有</el-button> --> <el-button type="success" @click="handleSaveCurrent" >保存当前</el-button > <el-button @click="createDialog = false">取消</el-button> </div> </div> </el-dialog> </template> <script setup name="User"> import { ptrInfo, addQualityStand, qsInfo, getQsMain, getTemplateTree, saveQualityStandards, getSavedQualityStandards, uploadImages, getImages, deleteImage, } from "@/api/quality/standard/quasd.js"; import { getSrc } from "@/utils/interceptSrc"; const { proxy } = getCurrentInstance(); const qsList = ref([]); const ptrList = ref([]); const mainList = ref([]); const loading = ref(true); const showSearch = ref(true); const testDialog = ref(false); const createDialog = ref(false); const total = ref(0); const totalTest = ref(0); const activeTab = ref("0"); // 为每个标签页存储独立的数据 const currentTabData = ref({}); const data = reactive({ queryParams: { pageNum: 1, pageSize: 10, stage: null, modelNo: null, colorwayid: null, seasonNo: null, pfcNo: null, pattern: null, firstsource: null, pageNumber: null, stepno: null, pfcCaption: null, }, rules: { styleNo: [{ required: true, message: "STYLE NO不能为空", trigger: "blur" }], dpa: [{ required: true, message: "DPA不能为空", trigger: "blur" }], tdCode: [{ required: true, message: "TD CODE不能为空", trigger: "blur" }], factory: [{ required: true, message: "FACTORY不能为空", trigger: "blur" }], labTest: [{ required: true, message: "LAB TEST不能为空", trigger: "blur" }], approvedBy: [ { required: true, message: "APPROVED BY不能为空", trigger: "blur" }, ], approvedDate: [ { required: true, message: "APPROVED DATE不能为空", trigger: "blur" }, ], // 其他验证规则... }, }); const { queryParams, rules } = toRefs(data); // 初始化标签页数据 async function initTabData(tabIndex, sourceData) { currentTabData.value[tabIndex] = { baseInfo: { ...sourceData }, formData: { styleNo: "", dpa: "", tdCode: "", factory: "", labTest: "", approvedBy: "", approvedDate: "", description: "", }, dataRows: [], templateOptions: [], selectedTemplate: [], currentTemplateData: null, loading: false, // 添加加载状态 }; // 加载模板数据 await loadTemplateOptions(tabIndex, sourceData.pageNumber); // 加载已保存的测试用例数据 await loadSavedDataRows(tabIndex, sourceData.pfcNo, sourceData.pageNumber); } // 加载已保存的测试用例数据 async function loadSavedDataRows(tabIndex, pfcNo, pageNumber) { const currentTab = currentTabData.value[tabIndex]; try { currentTab.loading = true; const response = await getSavedQualityStandards(pfcNo, pageNumber); const savedData = response.data || []; if (savedData.length > 0) { const currentTab = currentTabData.value[tabIndex]; // 转换已保存数据为表格行格式 currentTab.dataRows = savedData.map((item) => { const row = { templateId: item.templateId, // 保存模板ID // 复制表单数据到当前标签页的表单中(只复制一次,取第一条数据) ...(currentTab.dataRows.length === 0 ? { // 这些字段会被表单数据覆盖,所以这里主要处理测试用例特定字段 } : {}), }; // 根据页面类型设置不同的字段 if (pageNumber == "P42" || pageNumber == "P124") { // P42/P124字段 row.bondTest = item.bondTest || ""; row.testItem = item.testItem || ""; row.pictureRef = item.pictureRef || ""; row.materialOne = item.materialOne || ""; row.materialTwo = item.materialTwo || ""; row.speed = item.speed || ""; row.minSpec = item.minSpec || ""; row.peSpec = item.peSpec || ""; } else if (pageNumber == "P43" || pageNumber == "P125") { // P43/P125字段 row.testMethod = item.testMethod || ""; row.testMethodName = item.testMethodName || ""; row.pictureRef = item.pictureRef || ""; row.flmc = item.flmc || ""; row.minSpec = item.minSpec || ""; if (pageNumber == "P43") { row.testParameter = item.testParameter || ""; } else if (pageNumber == "P125") { row.speed = item.speed || ""; } } // 设置可选字段 row.csan = item.csan || ""; row.pead = item.pead || ""; row.peSpec = item.peSpec || ""; return row; }); // 设置表单数据(取第一条数据的表单信息) if (savedData.length > 0) { const firstItem = savedData[0]; currentTab.formData = { styleNo: firstItem.styleNo || "", dpa: firstItem.dpa || "", tdCode: firstItem.tdCode || "", factory: firstItem.factory || "", labTest: firstItem.labTest || "", approvedBy: firstItem.approvedBy || "", approvedDate: firstItem.approvedDate || "", description: firstItem.description || "", }; } console.log(`标签页 ${tabIndex} 加载了 ${savedData.length} 条已保存数据`); } } catch (error) { console.error("加载已保存数据失败:", error); } finally { currentTab.loading = false; } } // 加载模板选项 async function loadTemplateOptions(tabIndex, pageNumber) { try { const response = await getTemplateTree(pageNumber); currentTabData.value[tabIndex].templateOptions = response.data || []; } catch (error) { console.error("加载模板数据失败:", error); proxy.$modal.msgError("加载测试用例模板失败"); } } // 获取级联选择器配置 function getCascaderProps(tabIndex) { const pageNumber = mainList.value[tabIndex]?.pageNumber; // 所有页面使用相同的配置 return { value: "value", label: "label", children: "children", checkStrictly: false, emitPath: true, expandTrigger: "hover", }; } // 处理模板选择 function handleTemplateSelect(tabIndex) { const currentTab = currentTabData.value[tabIndex]; const selectedValue = currentTab.selectedTemplate; const pageNumber = mainList.value[tabIndex]?.pageNumber; if (selectedValue && selectedValue.length > 0) { // 找到选中的模板数据 const templateData = findTemplateData( currentTab.templateOptions, selectedValue ); if (templateData && templateData.data) { currentTab.currentTemplateData = templateData.data; // 自动添加数据行 addDataRowFromTemplate(tabIndex); } else { // 如果不是叶子节点,不清空选择,让用户继续选择下级 if ( templateData && templateData.children && templateData.children.length > 0 ) { currentTab.currentTemplateData = null; } else { // 没有数据也没有子节点,清空选择 currentTab.selectedTemplate = []; currentTab.currentTemplateData = null; proxy.$modal.msgWarning("请选择有效的测试用例模板"); } } } else { currentTab.currentTemplateData = null; } } // 从模板添加数据行 function addDataRowFromTemplate(tabIndex) { const currentTab = currentTabData.value[tabIndex]; if (currentTab.currentTemplateData) { const columns = getTableColumns(tabIndex); const newRow = {}; // 初始化所有字段为空 columns.forEach((col) => { newRow[col.prop] = ""; }); // 用模板数据填充 fillRowWithTemplateData(newRow, currentTab.currentTemplateData, tabIndex); currentTab.dataRows.push(newRow); // 清空选择器 currentTab.selectedTemplate = []; currentTab.currentTemplateData = null; proxy.$modal.msgSuccess("已从模板添加测试用例"); } } // 在模板树中查找数据 function findTemplateData(options, path) { if (!options || !path || path.length == 0) return null; let currentLevel = options; let result = null; for (let i = 0; i < path.length; i++) { const currentValue = path[i]; const found = currentLevel.find((item) => item.value == currentValue); if (!found) return null; if (i == path.length - 1) { result = found; } else { currentLevel = found.children; } } return result; } // 修改原有的添加数据行方法 function addDataRow(tabIndex) { const currentTab = currentTabData.value[tabIndex]; const columns = getTableColumns(tabIndex); // 创建新行 const newRow = {}; columns.forEach((col) => { newRow[col.prop] = ""; }); currentTab.dataRows.push(newRow); proxy.$modal.msgSuccess("已添加空白的测试用例行"); } // 用模板数据填充行 function fillRowWithTemplateData(row, templateData, tabIndex) { const pageNumber = mainList.value[tabIndex]?.pageNumber; // 保存模板ID,用于后续保存操作 row.templateId = templateData.id; if (pageNumber == "P42" || pageNumber == "P124") { // P42/P124模板数据映射 const bondingData = templateData; // 映射所有字段 row.bondTest = bondingData.bondTest || ""; row.testItem = bondingData.testItem || ""; row.pictureRef = bondingData.pictureRef || ""; row.materialOne = bondingData.materialOne || ""; row.materialTwo = bondingData.materialTwo || ""; row.speed = bondingData.speed || ""; row.minSpec = bondingData.minSpec || ""; row.peSpec = bondingData.peSpec || ""; row.csan = bondingData.csan || ""; row.pead = bondingData.pead || ""; } else if (pageNumber == "P43") { // P43模板数据映射 const customData = templateData; row.testMethod = customData.testMethod || ""; row.testMethodName = customData.testMethodName || ""; row.pictureRef = customData.pictureRef || ""; row.flmc = customData.flmc || ""; row.testParameter = customData.testParameter || ""; // P43特有 row.minSpec = customData.minSpec || ""; row.peSpec = customData.peSpec || ""; row.csan = customData.csan || ""; row.pead = customData.pead || ""; } else if (pageNumber == "P125") { // P125模板数据映射 const customData = templateData; row.testMethod = customData.testMethod || ""; row.testMethodName = customData.testMethodName || ""; row.pictureRef = customData.pictureRef || ""; row.flmc = customData.flmc || ""; row.speed = customData.speed || ""; // P125特有 row.minSpec = customData.minSpec || ""; row.peSpec = customData.peSpec || ""; row.csan = customData.csan || ""; row.pead = customData.pead || ""; } } /** 查询【请填写功能名称】列表 */ function getList() { loading.value = true; qsInfo(queryParams.value).then((response) => { qsList.value = response.rows; total.value = response.total; loading.value = false; }); } function handleTest() { testDialog.value = true; getTestList(); } // 根据页面类型获取表格列配置 function getTableColumns(tabIndex) { const pageNumber = mainList.value[tabIndex]?.pageNumber; // 根据不同的pageNumber返回不同的列配置 switch (pageNumber) { case "P42": // Finished Shoe Bonding页面 return [ { prop: "bondTest", label: "BOND TEST", width: 120 }, { prop: "testItem", label: "TEST ITEM", width: 100 }, { prop: "pictureRef", label: "PICTURE REF.#", width: 130 }, { prop: "materialOne", label: "MATERIAL 1", width: 120 }, { prop: "materialTwo", label: "MATERIAL 2", width: 120 }, { prop: "speed", label: "Speed(mm/min)", width: 150 }, { prop: "minSpec", label: "Min.Spec(kgf/cm)", width: 150 }, { prop: "peSpec", label: "Product Exc.Spec", width: 150 }, { prop: "csan", label: "COMMENT SECTION Approver Name / Product Exception Detail...", width: 250, }, { prop: "pead", label: "Product Exception Approved Date", width: 150 }, ]; case "P43": // Finished Shoe Custom页面 return [ { prop: "testMethod", label: "Nike Test Method #", width: 120 }, { prop: "testMethodName", label: "Nike Test Method Name", width: 130 }, { prop: "pictureRef", label: "PICTURE REF.#", width: 100 }, { prop: "flmc", label: "FOCUSED LOCATION/MATERIAL/COLOR", width: 220 }, { prop: "testParameter", label: "Test Parameter", width: 120 }, // P43特有 { prop: "minSpec", label: "Min.Spec", width: 120 }, { prop: "peSpec", label: "Product Exc.Spec", width: 120 }, { prop: "csan", label: "COMMENT SECTION Approver Name / Product Exception Detail...", width: 250, }, { prop: "pead", label: "Product Exception Approved Date", width: 150 }, ]; case "P124": //Fuse & No Sew Upper return [ { prop: "bondTest", label: "BOND TEST", width: 120 }, { prop: "testMethod", label: "Nike Test Method #", width: 120 }, { prop: "testItem", label: "TEST ITEM", width: 100 }, { prop: "pictureRef", label: "PICTURE REF.#", width: 100 }, { prop: "materialOne", label: "MATERIAL 1", width: 120 }, { prop: "materialTwo", label: "MATERIAL 2", width: 120 }, { prop: "speed", label: "Speed(mm/min)", width: 150 }, { prop: "minSpec", label: "Min.Spec(kgf/cm)", width: 150 }, { prop: "peSpec", label: "Product Exc.Spec", width: 150 }, { prop: "csan", label: "COMMENT SECTION Approver Name / Product Exception Detail...", width: 250, }, { prop: "pead", label: "Product Exception Approved Date", width: 150 }, ]; case "P125": // Sockliner页面 return [ { prop: "testMethod", label: "Nike Test Method #", width: 120 }, { prop: "testMethodName", label: "Nike Test Method Name", width: 130 }, { prop: "pictureRef", label: "PICTURE REF.#", width: 100 }, { prop: "flmc", label: "FOCUSED LOCATION/MATERIAL/COLOR", width: 220 }, { prop: "speed", label: "Speed(mm/min)", width: 150 }, // P125特有 { prop: "minSpec", label: "Min.Spec(kgf/cm)", width: 150 }, { prop: "peSpec", label: "Product Exc.Spec", width: 150 }, { prop: "csan", label: "COMMENT SECTION Approver Name / Product Exception Detail...", width: 250, }, { prop: "pead", label: "Product Exception Approved Date", width: 150 }, ]; default: return []; } } // 获取标签页显示名称 function getTabLabel(item) { return `${item.pfcCaption} (${item.pageNumber})`; } // 删除数据行 function removeDataRow(tabIndex, rowIndex) { currentTabData.value[tabIndex].dataRows.splice(rowIndex, 1); } // 获取对应标签页的验证规则 function getTabRules(tabIndex) { // 可以根据不同的标签页返回不同的验证规则 return data.rules; } async function handleCreate(row) { createDialog.value = true; try { const response = await getQsMain(row.pfcNo); mainList.value = response.data; // 为每个标签页初始化独立的数据 for (let index = 0; index < mainList.value.length; index++) { const item = mainList.value[index]; await initTabData(index, item); } // 设置默认激活第一个标签页 if (mainList.value.length > 0) { activeTab.value = "0"; } } catch (error) { console.error("加载数据失败:", error); proxy.$modal.msgError("加载数据失败"); } } // 保存当前标签页 async function handleSaveCurrent() { const currentIndex = parseInt(activeTab.value); const currentData = currentTabData.value[currentIndex]; const baseInfo = currentData.baseInfo; try { // 构建保存数据 const saveDataList = []; // 遍历当前标签页的所有测试用例 for (const dataRow of currentData.dataRows) { const saveData = { pageNumber: baseInfo.pageNumber, // 当前标签页的页码 pfcNo: baseInfo.pfcNo, // PFC手册号 templateId: dataRow.templateId, // 模板树第七层data的id // 其他表单数据 // styleNo: currentData.formData.styleNo, // dpa: currentData.formData.dpa, // tdCode: currentData.formData.tdCode, // factory: currentData.formData.factory, // labTest: currentData.formData.labTest, // approvedBy: currentData.formData.approvedBy, // approvedDate: currentData.formData.approvedDate, // description: currentData.formData.description }; // 添加测试用例数据 // const pageNumber = baseInfo.pageNumber; // if (pageNumber == 'P42' || pageNumber == 'P124') { // // P42/P124字段 // saveData.testItem = dataRow.testItem || ''; // saveData.pictureRef = dataRow.pictureRef || ''; // saveData.materialOne = dataRow.materialOne || ''; // saveData.materialTwo = dataRow.materialTwo || ''; // saveData.speed = dataRow.speed || ''; // saveData.minSpec = dataRow.minSpec || ''; // saveData.bondTest = dataRow.bondTest || ''; // } else if (pageNumber == 'P43' || pageNumber == 'P125') { // // P43/P125字段 // saveData.testMethod = dataRow.testMethod || ''; // saveData.testMethodName = dataRow.testMethodName || ''; // saveData.pictureRef = dataRow.pictureRef || ''; // saveData.flmc = dataRow.flmc || ''; // saveData.minSpec = dataRow.minSpec || ''; // if (pageNumber == 'P43') { // saveData.testParameter = dataRow.testParameter || ''; // } else if (pageNumber == 'P125') { // saveData.speed = dataRow.speed || ''; // } // } // 如果csan和pead不为空,也添加到保存数据中 if (dataRow.csan && dataRow.csan.trim() !== "") { saveData.csan = dataRow.csan; } if (dataRow.pead && dataRow.pead.trim() !== "") { saveData.pead = dataRow.pead; } saveDataList.push(saveData); } console.log("保存数据:", saveDataList); // 调用API保存数据 const response = await saveQualityStandards(saveDataList); proxy.$modal.msgSuccess( `保存 ${mainList.value[currentIndex].pfcCaption} 成功,共保存 ${saveDataList.length} 个测试用例` ); // 保存成功后重新加载数据,确保数据一致性 await loadSavedDataRows(currentIndex, baseInfo.pfcNo, baseInfo.pageNumber); } catch (error) { console.error("保存失败:", error); proxy.$modal.msgError("保存失败: " + (error.message || "未知错误")); } } // 监听弹窗关闭 watch(createDialog, (newVal) => { if (!newVal) { // 关闭弹窗时清理数据 currentTabData.value = {}; mainList.value = []; activeTab.value = "0"; } }); // 保存所有标签页 function handleSaveAll() { console.log("保存所有标签页数据:", currentTabData.value); // 遍历所有标签页数据并保存 Object.keys(currentTabData.value).forEach((index) => { const tabData = currentTabData.value[index]; console.log(`标签页 ${index} 数据:`, tabData); // 调用API保存每个标签页的数据 }); proxy.$modal.msgSuccess("所有数据保存成功"); createDialog.value = false; } function getTestList() { loading.value = true; ptrInfo(queryParams.value).then((response) => { ptrList.value = response.rows; totalTest.value = response.total; loading.value = false; }); } function handleAdd(row) { addQualityStand(row).then((response) => { proxy.$modal.msgSuccess("新增成功"); getList(); }); } /** 搜索按钮操作 */ function handleQuery() { queryParams.value.pageNum = 1; getList(); } function handleQueryTest() { queryParams.value.pageNum = 1; getTestList(); } /** 重置按钮操作 */ function resetQuery() { proxy.resetForm("queryRef"); handleQuery(); } function resetQueryTest() { proxy.resetForm("testRef"); handleQuery(); } getList(); </script> <style scoped> /* 为级联选择器添加样式,确保显示完整 */ :deep(.el-cascader) { width: 450px; /* 增加宽度以显示更长的路径 */ margin-right: 10px; } :deep(.el-cascader__dropdown) { max-height: 400px; /* 增加下拉框高度 */ } .readonly-input :deep(.el-input__inner) { background-color: #fff7e6; } /* 为级联选择器的标签添加更好的显示 */ :deep(.el-cascader-node__label) { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 300px; } /* 模板选择区域样式 */ .template-selector { display: flex; align-items: center; margin-bottom: 15px; padding: 10px; background-color: #f8f9fa; border-radius: 4px; } .template-selector-label { font-weight: bold; margin-right: 10px; min-width: 120px; } .template-selector-hint { margin-left: 10px; color: #666; font-size: 12px; } /* 图片上传样式 */ :deep(.el-upload-list--picture-card .el-upload-list__item) { width: 100px; height: 100px; } :deep(.el-upload--picture-card) { width: 100px; height: 100px; line-height: 100px; } :deep(.el-upload-list--picture-card .el-upload-list__item-thumbnail) { object-fit: contain; } .upload-tip { margin-top: 10px; color: #909399; font-size: 12px; } </style> 这是我目前的页面,需要在图片上传区域加一个照片墙用于上传图片和回显,我希望可以参考刚刚我提供的案例那样去实现,后端的接口我已经写好了@PostMapping("/images/upload") public AjaxResult uploadImages(@RequestParam("files") MultipartFile[] files, @RequestParam String pfcNo, @RequestParam String pageNumber) { try { if (files == null || files.length == 0) { return AjaxResult.error("上传文件不能为空"); } int successCount = qualityStandardService.uploadImages(files, pfcNo, pageNumber); return AjaxResult.success("上传成功,共上传 " + successCount + " 张图片"); } catch (Exception e) { e.printStackTrace(); return AjaxResult.error("上传失败: " + e.getMessage()); } }@Override public Integer uploadImages(MultipartFile[] files, String pfcNo, String pageNumber) { if (files == null || files.length == 0) { throw new ServiceException("上传文件不能为空"); } int successCount = 0; String username = SecurityUtils.getUsername(); Date now = MyDateUtils.getNowDate(); for (MultipartFile file : files) { try { // 跳过空文件 if (file.isEmpty()) { continue; } QualityShoeDesign qualityShoeDesign = new QualityShoeDesign(); // 执行上传 R<ToolsFile> fileResult = remoteFileService.upload(file, MinioBucketNameConstants.SHOEDESIGN, SecurityUtils.getFact(), file.getOriginalFilename(), FileEnum.LOCAL, SecurityConstants.INNER); if (MyStringUtils.isNull(fileResult) || MyStringUtils.isNull(fileResult.getData())) { throw new ServiceException("文件服务器异常"); } qualityShoeDesign.setPfcNo(pfcNo); qualityShoeDesign.setPageNumber(pageNumber); qualityShoeDesign.setFilePath(fileResult.getData().getUrl()); qualityShoeDesign.setOriginalName(file.getOriginalFilename()); qualityShoeDesign.setCreateBy(username); qualityShoeDesign.setCreateTime(now); // 插入记录 int result = qualityStandardMapper.insertQualityShoeDesign(qualityShoeDesign); if (result > 0) { successCount++; } } catch (Exception e) { // 记录错误日志,但继续处理其他文件 log.error("上传文件失败: {}", file.getOriginalFilename(), e); } } if (successCount == 0) { throw new ServiceException("所有文件上传失败"); } return successCount; }@GetMapping("/images/list") public AjaxResult getImages(@RequestParam String pfcNo, @RequestParam String pageNumber) { try { List<QualityShoeDesign> images = qualityStandardService.getImagesByPfcNoAndPage(pfcNo, pageNumber); return AjaxResult.success(images); } catch (Exception e) { return AjaxResult.error("获取图片列表失败: " + e.getMessage()); } }这些是上传代码和获取图片代码,请帮我生成合适的前端vue代码,图片上传我要可多选的
10-15
基于MinIO对象存储构建医疗影像云平台,支持DICOM文件上传(SpringBoot切片上传)。采用国密SM4算法加密敏感数据,Redis管理访问令牌。Vue实现影像三维重建(Cornerstone.js),医生可创建加密分享链接(有效期控制),提升跨院协作效率。 1. 医疗影像存储模块​​ ​​功能说明​​ DICOM文件标准化云存储服务 支持标准DICOM格式上传/下载 分片上传技术(大文件断点续传) 存储空间配额管理(用户/科室维度) 影像缩略图自动生成 ​​2. 安全加密模块​​ ​​功能说明​​ 医疗数据全生命周期加密保护 国密SM4算法加密敏感数据 基于角色的访问密钥分配机制 加密密钥独立存储(与影像分离) 传输层HTTPS加密保障 ​​3. 影像处理模块​​ ​​功能说明​​ 基础医学影像处理能力 DICOM文件解析与元数据提取 窗宽窗位调节等基础操作 三维重建可视化(冠状/矢状/轴状切面) 影像测量工具(长度/角度计算) ​​4. 分享协作模块​​ ​​功能说明​​ 加密分享与访问控制体系 创建分享链接(口令访问/免密访问) 链接有效期控制(24小时/7天/30天) 访问次数限额(防数据扩散) 分享记录追踪(时间、访问者信息) ​​5. 权限管理模块​​ ​​功能说明​​ 多层级医疗数据权限控制 医生(查看+注释)、技师(上传+标注)、患者(查看) 临时权限授予(会诊场景) 操作审计日志(影像查看/下载行为) ​​6. 前端可视化模块​​ ​​功能说明​​ 医学影像交互式工作台 DICOM序列动态切换浏览 影像基础标记与注释工具 分享链接生成向导 个人文件库分类管理 ​​7. 系统管理后台​​ ​​功能说明​​ 平台运营支撑功能 用户管理(医院/科室两级结构) 存储空间监控与告警 操作日志查询 分享策略配置
09-23
【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)内容概要:本文研究了一种基于机器学习(ML)和离散小波变换(DWT)的电能质量扰动分类方法,并提供了Matlab实现方案。首先利用DWT对电能质量信号进行多尺度分解,提取信号的时频域特征,有效捕捉电压暂降、暂升、中断、谐波、闪变等常见扰动的关键信息;随后结合机器学习分类器(如SVM、BP神经网络等)对提取的特征进行训练与分类,实现对不同类型扰动的自动识别与准确区分。该方法充分发挥DWT在信号去噪与特征提取方面的优势,结合ML强大的模式识别能力,提升了分类精度与鲁棒性,具有较强的实用价值。; 适合人群:电气工程、自动化、电力系统及其自动化等相关专业的研究生、科研人员及从事电能质量监测与分析的工程技术人员;具备一定的信号处理基础和Matlab编程能力者更佳。; 使用场景及目标:①应用于智能电网中的电能质量在线监测系统,实现扰动类型的自动识别;②作为高校或科研机构在信号处理、模式识别、电力系统分析等课程的教学案例或科研实验平台;③目标是提高电能质量扰动分类的准确性与效率,为后续的电能治理与设备保护提供决策依据。; 阅读建议:建议读者结合Matlab代码深入理解DWT的实现过程与特征提取步骤,重点关注小波基选择、分解层数设定及特征向量构造对分类性能的影响,并尝试对比不同机器学习模型的分类效果,以全面掌握该方法的核心技术要点。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值