| .env.development | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/product/attachment.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/product/manage.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/product/wire.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/production/wire/attachment/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/production/wire/report/rawMaterial.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/production/wire/report/reportManage.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/production/wire/report/wire.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/production/wire/selfInspect/form.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/production/wire/selfInspect/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.env.development
@@ -14,6 +14,6 @@ # API 服务器的 URL #VITE_APP_API_URL = 'http://114.132.189.42:7002/mes' VITE_APP_API_URL = 'http://114.132.10.119:7002/mes' VITE_APP_API_URL = 'http://192.168.10.119:7002/mes' # VITE_APP_API_URL = 'http://192.168.100.131:7002/mes' # src/api/product/attachment.ts
@@ -13,6 +13,7 @@ // 上传单个附件文件 uploadSingleFile(filePath: string) { return new Promise<BaseResult<any>>((resolve, reject) => { console.log("开始上传文件:", filePath); uni.uploadFile({ url: `${baseApi}/app/addAttachmentFiles`, filePath: filePath, @@ -21,35 +22,44 @@ Authorization: getToken() ? `Bearer ${getToken()}` : "", }, success: (uploadRes) => { console.log("上传响应:", uploadRes); try { const result = JSON.parse(uploadRes.data) as BaseResult<any>; console.log("解析后的结果:", result); // 业务状态码 00000 表示成功 if (result.code === ResultCodeEnum.SUCCESS) { resolve(result); } else { // 其他业务处理失败 const errorMsg = result.msg || "文件上传失败"; console.error("上传失败:", errorMsg, result); uni.showToast({ title: result.msg || "文件上传失败", title: errorMsg, icon: "none", }); reject({ message: result.msg || "业务处理失败", message: errorMsg, code: result.code, }); } } catch (e) { reject(e); console.error("解析上传响应失败:", e, uploadRes.data); reject({ message: "解析上传响应失败", error: e, }); } }, fail: (error) => { console.log("upload fail error", error); console.error("上传请求失败:", error); const errorMsg = error.errMsg || "文件上传请求失败"; uni.showToast({ title: "文件上传请求失败", title: errorMsg, icon: "none", duration: 2000, }); reject({ message: "文件上传请求失败", message: errorMsg, error, }); }, src/api/product/manage.ts
@@ -29,11 +29,37 @@ }, // 查询自检信息 getSelfInspection(params: any) { return request<BaseResult<any>>({ url: "/app/getSelfInspection", method: "GET", data: params, getSelfInspection(data: any) { return request<BaseResult<any[]>>({ url: "/wireInspection/getDrawInspectInfoListByGetDrawInspect", method: "POST", data: data, }); }, // 新增自检 addSelfInspection(data: any) { return request<BaseResult<any[]>>({ url: "/wireInspection/saveDrawInspectionRecord", method: "POST", data: data, }); }, // 删除自检 deleteSelfInspection(ids: string | number) { return request<BaseResult<any[]>>({ url: `/wireInspection/deleteDrawWireInspectionRecord/${ids}`, method: "POST", }); }, // 获取自检样式数据 getDrawInspectStyleByGetDrawInspect(data: any) { return request<BaseResult<any[]>>({ url: "/wireInspection/getDrawInspectStyleByGetDrawInspect", method: "POST", data: data, }); }, src/api/product/wire.ts
@@ -32,7 +32,7 @@ // 新增杆包 addRodBagTree(data: any) { return request<BaseResult<any>>({ url: "/app/addRodBagTree", url: "/wireOutput/addRodBagTree", method: "POST", data: data, }); @@ -41,8 +41,8 @@ // 删除杆包 deleteRodBagTree(data: any) { return request<BaseResult<any>>({ url: "/app/deleteRodBagTree", method: "DELETE", url: "/wireOutput/deleteRodBagTree", method: "POST", data: data, }); }, src/pages.json
@@ -196,7 +196,7 @@ { "path": "pages/production/wire/selfInspect/index", "style": { "navigationBarTitleText": "拉丝自检" "navigationBarTitleText": "自检管理" } }, { src/pages/production/wire/attachment/index.vue
@@ -36,23 +36,6 @@ </view> </template> <!-- 视频预览 --> <template v-else-if="isVideoType(item.url)"> <video v-if="!item.loadError" :src="getFullUrl(item.url)" class="media-preview" :controls="false" :show-center-play-btn="false" @error="onVideoError(item)" /> <!-- 视频加载失败显示默认图标 --> <view v-else class="file-icon-wrapper"> <wd-icon name="video" size="48px" color="#ccc" /> <text class="file-name error-text">加载失败</text> </view> </template> <!-- 其他文件类型显示图标 --> <view v-else class="file-icon-wrapper"> <wd-icon name="file-outline" size="48px" color="#999" /> @@ -91,7 +74,7 @@ const detailData = ref<any>({}); // 获取完整的图片/视频 URL // 获取完整的图片 URL const getFullUrl = (url: string) => { if (!url) return ""; // 如果已经是完整的 URL(http 或 https 开头),直接返回 @@ -118,12 +101,6 @@ return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension); }; // 判断是否为视频类型 const isVideoType = (urlOrFileName: string) => { const extension = getExtension(urlOrFileName); return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension); }; // 图片加载成功 const onImageLoad = (item: any) => { item.loadError = false; @@ -132,12 +109,6 @@ // 图片加载失败 const onImageError = (item: any) => { console.error("图片加载失败:", item.url); item.loadError = true; }; // 视频加载失败 const onVideoError = (item: any) => { console.error("视频加载失败:", item.url); item.loadError = true; }; @@ -178,20 +149,14 @@ const addAttachment = () => { // 显示选择文件类型的弹窗 uni.showActionSheet({ itemList: ["选择图片", "选择视频", "拍照", "录像"], itemList: ["选择图片", "拍照"], success: (res) => { switch (res.tapIndex) { case 0: // 选择图片 chooseImages(); break; case 1: // 选择视频 chooseVideos(); break; case 2: // 拍照 case 1: // 拍照 takePhoto(); break; case 3: // 录像 recordVideo(); break; } }, @@ -219,22 +184,6 @@ }); }; // 选择视频 const chooseVideos = () => { uni.chooseVideo({ sourceType: ["album"], maxDuration: 60, camera: "back", success: async (res) => { await handleFileUpload([res.tempFilePath]); }, fail: (error) => { console.error("选择视频失败:", error); toast.show("选择视频失败"); }, }); }; // 拍照 const takePhoto = () => { uni.chooseImage({ @@ -248,22 +197,6 @@ fail: (error) => { console.error("拍照失败:", error); toast.show("拍照失败"); }, }); }; // 录像 const recordVideo = () => { uni.chooseVideo({ sourceType: ["camera"], maxDuration: 60, camera: "back", success: async (res) => { await handleFileUpload([res.tempFilePath]); }, fail: (error) => { console.error("录像失败:", error); toast.show("录像失败"); }, }); }; @@ -382,14 +315,6 @@ case "bmp": case "webp": return "image"; case "mp4": case "mov": case "avi": case "wmv": case "flv": case "mkv": case "webm": return "video"; case "pdf": return "pdf"; case "doc": @@ -516,14 +441,3 @@ } } </style> src/pages/production/wire/report/rawMaterial.vue
@@ -30,6 +30,7 @@ v-model="materialData.conductivity" range-key="label" placeholder="请输入" type="number" ></wd-input> </wd-form-item> <wd-form-item label="抗拉强度(Mpa)" prop="tensileStrength" required> @@ -37,6 +38,7 @@ v-model="materialData.tensileStrength" range-key="label" placeholder="请输入" type="number" ></wd-input> </wd-form-item> <wd-form-item label="电阻率(nΩ·m)" prop="resistivity" required> @@ -44,6 +46,7 @@ v-model="materialData.resistivity" range-key="label" placeholder="请输入" type="number" ></wd-input> </wd-form-item> <wd-form-item label="伸长率(%)" prop="elongationRate" required> @@ -51,15 +54,25 @@ v-model="materialData.elongationRate" range-key="label" placeholder="请输入" type="number" ></wd-input> </wd-form-item> <wd-form-item label="外观质量" prop="appearanceQuality" required> <wd-select-picker v-model="materialData.appearanceQuality" range-key="label" :columns="drawing_appearanceQuality" placeholder="请选择" ></wd-select-picker> <view class="checkbox-group"> <wd-checkbox v-for="option in drawing_appearanceQuality" :key="option.value" :modelValue=" Array.isArray(materialData.appearanceQuality) ? materialData.appearanceQuality.includes(String(option.value)) : false " shape="square" @change="(val) => handleAppearanceQualityCheckbox(String(option.value), val)" > {{ option.label }} </wd-checkbox> </view> </wd-form-item> </wd-form> </view> @@ -73,7 +86,7 @@ </template> <script setup lang="ts"> import { ref, onMounted } from "vue"; import { ref, onMounted, watch, nextTick } from "vue"; import { useToast } from "wot-design-uni"; import ManageApi from "@/api/product/manage"; @@ -131,9 +144,104 @@ const activeTab = ref("reel"); // 与tab的name保持一致 const toast = useToast(); // 存储前一个外观质量值,用于比较变化 const previousAppearanceQuality = ref<string[]>([]); // 处理外观质量下拉框的互斥选择逻辑 const handleAppearanceQualityChange = ( selectedValues: string[], allOptions: Array<{ label: string; value: string | number }> ): string[] => { // 如果没有选项数据,直接返回 if (!allOptions || allOptions.length === 0) { return selectedValues; } // 获取之前的值 const previousValues = previousAppearanceQuality.value || []; // 查找"无外观问题"选项的值 const noIssueOption = allOptions.find( (item) => item.label === "无外观问题" || item.value === "无外观问题" ); if (!noIssueOption) { // 如果字典中没有"无外观问题"选项,直接返回 return selectedValues; } const noIssueValue = String(noIssueOption.value); // 检查当前选中值中是否包含"无外观问题" const hasNoIssue = selectedValues.includes(noIssueValue); // 检查之前是否包含"无外观问题" const hadNoIssue = previousValues.includes(noIssueValue); // 判断是否新选择了"无外观问题"(之前没有,现在有) const isNewlySelectedNoIssue = !hadNoIssue && hasNoIssue; // 判断是否移除了"无外观问题"(之前有,现在没有) const isRemovedNoIssue = hadNoIssue && !hasNoIssue; // 判断是否在"无外观问题"已选中的情况下选择了其他选项 const isSelectingOtherWithNoIssue = hadNoIssue && hasNoIssue && selectedValues.length > previousValues.length; let result: string[]; if (isNewlySelectedNoIssue) { // 如果新选择了"无外观问题",则只保留"无外观问题" result = [noIssueValue]; } else if (isSelectingOtherWithNoIssue) { // 如果"无外观问题"已经被选中,且新选择了其他选项,则移除"无外观问题" result = selectedValues.filter((val) => val !== noIssueValue); } else if (isRemovedNoIssue) { // 如果移除了"无外观问题",直接返回(已经是其他选项了) result = selectedValues; } else { result = selectedValues; } // 保存当前值作为下一次的前一个值 previousAppearanceQuality.value = result; return result; }; // 处理复选框的change事件 const handleAppearanceQualityCheckbox = (optionValue: string, checked: boolean) => { const currentValues = Array.isArray(materialData.value.appearanceQuality) ? materialData.value.appearanceQuality : []; let newValues: string[]; if (checked) { // 选中 newValues = [...currentValues, optionValue]; } else { // 取消选中 newValues = currentValues.filter((v) => v !== optionValue); } // 应用互斥逻辑 const result = handleAppearanceQualityChange( newValues, drawing_appearanceQuality.value as Array<{ label: string; value: string | number }> ); // 更新值 materialData.value.appearanceQuality = result; }; // 本地响应式数据,用于存储用户输入 const materialData = ref<Record<string, any>>({}); const initializeData = () => { // 查找"无外观问题"的值 const noIssueOption = drawing_appearanceQuality.value.find( (item) => item.label === "无外观问题" || item.value === "无外观问题" ); const defaultAppearanceQuality = noIssueOption ? [String(noIssueOption.value)] : []; // 初始化原材料数据 materialData.value = { model: "", @@ -142,8 +250,11 @@ tensileStrength: "", resistivity: "", elongationRate: "", appearanceQuality: "", appearanceQuality: defaultAppearanceQuality, }; // 初始化前一个值 previousAppearanceQuality.value = defaultAppearanceQuality; }; // 初始化数据 @@ -152,7 +263,32 @@ // 在组件挂载时异步加载数据字典 onMounted(async () => { await loadDictData(); // 字典数据加载完成后,重新初始化数据,确保默认值能正确匹配 initializeData(); }); // 监听外观质量变化,应用互斥逻辑 watch( () => materialData.value.appearanceQuality, (newValue, oldValue) => { const normalizedNewValue = Array.isArray(newValue) ? newValue : []; const normalizedOldValue = Array.isArray(oldValue) ? oldValue : []; // 应用互斥逻辑 const result = handleAppearanceQualityChange( normalizedNewValue, drawing_appearanceQuality.value as Array<{ label: string; value: string | number }> ); // 如果值被修改了,异步更新,避免在监听中同步修改 if (JSON.stringify(result) !== JSON.stringify(normalizedNewValue)) { nextTick(() => { materialData.value.appearanceQuality = result; }); } }, { deep: true } ); // 监听props变化,更新本地数据 @@ -166,6 +302,30 @@ }; const handleSubmit = async () => { // 表单验证 if ( !materialData.value.model || materialData.value.model === "" || !materialData.value.spec || materialData.value.spec === "" || !materialData.value.conductivity || materialData.value.conductivity === "" || !materialData.value.tensileStrength || materialData.value.tensileStrength === "" || !materialData.value.resistivity || materialData.value.resistivity === "" || !materialData.value.elongationRate || materialData.value.elongationRate === "" || !materialData.value.appearanceQuality || (Array.isArray(materialData.value.appearanceQuality) && materialData.value.appearanceQuality.length === 0) || (!Array.isArray(materialData.value.appearanceQuality) && materialData.value.appearanceQuality === "") ) { toast.error("请填写完整的必填项"); return false; } try { // 调用API提交数据 await ManageApi.addWireRawMaterialInspect({ @@ -273,47 +433,13 @@ width: 100%; } // 美化选择器样式 - 增加权重确保样式生效 :deep(.wd-select-picker) { & .wd-select-picker__option { text-align: center !important; padding: 12px 0 !important; font-size: 16px !important; .checkbox-group { display: flex; flex-wrap: wrap; gap: 12px; } & .wd-select-picker__confirm { border-radius: 8px !important; background-color: #409eff !important; color: white !important; font-weight: 500 !important; } & .wd-select-picker__header { padding: 10px 0 !important; border-bottom: 1px solid #e6e6e6 !important; } & .wd-select-picker__title { font-size: 16px !important; font-weight: 500 !important; } } // 确保选择器内部选项居中 - 更通用的选择器 :deep(.wd-select-picker__content) { .wd-select-picker__option { text-align: center !important; padding: 12px 0 !important; font-size: 16px !important; } } // 直接针对选项元素的选择器 :deep([class*="select-picker"].wd-popup) { .wd-select-picker__option { text-align: center !important; padding: 12px 0 !important; font-size: 16px !important; } :deep(.wd-checkbox) { margin-right: 0; } </style> src/pages/production/wire/report/reportManage.vue
@@ -1,7 +1,7 @@ <template> <view class="report-manage-page"> <view class="pt-2"> <wd-card class="card_bg"> <view class="pt-2 fixed-header-card"> <wd-card class="card_bg header-card"> <template #title> <view class="flex justify-between w-full"> <text class="font-medium text-[#252525]">报工管理</text> @@ -195,7 +195,9 @@ </wd-row> <template #footer> <view class="flex gap-2"> <wd-button plain size="small" @click="toAttachment(child)">附件</wd-button> <wd-button plain size="small" @click="toAttachment(child)" style="margin-right: 10px"> 附件 </wd-button> <wd-button plain type="error" size="small" @click="handleDeleteSingle(child)"> 删除 </wd-button> @@ -242,7 +244,7 @@ </wd-cell-group> </view> <view class="dialog-footer"> <wd-button plain @click="closeAddDialog">取消</wd-button> <wd-button plain @click="closeAddDialog" style="margin-right: 10px">取消</wd-button> <wd-button type="primary" class="ml-2" @click="handleSaveNewChild">保存</wd-button> </view> </view> @@ -578,12 +580,27 @@ .report-manage-page { min-height: 100vh; background: #f3f9f8; padding-bottom: 20px; padding: 0 4px 20px 4px; } .card_bg { box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05); padding-bottom: 10px; } .fixed-header-card { position: sticky; top: 0; z-index: 100; background: #f3f9f8; padding: 8px 0; } .header-card { border: none; margin: 0; border-radius: 0; box-shadow: none; } .icon_box { @@ -649,7 +666,37 @@ } .page-content { padding: 12px; padding: 12px 4px; margin-top: 12px; } // 弹框 z-index 需要高于固定头部卡片 :deep(.yl-popup) { z-index: 400 !important; } // 弹框遮罩层 z-index 也需要高于固定头部卡片(使用更高的值确保在固定头部之上) :deep(.wd-popup__mask), :deep(.wd-popup-mask), :deep([class*="popup"][class*="mask"]), :deep(.wd-overlay), :deep([class*="overlay"]) { z-index: 300 !important; } .wd-card { margin-left: 5px; margin-right: 5px; margin-top: 0px; } </style> <style lang="scss"> // 全局样式:确保弹框遮罩层 z-index 高于固定头部卡片 .wd-popup__mask, .wd-popup-mask, [class*="popup"][class*="mask"], .wd-overlay, [class*="overlay"] { z-index: 300 !important; } </style> src/pages/production/wire/report/wire.vue
@@ -116,10 +116,17 @@ </view> <template #footer> <view class="flex justify-start"> <wd-button plain size="small" type="primary" @click="openChildDialog(item)"> <view class="flex justify-start gap-2"> <wd-button plain size="small" type="primary" @click="openChildDialog(item)" style="margin-right: 10px" > 报工管理 </wd-button> <wd-button plain size="small" @click="() => toSelfInspect(item)">自检</wd-button> </view> </template> </wd-card> @@ -151,13 +158,14 @@ label="杆包号" label-width="100px" placeholder="请输入杆包号" type="number" /> <wd-input v-model="newParentData.poleWeight" label="杆重(kg)" label-width="100px" type="number" placeholder="请输入杆重" type="number" /> <wd-picker v-model="newParentData.supplier" @@ -169,7 +177,7 @@ </wd-cell-group> </view> <view class="dialog-footer"> <wd-button plain @click="closeParentDialog">取消</wd-button> <wd-button plain @click="closeParentDialog" style="margin-right: 10px">取消</wd-button> <wd-button type="primary" class="ml-2" @click="handleSaveNewParent">保存</wd-button> </view> </view> @@ -490,6 +498,13 @@ drawFormRef.visible = false; }; // 跳转到自检页面 const toSelfInspect = (row: any) => { uni.navigateTo({ url: `/pages/production/wire/selfInspect/index?wireId=${paramsId.value}&poleModel=${row.poleModel || ""}&poleNumber=${row.poleNumber || ""}`, }); }; onLoad(async (options: any) => { paramsId.value = options.id; await getDetailData(options.id); @@ -501,7 +516,7 @@ <style lang="scss" scoped> .list { min-height: calc(100vh - 30px); padding: 12px; padding: 12px 4px; background: #f3f9f8; :deep() { @@ -543,6 +558,30 @@ border-radius: 12px 12px 0 0; } .dialog-header { display: flex; justify-content: space-between; align-items: center; padding: 16px; border-bottom: 1px solid #e6e6e6; position: sticky; top: 0; background: #fff; z-index: 10; } .dialog-title { font-size: 16px; color: #333; font-weight: 500; } .close-icon { font-size: 20px; color: #999; padding: 4px; } .dialog-content { flex: 1; overflow-y: auto; src/pages/production/wire/selfInspect/form.vue
@@ -1,66 +1,333 @@ <template> <wd-form ref="form" :model="model" class="relative form_box"> <wd-cell-group :border="true"> <view class="form-container"> <view class="dialog-header"> <text class="dialog-title">{{ isEdit ? "编辑自检" : "新增自检" }}</text> <wd-icon name="close" class="close-icon" @click="handleClose"></wd-icon> </view> <view class="dialog-content"> <wd-form ref="formRef" :model="formData"> <!-- 基本信息 --> <view class="section-title">基本信息</view> <wd-cell-group border> <wd-input v-model="model.selfInspectName" label="自检名称" label-width="100px" prop="selfInspectName" clearable placeholder="请输入自检名称" v-model="formData.fixedInfo.firstNo" label="首检单号" label-width="120px" placeholder="请输入首检单号" /> <wd-input v-model="model.unit" label="单位" label-width="100px" prop="unit" clearable placeholder="请输入单位" /> <wd-input v-model="model.standard" label="标准值" label-width="100px" prop="standard" clearable placeholder="请输入标准值" /> <wd-input v-model="model.selfInspectValue" label="自检值" label-width="100px" prop="selfInspectValue" clearable placeholder="请输入自检值" /> <wd-input v-model="model.selfInspectResult" label="自检结果" label-width="100px" prop="selfInspectResult" clearable placeholder="请输入自检结果" v-model="formData.fixedInfo.lastSlot" label="定径模具" label-width="120px" placeholder="请输入定径模具" /> </wd-cell-group> <!-- 单丝直径 --> <view class="section-title">单丝直径(mm)</view> <wd-cell-group border> <wd-cell title="最大值" label-width="120px"> <wd-input-number v-model="formData.inspectionResult.maxDia" :min="0" :precision="3" :step="0.01" placeholder="请输入最大值" :max=" formData.inspectionResult.minDia !== null && formData.inspectionResult.minDia !== undefined ? undefined : 999999 " /> </wd-cell> <wd-cell title="最小值" label-width="120px"> <wd-input-number v-model="formData.inspectionResult.minDia" :min="0" :precision="3" :step="0.01" placeholder="请输入最小值" :max=" formData.inspectionResult.maxDia !== null && formData.inspectionResult.maxDia !== undefined ? formData.inspectionResult.maxDia : 999999 " /> </wd-cell> </wd-cell-group> <!-- 外观 --> <view class="section-title">外观</view> <wd-cell-group border> <wd-cell title="外观质量" label-width="120px"> <view class="checkbox-group"> <wd-checkbox v-for="option in appearanceOptions" :key="option.value" :modelValue=" Array.isArray(formData.inspectionResult.appearance) ? formData.inspectionResult.appearance.includes(String(option.value)) : false " shape="square" @change="(val) => handleAppearanceChange(String(option.value), val)" > {{ option.label }} </wd-checkbox> </view> </wd-cell> </wd-cell-group> <!-- 成盘质量 --> <view class="section-title">成盘质量</view> <wd-cell-group border> <wd-cell title="卷绕紧密" label-width="120px"> <wd-radio-group v-model="formData.inspectionResult.windingTightness" size="small"> <wd-radio value="是">是</wd-radio> <wd-radio value="否">否</wd-radio> </wd-radio-group> </wd-cell> <wd-cell title="排列整齐" label-width="120px"> <wd-radio-group v-model="formData.inspectionResult.arrangementNeatness" size="small"> <wd-radio value="是">是</wd-radio> <wd-radio value="否">否</wd-radio> </wd-radio-group> </wd-cell> <wd-cell title="外层铝线离侧板边缘距离(mm)" label-width="200px"> <wd-input-number v-model="formData.inspectionResult.aluminumWireDistance" :min="0" :precision="0" placeholder="请输入距离" :class="{ 'text-danger': formData.inspectionResult.aluminumWireDistance < 25 }" /> </wd-cell> </wd-cell-group> <!-- 其他信息 --> <view class="section-title">其他信息</view> <wd-cell-group border> <wd-cell title="成品模后接头情况" label-width="120px"> <wd-radio-group v-model="formData.inspectionResult.jointCondition" size="small"> <wd-radio value="有">有</wd-radio> <wd-radio value="无">无</wd-radio> </wd-radio-group> </wd-cell> <wd-cell title="结论" label-width="120px"> <wd-radio-group v-model="formData.inspectionResult.conclusion" size="small"> <wd-radio value="合格">合格</wd-radio> <wd-radio value="不合格">不合格</wd-radio> </wd-radio-group> </wd-cell> </wd-cell-group> </wd-form> </view> <view class="dialog-footer"> <wd-button plain @click="handleClose" style="margin-right: 10px">取消</wd-button> <wd-button type="primary" class="ml-2" @click="handleSubmit">保存</wd-button> </view> </view> </template> <script setup lang="ts"> import useFormData from "@/hooks/useFormData"; const { form: model } = useFormData({ selfInspectName: undefined, // 自检名称 unit: undefined, // 单位 standard: undefined, // 标准值 selfInspectValue: undefined, // 自检值 selfInspectResult: undefined, // 自检值 }); </script> <style lang="scss" scoped> .form_box { import { ref, watch, computed } from "vue"; import { dayjs } from "wot-design-uni"; const props = defineProps<{ visible: boolean; formData: any; appearanceOptions: Array<{ label: string; value: string | number }>; previousAppearanceValue?: string[]; }>(); const emit = defineEmits<{ (e: "update:visible", value: boolean): void; (e: "submit", data: any): void; (e: "close"): void; }>(); const formRef = ref(); const isEdit = computed(() => !!props.formData?.id && !props.formData?.isNew); // 存储前一个外观质量值 const previousAppearanceValue = ref<string[]>(props.previousAppearanceValue || []); // 处理外观质量互斥逻辑 const handleAppearanceQualityChange = ( selectedValues: string[], previousValues: string[], allOptions: Array<{ label: string; value: string | number }> ): string[] => { if (!allOptions || allOptions.length === 0) { return selectedValues; } .submit_btn { position: absolute; bottom: 0; width: 100%; const noIssueOption = allOptions.find( (item) => item.label === "无外观问题" || item.value === "无外观问题" ); if (!noIssueOption) { return selectedValues; } const noIssueValue = String(noIssueOption.value); const hasNoIssue = selectedValues.includes(noIssueValue); const hadNoIssue = previousValues.includes(noIssueValue); const isNewlySelectedNoIssue = !hadNoIssue && hasNoIssue; const isSelectingOtherWithNoIssue = hadNoIssue && hasNoIssue && selectedValues.length > previousValues.length; let result: string[]; if (isNewlySelectedNoIssue) { result = [noIssueValue]; } else if (isSelectingOtherWithNoIssue) { result = selectedValues.filter((val) => val !== noIssueValue); } else { result = selectedValues; } return result; }; // 处理外观选择变化 const handleAppearanceChange = (optionValue: string, checked: boolean) => { const currentValues = Array.isArray(props.formData.inspectionResult.appearance) ? props.formData.inspectionResult.appearance : []; let newValues: string[]; if (checked) { newValues = [...currentValues, optionValue]; } else { newValues = currentValues.filter((v: string) => v !== optionValue); } const processedValues = handleAppearanceQualityChange( newValues, previousAppearanceValue.value, props.appearanceOptions ); props.formData.inspectionResult.appearance = JSON.parse(JSON.stringify(processedValues)); previousAppearanceValue.value = JSON.parse(JSON.stringify(processedValues)); }; const handleClose = () => { emit("update:visible", false); emit("close"); }; const handleSubmit = () => { // 验证单丝直径:最大值应该大于或等于最小值 const maxDia = props.formData.inspectionResult.maxDia; const minDia = props.formData.inspectionResult.minDia; if (maxDia !== null && minDia !== null && maxDia < minDia) { uni.showToast({ title: "单丝直径最大值不能小于最小值", icon: "none", }); return; } // 验证外层铝线离侧板边缘距离 const aluminumWireDistance = props.formData.inspectionResult.aluminumWireDistance; if (aluminumWireDistance < 25) { uni.showToast({ title: "外层铝线离侧板边缘距离不能低于25mm", icon: "none", }); return; } emit("submit", props.formData); }; // 监听 visible 变化,初始化 previousAppearanceValue watch( () => props.visible, (newVal) => { if (newVal && props.formData?.inspectionResult?.appearance) { previousAppearanceValue.value = JSON.parse( JSON.stringify(props.formData.inspectionResult.appearance || []) ); } } ); </script> <style lang="scss" scoped> .form-container { max-height: 80vh; display: flex; flex-direction: column; background: #fff; border-radius: 12px 12px 0 0; } .dialog-header { display: flex; justify-content: space-between; align-items: center; padding: 16px; border-bottom: 1px solid #e6e6e6; position: sticky; top: 0; background: #fff; z-index: 10; } .dialog-title { font-size: 16px; color: #333; font-weight: 500; } .close-icon { font-size: 20px; color: #999; padding: 4px; } .dialog-content { flex: 1; overflow-y: auto; padding: 16px; } .section-title { font-size: 14px; font-weight: 700; color: #252525; margin: 16px 0 8px 0; padding-bottom: 8px; border-bottom: 1px solid #e6e6e6; } .checkbox-group { display: flex; flex-wrap: wrap; gap: 12px; } :deep(.wd-checkbox) { margin-right: 0; } .dialog-footer { display: flex; justify-content: flex-end; padding: 16px; border-top: 1px solid #e6e6e6; gap: 8px; } .text-danger { :deep(.wd-input-number__input) { color: #f56c6c; border-color: #f56c6c; } } </style> src/pages/production/wire/selfInspect/index.vue
@@ -1,103 +1,571 @@ <template> <view class="list"> <z-paging ref="pagingRef" v-model="cardList" :fixed="false" @query="getList"> <template #top> <CardTitle title="拉丝自检" :hideAction="false" /> <CardTitle title="自检管理" :hideAction="false" :full="false"> <template #action> <wd-button icon="file-add" :round="false" size="small" custom-class="add_btn" @click="handleOpenAddDialog" > 新增自检 </wd-button> </template> <wd-card v-for="(item, index) in cardList" :key="index" type="rectangle" custom-class="round"> </CardTitle> <!-- 自检列表(卡片样式) --> <view class="inspection-list"> <wd-card v-for="(item, index) in dataList" :key="item.id || index" class="card_bg" style="margin-bottom: 12px" > <template #title> <view class="flex justify-between"> <view> <wd-icon name="a-rootlist" color="#0D867F"></wd-icon> <text class="text-[#252525] ml-2 font-medium">{{ item.inspectionProject }}</text> <view class="flex justify-between w-full"> <view class="flex items-center"> <wd-icon name="file" color="#0D867F" size="20px"></wd-icon> <text class="font-medium text-[#252525] ml-2">自检记录 #{{ index + 1 }}</text> </view> <view class="text-[#A8A8A8]" @click="toEdit">编辑</view> <view class="flex gap-2"> <wd-button plain type="primary" size="small" @click="handleEdit(item)" style="margin-right: 10px" > 编辑 </wd-button> <wd-button plain type="error" size="small" @click="handleDelete(item)"> 删除 </wd-button> </view> </view> </template> <ProductionCard :data="cardAttr" :value="item" color="#0D867F" /> </wd-card> </z-paging> <wd-popup v-model="dialog.visible" position="bottom" custom-class="yl-popup"> <view class="action px-3"> <wd-button type="text" @click="cancel">取消</wd-button> <wd-button type="text" @click="submit">确定</wd-button> <view class="card-content"> <!-- 基本信息 --> <view class="info-section"> <view class="section-header"> <wd-icon name="info" color="#0D867F" size="16px"></wd-icon> <text class="section-title">基本信息</text> </view> <SelfInspectForm /> <view class="info-grid"> <view class="info-item"> <text class="info-label">自检时间:</text> <text class="info-value">{{ item.fixedInfo?.recordDate || "-" }}</text> </view> <view class="info-item"> <text class="info-label">机台:</text> <text class="info-value">{{ item.fixedInfo?.deviceModel || "-" }}</text> </view> <view class="info-item"> <text class="info-label">班组:</text> <text class="info-value">{{ item.fixedInfo?.teamName || "-" }}</text> </view> <view class="info-item"> <text class="info-label">首检单号:</text> <text class="info-value">{{ item.fixedInfo?.firstNo || "-" }}</text> </view> </view> </view> <!-- 原材料信息 --> <view class="info-section"> <view class="section-header"> <wd-icon name="folder" color="#0D867F" size="16px"></wd-icon> <text class="section-title">原材料信息</text> </view> <view class="info-grid"> <view class="info-item"> <text class="info-label">型号:</text> <text class="info-value">{{ item.fixedInfo?.poleModel || "-" }}</text> </view> <view class="info-item"> <text class="info-label">批次:</text> <text class="info-value">{{ item.fixedInfo?.poleNumber || "-" }}</text> </view> <view class="info-item"> <text class="info-label">生产轴数:</text> <text class="info-value">{{ item.fixedInfo?.outputNumber || "-" }}</text> </view> <view class="info-item"> <text class="info-label">定径模具:</text> <text class="info-value">{{ item.fixedInfo?.lastSlot || "-" }}</text> </view> </view> </view> <!-- 单丝直径 --> <view class="info-section"> <view class="section-header"> <wd-icon name="setting" color="#0D867F" size="16px"></wd-icon> <text class="section-title">单丝直径(mm)</text> </view> <view class="info-grid"> <view class="info-item"> <text class="info-label">规格型号:</text> <text class="info-value">{{ item.fixedInfo?.model || "-" }}</text> </view> <view class="info-item"> <text class="info-label">最大值:</text> <text class="info-value"> {{ item.inspectionResult?.maxDia !== null && item.inspectionResult?.maxDia !== undefined ? item.inspectionResult.maxDia : "-" }} </text> </view> <view class="info-item"> <text class="info-label">最小值:</text> <text class="info-value"> {{ item.inspectionResult?.minDia !== null && item.inspectionResult?.minDia !== undefined ? item.inspectionResult.minDia : "-" }} </text> </view> </view> </view> <!-- 成盘质量 --> <view class="info-section"> <view class="section-header"> <wd-icon name="check" color="#0D867F" size="16px"></wd-icon> <text class="section-title">成盘质量</text> </view> <view class="info-grid"> <view class="info-item"> <text class="info-label">卷绕紧密:</text> <text class="info-value"> {{ item.inspectionResult?.windingTightness || "是" }} </text> </view> <view class="info-item"> <text class="info-label">排列整齐:</text> <text class="info-value"> {{ item.inspectionResult?.arrangementNeatness || "是" }} </text> </view> <view class="info-item-full"> <text class="info-label">外层铝线离侧板边缘距离(mm):</text> <text class="info-value" :style="{ color: item.inspectionResult?.aluminumWireDistance < 25 ? '#f56c6c' : '', }" > {{ item.inspectionResult?.aluminumWireDistance || "-" }} </text> </view> </view> </view> <!-- 其他信息 --> <view class="info-section"> <view class="section-header"> <wd-icon name="more" color="#0D867F" size="16px"></wd-icon> <text class="section-title">其他信息</text> </view> <view class="info-grid"> <view class="info-item-full"> <text class="info-label">外观质量:</text> <text class="info-value"> {{ getAppearanceLabel(item.inspectionResult?.appearance) }} </text> </view> <view class="info-item"> <text class="info-label">成品模后接头情况:</text> <text class="info-value">{{ item.inspectionResult?.jointCondition || "无" }}</text> </view> <view class="info-item"> <text class="info-label">结论:</text> <text class="info-value" :style="{ color: item.inspectionResult?.conclusion === '合格' ? '#67c23a' : '#f56c6c', fontWeight: '500', }" > {{ item.inspectionResult?.conclusion || "合格" }} </text> </view> <view class="info-item"> <text class="info-label">记录人:</text> <text class="info-value">{{ item.fixedInfo?.createUserName || "-" }}</text> </view> </view> </view> </view> </wd-card> </view> <!-- 新增/编辑弹框 --> <wd-popup v-model="dialogVisible" position="bottom" custom-class="yl-popup"> <SelfInspectForm :visible="dialogVisible" :form-data="currentFormData" :appearance-options="appearanceOptions" :previous-appearance-value="previousAppearanceValueMap.get(currentFormData?.id || '')" @update:visible="dialogVisible = $event" @submit="handleFormSubmit" @close="handleCloseDialog" /> </wd-popup> <wd-toast /> </view> </template> <script setup lang="ts"> import CardTitle from "@/components/card-title/index.vue"; import ProductionCard from "../../components/ProductionCard.vue"; import { useToast } from "wot-design-uni"; import SelfInspectForm from "./form.vue"; import { useToast, dayjs } from "wot-design-uni"; import { onLoad } from "@dcloudio/uni-app"; import { ref } from "vue"; import ManageApi from "@/api/product/manage"; import SelfInspectForm from "./form.vue"; const pagingRef = ref(); const paramsId = ref(); const toast = useToast(); const dialog = reactive({ visible: false, const wireId = ref<string>(""); const poleModel = ref<string>(""); const poleNumber = ref<string>(""); // 数据列表 const dataList = ref<any[]>([]); const loading = ref(false); // 外观选项 const appearanceOptions = ref<Array<{ label: string; value: string | number }>>([]); // 存储每行外观质量的之前的值 const previousAppearanceValueMap = ref<Map<string, string[]>>(new Map()); // 弹框相关 const dialogVisible = ref(false); const currentFormData = ref<any>(null); // 加载外观字典数据 const loadAppearanceDict = async () => { try { const qualityRes = await ManageApi.dictAPI("draw_appearance_quality"); if (qualityRes.data && Array.isArray(qualityRes.data)) { appearanceOptions.value = qualityRes.data.map((item: any) => ({ label: item.dictLabel || "", value: item.dictValue || "", })); } } catch (error) { console.error("加载外观字典失败:", error); } }; // 获取外观标签 const getAppearanceLabel = (value: string | string[]) => { if (Array.isArray(value) && value.length > 0) { const labels = value.map((v) => { const option = appearanceOptions.value.find((item) => item.value === v); return option ? option.label : v; }); const cardAttr = ref<any[]>([ { label: "单位", prop: "inspectionUnit", }, { label: "标准值", prop: "standardValue", }, { label: "自检值", prop: "inspectionItem", }, { label: "自检结果", prop: "inspectionResult", color: "#FF1E1E", }, ]); const cardList = ref<any[]>([]); return labels.join(", "); } if (typeof value === "string" && value) { const option = appearanceOptions.value.find((item) => item.value === value); return option ? option.label : value; } return "无"; }; // const addReport = () => { // dialog.visible = true; // }; // 获取数据 const getData = async () => { if (!wireId.value) return; const toEdit = () => { uni.navigateTo({ url: "/pages/production/wire/selfInspect/edit", loading.value = true; try { const { code, data } = await ManageApi.getSelfInspection({ wireId: wireId.value, poleModel: poleModel.value, poleNumber: poleNumber.value, }); }; const submit = () => { toast.show("提交"); dialog.visible = false; }; const cancel = () => { toast.show("取消"); dialog.visible = false; }; const getList = async () => { const { data } = await ManageApi.getSelfInspection({ outPutId: paramsId.value, type: "拉丝", if (code === 200) { dataList.value = data || []; // 初始化每行的 previousAppearanceValue dataList.value.forEach((item) => { if (item.id) { previousAppearanceValueMap.value.set( item.id, JSON.parse(JSON.stringify(item.inspectionResult?.appearance || [])) ); } }); pagingRef.value.complete(data); } } catch (error) { console.error("获取自检数据失败:", error); toast.error("获取自检数据失败"); } finally { loading.value = false; } }; onLoad((options: any) => { paramsId.value = options.id; // 存储从接口获取的原始数据(用于保存时补充缺失字段) const originalApiData = ref<any>(null); // 打开新增弹框 const handleOpenAddDialog = async () => { const newItem = { id: `temp_${Date.now()}`, fixedInfo: { firstNo: "", recordDate: dayjs().format("YYYY-MM-DD"), deviceModel: "", teamName: "", workShift: "", poleModel: poleModel.value || "", poleNumber: poleNumber.value || "", outputNumber: "", lastSlot: "", model: "", createUserName: "", id: null, teamId: null, wireId: wireId.value ? Number(wireId.value) : null, }, inspectionResult: { maxDia: null, minDia: null, appearance: ["无外观问题"], windingTightness: "是", arrangementNeatness: "是", aluminumWireDistance: 25, jointCondition: "无", conclusion: "合格", dia: null, twistedLayer: null, wireId: wireId.value ? String(wireId.value) : null, }, isEditing: true, isNew: true, }; // 调用API获取默认数据 try { const { code, data } = await ManageApi.getDrawInspectStyleByGetDrawInspect({ wireId: wireId.value, poleModel: poleModel.value, poleNumber: poleNumber.value, }); if (code === 200 && data) { // 保存原始接口数据,用于提交时补充缺失字段 originalApiData.value = JSON.parse(JSON.stringify(data)); if (data.fixedInfo) { const clonedFixedInfo = JSON.parse(JSON.stringify(data.fixedInfo)); // 合并数据,保留表单中已有的值,补充接口返回的值 Object.assign(newItem.fixedInfo, clonedFixedInfo, { // 确保这些字段从接口数据中获取 id: clonedFixedInfo.id || null, teamId: clonedFixedInfo.teamId || null, wireId: clonedFixedInfo.wireId || (wireId.value ? Number(wireId.value) : null), }); } if (data.drawWireInfo) { const clonedDrawWireInfo = JSON.parse(JSON.stringify(data.drawWireInfo)); // 合并数据,保留表单中已有的值,补充接口返回的值 Object.assign(newItem.inspectionResult, clonedDrawWireInfo, { // 确保这些字段从接口数据中获取 dia: clonedDrawWireInfo.dia || null, twistedLayer: clonedDrawWireInfo.twistedLayer || null, wireId: clonedDrawWireInfo.wireId || (wireId.value ? String(wireId.value) : null), }); } } } catch (error) { console.error("获取默认数据失败:", error); } currentFormData.value = JSON.parse(JSON.stringify(newItem)); previousAppearanceValueMap.value.set( currentFormData.value.id, JSON.parse(JSON.stringify(currentFormData.value.inspectionResult.appearance || [])) ); dialogVisible.value = true; }; // 编辑自检 const handleEdit = async (row: any) => { // 编辑时也需要获取接口数据,确保有所有必要字段 try { const { code, data } = await ManageApi.getDrawInspectStyleByGetDrawInspect({ wireId: wireId.value, poleModel: poleModel.value, poleNumber: poleNumber.value, }); if (code === 200 && data) { originalApiData.value = JSON.parse(JSON.stringify(data)); } } catch (error) { console.error("获取默认数据失败:", error); } currentFormData.value = JSON.parse(JSON.stringify(row)); previousAppearanceValueMap.value.set( row.id, JSON.parse(JSON.stringify(row.inspectionResult.appearance || [])) ); dialogVisible.value = true; }; // 表单提交 const handleFormSubmit = async (formData: any) => { const aluminumWireDistance = formData.inspectionResult.aluminumWireDistance; if (aluminumWireDistance < 25) { toast.error("外层铝线离侧板边缘距离不能低于25mm"); return; } // 构建提交数据,确保包含所有必要字段 const saveData: any = { fixedInfo: JSON.parse(JSON.stringify(formData.fixedInfo)), inspectionResult: JSON.parse(JSON.stringify(formData.inspectionResult)), wireId: wireId.value, }; // 编辑时,确保 fixedInfo.id 被正确传递 // 优先使用 formData 中的 id(编辑时应该存在) if (formData.fixedInfo?.id !== null && formData.fixedInfo?.id !== undefined) { saveData.fixedInfo.id = formData.fixedInfo.id; } else if (!formData.isNew) { // 如果不是新增,尝试从数据列表中查找 id const existingItem = dataList.value.find((item) => item.id === formData.id); if (existingItem?.fixedInfo?.id) { saveData.fixedInfo.id = existingItem.fixedInfo.id; } } // 如果是从接口获取的数据,补充表单中没有的字段 if (originalApiData.value) { // 补充 fixedInfo 中缺失的字段 if (originalApiData.value.fixedInfo) { const apiFixedInfo = originalApiData.value.fixedInfo; // 确保这些字段存在(如果表单中没有,使用接口返回的值) // 只有在 saveData.fixedInfo.id 不存在时才使用接口返回的 id if (apiFixedInfo.id !== undefined && !saveData.fixedInfo.id) { saveData.fixedInfo.id = apiFixedInfo.id; } if (apiFixedInfo.teamId !== undefined) { saveData.fixedInfo.teamId = apiFixedInfo.teamId; } if (apiFixedInfo.wireId !== undefined) { saveData.fixedInfo.wireId = apiFixedInfo.wireId; } // 补充其他可能缺失的字段 if (apiFixedInfo.recordDate && !saveData.fixedInfo.recordDate) { saveData.fixedInfo.recordDate = apiFixedInfo.recordDate; } } // 补充 inspectionResult 中缺失的字段 if (originalApiData.value.drawWireInfo) { const apiInspectionResult = originalApiData.value.drawWireInfo; // 确保这些字段存在(如果表单中没有,使用接口返回的值) if (apiInspectionResult.dia !== undefined) { saveData.inspectionResult.dia = apiInspectionResult.dia; } if (apiInspectionResult.twistedLayer !== undefined) { saveData.inspectionResult.twistedLayer = apiInspectionResult.twistedLayer; } if (apiInspectionResult.wireId !== undefined) { saveData.inspectionResult.wireId = apiInspectionResult.wireId; } } } // 确保 wireId 字段存在 if (!saveData.fixedInfo.wireId && wireId.value) { saveData.fixedInfo.wireId = Number(wireId.value); } if (!saveData.inspectionResult.wireId && wireId.value) { saveData.inspectionResult.wireId = String(wireId.value); } try { const { code, data } = await ManageApi.addSelfInspection(saveData); if (code === 200) { toast.success("保存成功"); dialogVisible.value = false; originalApiData.value = null; // 清空原始数据 previousAppearanceValueMap.value.delete(formData.id); await getData(); } else { toast.error(data || "保存失败"); } } catch (error) { console.error("保存失败:", error); toast.error("保存失败"); } }; // 关闭弹框 const handleCloseDialog = () => { dialogVisible.value = false; currentFormData.value = null; originalApiData.value = null; // 清空原始数据 }; // 删除自检 const handleDelete = async (row: any) => { try { const res = await uni.showModal({ title: "提示", content: "确定删除该自检记录吗?", }); if (res.confirm) { if (row.fixedInfo?.id && !row.isNew) { const { code } = await ManageApi.deleteSelfInspection(row.fixedInfo.id); if (code === 200) { toast.success("删除成功"); await getData(); } else { toast.error("删除失败"); } } else { const index = dataList.value.findIndex((item) => item.id === row.id); if (index !== -1) { dataList.value.splice(index, 1); toast.success("删除成功"); } } } } catch (error) { console.error("删除自检失败:", error); toast.error("删除失败"); } }; onLoad(async (options: any) => { wireId.value = options.wireId || ""; poleModel.value = options.poleModel || ""; poleNumber.value = options.poleNumber || ""; await loadAppearanceDict(); await getData(); }); </script> <style lang="scss" scoped> .list { height: calc(100vh - 30px); padding: 12px; min-height: calc(100vh - 30px); padding: 12px 4px; background: #f3f9f8; :deep() { @@ -107,8 +575,75 @@ } } .action { .inspection-list { margin-top: 12px; } .card_bg { box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05); padding-bottom: 10px; border-radius: 8px; overflow: hidden; } .card-content { padding: 0 4px; } .info-section { margin-bottom: 16px; &:last-child { margin-bottom: 0; } } .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #e6e6e6; } .section-title { font-size: 14px; font-weight: 600; color: #252525; margin-left: 6px; } .info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px 16px; } .info-item { display: flex; align-items: flex-start; min-height: 24px; } .info-item-full { display: flex; align-items: flex-start; min-height: 24px; grid-column: 1 / -1; } .info-label { font-size: 13px; color: #646874; margin-right: 8px; white-space: nowrap; flex-shrink: 0; } .info-value { font-size: 13px; color: #252525; flex: 1; word-break: break-all; } </style>