| src/api/product/outbound.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/index/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/outbound/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/outbound/index0.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/outbound/material.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/product/outbound.ts
@@ -11,6 +11,38 @@ }); }, // åºåºåæ¥è¯¢ queryErpOutboundOrder(params: { contractNo?: string; carNo?: string; current: number; size: number; }) { return request<BaseResult<any>>({ url: "/app/queryErpOutboundOrder", method: "POST", data: params, }); }, // æ¥è¯¢åºè´§åæç» queryErpOutboundOrderDetail(params: { vbillcode: string }) { return request<BaseResult<any[]>>({ url: "/app/queryErpOutboundOrderDetail", method: "POST", data: params, }); }, // ç»çº¿/æä¸äºç»´ç æ¥è¯¢ï¼æ¯æåºåºç¶æå¤æï¼ getTagByIdAll(params: { outPutId: string | number }) { return request<BaseResult<any[]>>({ url: "/app/getTagByIdAll", method: "GET", data: params, }); }, // ç»çº¿äºç»´ç æ¥è¯¢ getTagByIdJx(params: { outPutId: string | number }) { return request<BaseResult<any>>({ src/pages.json
@@ -284,6 +284,12 @@ } }, { "path": "pages/outbound/material", "style": { "navigationBarTitleText": "ç©æè¯¦æ " } }, { "path": "pages/routingInspection/index", "style": { "navigationBarTitleText": "å·¡æ£" src/pages/index/index.vue
@@ -278,14 +278,19 @@ } } if (data.deviceGroupName.includes("ä¸") || data.deviceGroupName.includes("ç»")) { if ( (data.deviceGroupName && data.deviceGroupName.includes("ä¸")) || (data.deviceGroupName && data.deviceGroupName.includes("ç»")) ) { // 妿æ¯ä¸æç»ï¼æ¾ç¤ºç产管çèå navList[0].show = true; } console.log("isInspector.value", isInspector.value); if (isInspector.value) { // 妿æ¯å·¡æ£åï¼æ¾ç¤ºå·¡æ£èå navList[1].show = true; } console.log("isStorage.value", isStorage.value); if (isStorage.value) { // 妿æ¯åºåºè§è²ï¼æ¾ç¤ºåºåºèå navList[2].show = true; src/pages/outbound/index.vue
@@ -1,350 +1,212 @@ <template> <view class="list_box"> <CardTitle title="åºåº" :hideAction="false"> <template #action> <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button> <view class="outbound-page"> <view class="search-section"> <wd-row> <wd-col :span="21"> <wd-search v-model="searchKeyword" placeholder="请è¾å ¥è½¦çå·" placeholder-left hide-cancel @search="handleSearch" @clear="handleClear" ></wd-search> </wd-col> <wd-col :span="3"> <view class="scan_box" @click="handleSearch"> <wd-icon name="search" size="24px" color="#0D867F"></wd-icon> </view> </wd-col> </wd-row> </view> <view class="list-section"> <z-paging ref="pagingRef" v-model="shippingList" :fixed="false" :auto-show-back-to-top="true" @query="getList" > <wd-card v-for="(item, index) in shippingList" :key="item.deliveryid || index" custom-class="card_bg" @click="toMaterialDetail(item)" > <template #title> <text class="font-medium text-[#252525]">åè´§åå·: {{ item.vbillcode || "-" }}</text> </template> </CardTitle> <view class="list_content"> <view v-if="outboundList.length === 0" class="empty_tip"> <view class="empty_text">ææ åºåºæ°æ®</view> <wd-row class="my-2"> <wd-col :span="24"> <view class="flex"> <view class="icon_box"> <wd-icon name="folder" color="#0D867F"></wd-icon> </view> <view v-for="(item, index) in outboundList" :key="index" class="outbound_item"> <view class="outbound_item_left"> <view class="outbound_item_content"> <view class="outbound_item_row"> <text class="outbound_item_label">ååå·ï¼</text> <text class="outbound_item_value">{{ item.contractNo || "-" }}</text> <text class="text-[#646874] mx-2"> åè´§åæ¥æ: <text class="text-[#252525]">{{ item.dbilldate || "-" }}</text> </text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">çäº§æ¹æ¬¡å·ï¼</text> <text class="outbound_item_value">{{ item.batchNo || "-" }}</text> </wd-col> </wd-row> <wd-row class="my-2"> <wd-col :span="24"> <view class="flex"> <view class="icon_box"> <wd-icon name="folder" color="#0D867F"></wd-icon> </view> <view class="outbound_item_row"> <text class="outbound_item_label">è§æ ¼åå·ï¼</text> <text class="outbound_item_value">{{ item.model || "-" }}</text> <text class="text-[#646874] mx-2"> 车çå·: <text class="text-[#252525]">{{ item.carno || "-" }}</text> </text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">ééï¼</text> <text class="outbound_item_value">{{ item.weight || "-" }} kg</text> </wd-col> </wd-row> <wd-row class="my-2" v-if="item.vmemo"> <wd-col :span="24"> <view class="flex"> <view class="icon_box"> <wd-icon name="folder" color="#0D867F"></wd-icon> </view> <view class="outbound_item_row"> <text class="outbound_item_label">åå®¶ï¼</text> <text class="outbound_item_value">{{ item.clienteleName || "-" }}</text> <text class="text-[#646874] mx-2"> åè´§å夿³¨: <text class="text-[#252525]">{{ item.vmemo || "-" }}</text> </text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">段é¿ï¼</text> <text class="outbound_item_value">{{ item.actuallyLength || "-" }} M</text> </wd-col> </wd-row> </wd-card> </z-paging> </view> <view class="outbound_item_row"> <text class="outbound_item_label">çäº§æ¥æï¼</text> <text class="outbound_item_value">{{ item.productionDate || "-" }}</text> </view> </view> </view> <view class="outbound_item_action"> <wd-button type="icon" icon="delete" size="small" custom-class="delete-btn" @click.stop="removeOutboundItem(index)" ></wd-button> </view> </view> </view> <view v-if="outboundList.length > 0" class="outbound_footer"> <wd-button block @click="handleOutbound"> <text class="text-[#fff]">åºåº</text> </wd-button> </view> <Scan ref="scanRef" emitName="scanOutbound" /> <wd-toast /> </view> </template> <script setup lang="ts"> import CardTitle from "@/components/card-title/index.vue"; import Scan from "@/components/scan/index.vue"; import { ref } from "vue"; import { useToast } from "wot-design-uni"; import { dayjs } from "wot-design-uni"; import zPaging from "@/components/z-paging/z-paging.vue"; import OutboundApi from "@/api/product/outbound"; const scanRef = ref(); const toast = useToast(); const outboundList = ref<any[]>([]); const projectId = ref<string | number>(""); const pagingRef = ref(); const searchKeyword = ref(""); const shippingList = ref<any[]>([]); // æ ¼å¼åæ¶é´ const formatTime = (date: Date) => { return dayjs(date).format("YYYY-MM-DD HH:mm:ss"); // è·ååè¡¨æ°æ® const getList = async (pageNo: number, pageSize: number) => { const params: any = { contractNo: "", carNo: searchKeyword.value || "", current: pageNo, size: pageSize, }; // æ«ç åè° const getScanCode = async (code: any) => { try { // 妿 code æ¯å¯¹è±¡ä¸æ code åæ®µï¼ä½¿ç¨ code.codeï¼å¦åç´æ¥ä½¿ç¨ code let scanCode = code.code || code; // 妿 scanCode æ¯å¯¹è±¡ï¼å°è¯è·åå ¶ code åæ®µ if (typeof scanCode === "object" && scanCode.code) { scanCode = scanCode.code; } // 妿 scanCode æ¯å符串ï¼ç´æ¥ä½¿ç¨ï¼å¦ææ¯å¯¹è±¡ï¼è½¬ä¸ºå符串 if (typeof scanCode !== "string") { scanCode = JSON.stringify(scanCode); } if (!scanCode) { toast.error("æ«ç å 容为空"); return; } // è§£ææ«ç æ°æ®ï¼ç°å¨äºç»´ç åªå å«id let scanData; try { scanData = JSON.parse(scanCode); } catch (e) { toast.error("äºç»´ç æ ¼å¼é误"); return; } const outPutId = scanData.id; if (!outPutId) { toast.error("äºç»´ç æ ¼å¼é误ï¼ç¼ºå°idä¿¡æ¯"); return; } // æ£æ¥æ¯å¦å·²åå¨ï¼æ ¹æ®idå¤æï¼ const exists = outboundList.value.some((item) => { const itemId = item.id; return itemId && itemId === outPutId && itemId !== "-"; }); if (exists) { toast.error("该æ¡ç å·²åå¨ï¼è¯·å¿é夿«ç "); return; } // è°ç¨æ¥å£è·åç»çº¿è¯¦ç»ä¿¡æ¯ const { data: tagData } = await OutboundApi.getTagByIdJx({ outPutId: outPutId, }); // æåæ°æ®åæ®µï¼æ ¹æ®æ¥å£è¿åçæ°æ®ç»æï¼ const parsedData = { id: tagData?.id || outPutId, contractNo: tagData?.contractno || tagData?.contractNo || "-", batchNo: tagData?.systemno || tagData?.systemNo || tagData?.batchNo || "-", model: tagData?.model || "-", weight: tagData?.actuallyweight || tagData?.actuallyWeight || tagData?.weight || "-", clienteleName: tagData?.clientelename || tagData?.clienteleName || "-", actuallyLength: tagData?.actuallylength || tagData?.actuallyLength || "-", productionDate: tagData?.producttime || tagData?.productionDate || "-", projectId: tagData?.projectid || tagData?.projectId || projectId.value || "", // ä¿çåå§æ°æ® rawData: tagData, scanCode: scanCode, }; // 妿æ¥å£è¿åçæ°æ®ä¸æ projectIdï¼ä¿åå® if (parsedData.projectId && !projectId.value) { projectId.value = parsedData.projectId; } // æ·»å å°å表 const newItem = { ...parsedData, scanTime: formatTime(new Date()), }; outboundList.value.push(newItem); toast.success("æ«ç æå"); } catch (error: any) { console.error("æ«ç å¤ç失败:", error); toast.error(error.msg || "äºç»´ç å¼å¸¸ï¼è¯·æ´æ¢äºç»´ç ï¼"); } }; // è§¦åæ«ç const openScan = () => { scanRef.value.triggerScan(); }; // å é¤é¡¹ const removeOutboundItem = (index: number) => { const item = outboundList.value[index]; const itemName = item.contractNo || item.batchNo || `第${index + 1}项`; uni.showModal({ title: "确认å é¤", content: `ç¡®å®è¦å é¤"${itemName}"åï¼`, confirmText: "å é¤", cancelText: "åæ¶", confirmColor: "#ff4444", success: (res) => { if (res.confirm) { outboundList.value.splice(index, 1); toast.success("å 餿å"); } }, }); }; // å¤çåºåº const handleOutbound = async () => { if (outboundList.value.length === 0) { toast.error("ææ åºåºæ°æ®"); return; } // æå»ºè¯·æ±æ°æ® const requestData = outboundList.value.map((item) => ({ outPutId: item.id, projectId: item.projectId, })); try { uni.showLoading({ title: "åºåºä¸...", mask: true, }); console.log("requestData", requestData); const { code, msg } = await OutboundApi.finishedOutbound(requestData); uni.hideLoading(); if (code === 200) { toast.success("åºåºæå"); // æ¸ ç©ºå表 outboundList.value = []; const { code, data, msg } = await OutboundApi.queryErpOutboundOrder(params); if (code === 200 && data) { const records = data.records || []; if (!records.length) { pagingRef.value.complete(true); } else { toast.error(msg || "åºåºå¤±è´¥"); pagingRef.value.complete(records); } } catch (error: any) { uni.hideLoading(); console.error("åºåºå¤±è´¥:", error); } else { toast.error(msg || "è·ååºåºåå表失败"); pagingRef.value.complete(true); } } catch (error) { console.error("è·ååºåºåå表失败:", error); toast.error("è·ååºåºåå表失败"); pagingRef.value.complete(true); } }; // ç¡®ä¿å ç§»é¤åæ·»å çå¬ const setupScanListener = () => { uni.$off("scanOutbound", getScanCode); // å ç§»é¤æ§ç uni.$on("scanOutbound", getScanCode); // åæ·»å æ°ç // æç´¢å¤ç const handleSearch = () => { // éæ°å è½½æ°æ® pagingRef.value.reload(); }; onMounted(() => { setupScanListener(); }); // æ¸ ç©ºæç´¢ const handleClear = () => { searchKeyword.value = ""; handleSearch(); }; onUnmounted(() => { uni.$off("scanOutbound", getScanCode); // 跳转å°ç©æè¯¦æ 页 const toMaterialDetail = (item: any) => { uni.navigateTo({ url: `/pages/outbound/material?id=${item.deliveryid}&vbillcode=${item.vbillcode}`, }); }; </script> <style lang="scss" scoped> .list_box { height: calc(100vh - 100px); .outbound-page { min-height: 100vh; background: #f3f9f8; display: flex; flex-direction: column; padding: 10rpx; } .list_content { flex: 1; overflow-y: auto; padding-bottom: 120rpx; .search-section { background: #fff; padding: 10rpx; margin-bottom: 10rpx; } .scan_box { display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; padding: 6px; background: #fff; } .list-section { padding: 0 4px; height: calc(100vh - 150rpx); } .card_bg { box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05); padding-bottom: 10px; margin-bottom: 10px; margin-left: 6px; margin-right: 6px; } .shipping-card { margin: 0 5px; } .icon_box { display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #e7f4ec99; border-radius: 50%; } .empty_tip { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100rpx 0; color: #999; align-items: center; padding: 40px 0; } .empty_text { font-size: 32rpx; margin-bottom: 20rpx; } color: #999; font-size: 14px; } .outbound_item { display: flex; align-items: center; justify-content: space-between; padding: 30rpx; margin: 20rpx; background: #fff; border-radius: 16rpx; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); .outbound_item_left { display: flex; align-items: center; flex: 1; } .outbound_item_content { flex: 1; } .outbound_item_row { display: flex; align-items: center; margin-bottom: 12rpx; font-size: 28rpx; &:last-child { margin-bottom: 0; } } .outbound_item_label { color: #666; min-width: 140rpx; flex-shrink: 0; } .outbound_item_value { color: #333; flex: 1; word-break: break-all; } .outbound_item_action { display: flex; align-items: center; justify-content: center; margin-left: 20rpx; flex-shrink: 0; } } :deep(.delete-btn) { .wd-button__content { color: #ff4444 !important; } &:active { opacity: 0.7; } } :deep(.wd-button__content) { color: #0d867f; } .outbound_footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 20rpx 30rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); z-index: 100; :deep(.wd-search__block) { border-radius: unset; } </style> src/pages/outbound/index0.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,350 @@ <template> <view class="list_box"> <CardTitle title="åºåº" :hideAction="false"> <template #action> <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button> </template> </CardTitle> <view class="list_content"> <view v-if="outboundList.length === 0" class="empty_tip"> <view class="empty_text">ææ åºåºæ°æ®</view> </view> <view v-for="(item, index) in outboundList" :key="index" class="outbound_item"> <view class="outbound_item_left"> <view class="outbound_item_content"> <view class="outbound_item_row"> <text class="outbound_item_label">ååå·ï¼</text> <text class="outbound_item_value">{{ item.contractNo || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">çäº§æ¹æ¬¡å·ï¼</text> <text class="outbound_item_value">{{ item.batchNo || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">è§æ ¼åå·ï¼</text> <text class="outbound_item_value">{{ item.model || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">ééï¼</text> <text class="outbound_item_value">{{ item.weight || "-" }} kg</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">åå®¶ï¼</text> <text class="outbound_item_value">{{ item.clienteleName || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">段é¿ï¼</text> <text class="outbound_item_value">{{ item.actuallyLength || "-" }} M</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">çäº§æ¥æï¼</text> <text class="outbound_item_value">{{ item.productionDate || "-" }}</text> </view> </view> </view> <view class="outbound_item_action"> <wd-button type="icon" icon="delete" size="small" custom-class="delete-btn" @click.stop="removeOutboundItem(index)" ></wd-button> </view> </view> </view> <view v-if="outboundList.length > 0" class="outbound_footer"> <wd-button block @click="handleOutbound"> <text class="text-[#fff]">åºåº</text> </wd-button> </view> <Scan ref="scanRef" emitName="scanOutbound" /> <wd-toast /> </view> </template> <script setup lang="ts"> import CardTitle from "@/components/card-title/index.vue"; import Scan from "@/components/scan/index.vue"; import { useToast } from "wot-design-uni"; import { dayjs } from "wot-design-uni"; import OutboundApi from "@/api/product/outbound"; const scanRef = ref(); const toast = useToast(); const outboundList = ref<any[]>([]); const projectId = ref<string | number>(""); // æ ¼å¼åæ¶é´ const formatTime = (date: Date) => { return dayjs(date).format("YYYY-MM-DD HH:mm:ss"); }; // æ«ç åè° const getScanCode = async (code: any) => { try { // 妿 code æ¯å¯¹è±¡ä¸æ code åæ®µï¼ä½¿ç¨ code.codeï¼å¦åç´æ¥ä½¿ç¨ code let scanCode = code.code || code; // 妿 scanCode æ¯å¯¹è±¡ï¼å°è¯è·åå ¶ code åæ®µ if (typeof scanCode === "object" && scanCode.code) { scanCode = scanCode.code; } // 妿 scanCode æ¯å符串ï¼ç´æ¥ä½¿ç¨ï¼å¦ææ¯å¯¹è±¡ï¼è½¬ä¸ºå符串 if (typeof scanCode !== "string") { scanCode = JSON.stringify(scanCode); } if (!scanCode) { toast.error("æ«ç å 容为空"); return; } // è§£ææ«ç æ°æ®ï¼ç°å¨äºç»´ç åªå å«id let scanData; try { scanData = JSON.parse(scanCode); } catch (e) { toast.error("äºç»´ç æ ¼å¼é误"); return; } const outPutId = scanData.id; if (!outPutId) { toast.error("äºç»´ç æ ¼å¼é误ï¼ç¼ºå°idä¿¡æ¯"); return; } // æ£æ¥æ¯å¦å·²åå¨ï¼æ ¹æ®idå¤æï¼ const exists = outboundList.value.some((item) => { const itemId = item.id; return itemId && itemId === outPutId && itemId !== "-"; }); if (exists) { toast.error("该æ¡ç å·²åå¨ï¼è¯·å¿é夿«ç "); return; } // è°ç¨æ¥å£è·åç»çº¿è¯¦ç»ä¿¡æ¯ const { data: tagData } = await OutboundApi.getTagByIdJx({ outPutId: outPutId, }); // æåæ°æ®åæ®µï¼æ ¹æ®æ¥å£è¿åçæ°æ®ç»æï¼ const parsedData = { id: tagData?.id || outPutId, contractNo: tagData?.contractno || tagData?.contractNo || "-", batchNo: tagData?.systemno || tagData?.systemNo || tagData?.batchNo || "-", model: tagData?.model || "-", weight: tagData?.actuallyweight || tagData?.actuallyWeight || tagData?.weight || "-", clienteleName: tagData?.clientelename || tagData?.clienteleName || "-", actuallyLength: tagData?.actuallylength || tagData?.actuallyLength || "-", productionDate: tagData?.producttime || tagData?.productionDate || "-", projectId: tagData?.projectid || tagData?.projectId || projectId.value || "", // ä¿çåå§æ°æ® rawData: tagData, scanCode: scanCode, }; // 妿æ¥å£è¿åçæ°æ®ä¸æ projectIdï¼ä¿åå® if (parsedData.projectId && !projectId.value) { projectId.value = parsedData.projectId; } // æ·»å å°å表 const newItem = { ...parsedData, scanTime: formatTime(new Date()), }; outboundList.value.push(newItem); toast.success("æ«ç æå"); } catch (error: any) { console.error("æ«ç å¤ç失败:", error); toast.error(error.msg || "äºç»´ç å¼å¸¸ï¼è¯·æ´æ¢äºç»´ç ï¼"); } }; // è§¦åæ«ç const openScan = () => { scanRef.value.triggerScan(); }; // å é¤é¡¹ const removeOutboundItem = (index: number) => { const item = outboundList.value[index]; const itemName = item.contractNo || item.batchNo || `第${index + 1}项`; uni.showModal({ title: "确认å é¤", content: `ç¡®å®è¦å é¤"${itemName}"åï¼`, confirmText: "å é¤", cancelText: "åæ¶", confirmColor: "#ff4444", success: (res) => { if (res.confirm) { outboundList.value.splice(index, 1); toast.success("å 餿å"); } }, }); }; // å¤çåºåº const handleOutbound = async () => { if (outboundList.value.length === 0) { toast.error("ææ åºåºæ°æ®"); return; } // æå»ºè¯·æ±æ°æ® const requestData = outboundList.value.map((item) => ({ outPutId: item.id, projectId: item.projectId, })); try { uni.showLoading({ title: "åºåºä¸...", mask: true, }); console.log("requestData", requestData); const { code, msg } = await OutboundApi.finishedOutbound(requestData); uni.hideLoading(); if (code === 200) { toast.success("åºåºæå"); // æ¸ ç©ºå表 outboundList.value = []; } else { toast.error(msg || "åºåºå¤±è´¥"); } } catch (error: any) { uni.hideLoading(); console.error("åºåºå¤±è´¥:", error); } }; // ç¡®ä¿å ç§»é¤åæ·»å çå¬ const setupScanListener = () => { uni.$off("scanOutbound", getScanCode); // å ç§»é¤æ§ç uni.$on("scanOutbound", getScanCode); // åæ·»å æ°ç }; onMounted(() => { setupScanListener(); }); onUnmounted(() => { uni.$off("scanOutbound", getScanCode); }); </script> <style lang="scss" scoped> .list_box { height: calc(100vh - 100px); background: #f3f9f8; display: flex; flex-direction: column; } .list_content { flex: 1; overflow-y: auto; padding-bottom: 120rpx; } .empty_tip { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100rpx 0; color: #999; .empty_text { font-size: 32rpx; margin-bottom: 20rpx; } } .outbound_item { display: flex; align-items: center; justify-content: space-between; padding: 30rpx; margin: 5rpx; background: #fff; border-radius: 16rpx; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); .outbound_item_left { display: flex; align-items: center; flex: 1; } .outbound_item_content { flex: 1; } .outbound_item_row { display: flex; align-items: center; margin-bottom: 12rpx; font-size: 28rpx; &:last-child { margin-bottom: 0; } } .outbound_item_label { color: #666; min-width: 140rpx; flex-shrink: 0; } .outbound_item_value { color: #333; flex: 1; word-break: break-all; } .outbound_item_action { display: flex; align-items: center; justify-content: center; margin-left: 20rpx; flex-shrink: 0; } } :deep(.delete-btn) { .wd-button__content { color: #ff4444 !important; } &:active { opacity: 0.7; } } :deep(.wd-button__content) { color: #0d867f; } .outbound_footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 20rpx 30rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); z-index: 100; } </style> src/pages/outbound/material.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,627 @@ <template> <view class="material-page"> <view class="material-header"> <CardTitle title="ç©æè¯¦æ " :hideAction="false"> <template #action> <wd-button type="icon" icon="scan" color="#0d867f" @click="openScan" style="color: #0d867f" ></wd-button> </template> </CardTitle> <!-- ç©æä¿¡æ¯ --> <view class="material-info"> <wd-card v-for="(item, index) in materialList" :key="item.materialcode || index" custom-class="info-card" > <wd-row class="info-row"> <wd-col :span="24"> <view class="flex"> <view class="icon_box"> <wd-icon name="folder" color="#0D867F"></wd-icon> </view> <text class="text-[#646874] mx-2"> ç©æåç§°: <text class="text-[#252525]">{{ item.materialname || "-" }}</text> </text> </view> </wd-col> </wd-row> <wd-row class="info-row"> <wd-col :span="24"> <view class="flex"> <view class="icon_box"> <wd-icon name="folder" color="#0D867F"></wd-icon> </view> <text class="text-[#646874] mx-2"> ç©æè§æ ¼: <text class="text-[#252525]">{{ item.materialspec || "-" }}</text> </text> </view> </wd-col> </wd-row> <wd-row class="info-row"> <wd-col :span="24"> <view class="flex"> <view class="icon_box"> <wd-icon name="folder" color="#0D867F"></wd-icon> </view> <text class="text-[#646874] mx-2"> å·²åè´§æ°é: <text class="text-[#252525]">{{ item.shippedQuantity || 0 }}</text> </text> </view> </wd-col> </wd-row> </wd-card> </view> </view> <!-- è´§ç©å表 --> <view class="list_content"> <view v-if="goodsList.length === 0" class="empty_tip"> <view class="empty_text">ææ è´§ç©æ°æ®</view> </view> <view v-for="(item, index) in goodsList" :key="index" class="outbound_item"> <view class="outbound_item_left"> <view class="outbound_item_content"> <view class="outbound_item_row"> <text class="outbound_item_label">ååå·ï¼</text> <text class="outbound_item_value">{{ item.contractNo || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">çäº§æ¹æ¬¡å·ï¼</text> <text class="outbound_item_value">{{ item.monofilamentNumber || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">è§æ ¼åå·ï¼</text> <text class="outbound_item_value">{{ item.model || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">ééï¼</text> <text class="outbound_item_value">{{ item.weight || "-" }} kg</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">åå®¶ï¼</text> <text class="outbound_item_value">{{ item.clienteleName || "-" }}</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">段é¿ï¼</text> <text class="outbound_item_value">{{ item.actuallyLength || "-" }} M</text> </view> <view class="outbound_item_row"> <text class="outbound_item_label">çäº§æ¥æï¼</text> <text class="outbound_item_value">{{ item.productionDate || "-" }}</text> </view> </view> </view> <view class="outbound_item_action"> <wd-button type="icon" icon="delete" size="small" custom-class="delete-btn" @click.stop="removeGoodsItem(index)" ></wd-button> </view> </view> </view> <!-- åºé¨æé® --> <view v-if="goodsList.length > 1" class="outbound_footer"> <wd-button block @click="handleOutbound" style="background: #0d867f"> <text class="text-[#fff]">åºåº</text> </wd-button> </view> <Scan ref="scanRef" emitName="scanMaterial" /> <wd-toast /> </view> </template> <script setup lang="ts"> import { ref, onMounted, onUnmounted } from "vue"; import { onLoad } from "@dcloudio/uni-app"; // @ts-ignore import CardTitle from "@/components/card-title/index.vue"; // @ts-ignore import Scan from "@/components/scan/index.vue"; import { useToast, dayjs } from "wot-design-uni"; import OutboundApi from "@/api/product/outbound"; const toast = useToast(); const scanRef = ref(); const materialId = ref<string>(""); const vbillcode = ref<string>(""); // ç©æä¿¡æ¯å表 const materialList = ref<any[]>([]); // è´§ç©å表 const goodsList = ref<any[]>([]); // æ ¼å¼åæ¶é´ const formatTime = (date: Date) => { return dayjs(date).format("YYYY-MM-DD HH:mm:ss"); }; // è·åç©æè¯¦æ const getMaterialDetail = async () => { if (!vbillcode.value) { toast.error("åè´§åå·ä¸è½ä¸ºç©º"); return; } try { uni.showLoading({ title: "å è½½ä¸...", mask: true, }); const { code, data, msg } = await OutboundApi.queryErpOutboundOrderDetail({ vbillcode: vbillcode.value, }); uni.hideLoading(); if (code === 200 && data) { // å°æ¥å£è¿åçæ°æ®æ å°å° materialList materialList.value = data.map((item: any) => ({ materialcode: item.materialcode, materialname: item.materialname || "-", materialspec: item.materialspec || "-", shippedQuantity: item.nnum || 0, cdeliveryid: item.cdeliveryid, cdeliverybid: item.cdeliverybid, vsrccode: item.vsrccode, })); } else { toast.error(msg || "è·åç©æè¯¦æ 失败"); materialList.value = []; } } catch (error: any) { uni.hideLoading(); console.error("è·åç©æè¯¦æ 失败:", error); toast.error(error.msg || "è·åç©æè¯¦æ 失败"); materialList.value = []; } }; // ç´æ¥æ«ç const openScan = () => { scanRef.value?.triggerScan(); }; // æ«ç åè° const getScanCode = async (code: any) => { try { // 妿 code æ¯å¯¹è±¡ä¸æ code åæ®µï¼ä½¿ç¨ code.codeï¼å¦åç´æ¥ä½¿ç¨ code let scanCode = code.code || code; // 妿 scanCode æ¯å¯¹è±¡ï¼å°è¯è·åå ¶ code åæ®µ if (typeof scanCode === "object" && scanCode.code) { scanCode = scanCode.code; } // 妿 scanCode æ¯å符串ï¼ç´æ¥ä½¿ç¨ï¼å¦ææ¯å¯¹è±¡ï¼è½¬ä¸ºå符串 if (typeof scanCode !== "string") { scanCode = JSON.stringify(scanCode); } if (!scanCode) { toast.error("æ«ç å 容为空"); return; } // è§£ææ«ç æ°æ®ï¼ç°å¨äºç»´ç åªå å«id let scanData; try { scanData = JSON.parse(scanCode); } catch (e) { toast.error("äºç»´ç æ ¼å¼é误"); return; } const outPutId = scanData.id; if (!outPutId) { toast.error("äºç»´ç æ ¼å¼é误ï¼ç¼ºå°idä¿¡æ¯"); return; } // æ£æ¥æ¯å¦å·²åå¨ï¼æ ¹æ®idå¤æï¼ const exists = goodsList.value.some((item) => { const itemId = item.id; return itemId && itemId === outPutId && itemId !== "-"; }); if (exists) { toast.error("该æ¡ç å·²åå¨ï¼è¯·å¿é夿«ç "); return; } // è°ç¨æ¥å£è·åç»çº¿/æä¸è¯¦ç»ä¿¡æ¯ï¼å«åºåºç¶æï¼ const { data } = await OutboundApi.getTagByIdAll({ outPutId: outPutId, }); const list = data || []; if (!list.length) { toast.error("æªæ¥è¯¢å°æ¡ç ä¿¡æ¯"); return; } const tagData = list[0]; // å·²åºåºæ ¡éª if (tagData?.state === "å·²åºåº") { toast.error("该æ¡ç å·²åºåºï¼æ æ³éå¤åºåº"); return; } // æåæ°æ®åæ®µï¼æ ¹æ®æ¥å£è¿åçæ°æ®ç»æï¼ const parsedData = { id: tagData?.id || outPutId, contractNo: tagData?.contractno || tagData?.contractNo || "-", monofilamentNumber: tagData?.monofilamentnumber || tagData?.monofilamentNumber || tagData?.systemno || tagData?.systemNo || "-", model: tagData?.model || "-", weight: tagData?.actuallylength || tagData?.actuallyLength || tagData?.weight || "-", clienteleName: tagData?.clientelename || tagData?.clienteleName || "-", actuallyLength: tagData?.actuallylength || tagData?.actuallyLength || "-", productionDate: tagData?.producttime || tagData?.productionDate || "-", type: tagData?.type || "", devicemodel: tagData?.devicemodel || "", state: tagData?.state || "", projectId: tagData?.projectid || tagData?.projectId || "", productuser: tagData?.productuser || "", // ä¿çåå§æ°æ® rawData: tagData, scanCode: scanCode, }; // æ·»å å°å表 const newItem = { ...parsedData, scanTime: formatTime(new Date()), }; goodsList.value.push(newItem); toast.success("æ«ç æå"); } catch (error: any) { console.error("æ«ç å¤ç失败:", error); toast.error(error.msg || "äºç»´ç å¼å¸¸ï¼è¯·æ´æ¢äºç»´ç ï¼"); } }; // å é¤è´§ç©é¡¹ const removeGoodsItem = (index: number) => { const item = goodsList.value[index]; const itemName = item.contractNo || item.monofilamentNumber || `第${index + 1}项`; uni.showModal({ title: "确认å é¤", content: `ç¡®å®è¦å é¤"${itemName}"åï¼`, confirmText: "å é¤", cancelText: "åæ¶", confirmColor: "#ff4444", success: (res) => { if (res.confirm) { goodsList.value.splice(index, 1); toast.success("å 餿å"); } }, }); }; // ç»è®¡ç©ææ°éåç±»åè§æ ¼ const getMaterialStatistics = () => { const statistics: Record<string, { type: string; model: string; count: number }> = {}; goodsList.value.forEach((item) => { const type = item.type || "æªç¥ç±»å"; const model = item.model || "æªç¥è§æ ¼"; const key = `${type}_${model}`; if (!statistics[key]) { statistics[key] = { type, model, count: 0, }; } statistics[key].count++; }); return Object.values(statistics); }; // å¤çåºåº const handleOutbound = async () => { if (goodsList.value.length === 0) { toast.error("ææ è´§ç©æ°æ®"); return; } // ç»è®¡ç©ææ°éåç±»åè§æ ¼ const statistics = getMaterialStatistics(); // æå»ºç»è®¡ä¿¡æ¯ææ¬ let statisticsText = "åºåºç©æç»è®¡ï¼\n\n"; statistics.forEach((item, index) => { statisticsText += `${index + 1}. ${item.type} - ${item.model}ï¼${item.count}ä»¶\n`; }); statisticsText += `\næ»è®¡ï¼${goodsList.value.length}ä»¶\n\n确认åºåºåï¼`; // æ¾ç¤ºç¡®è®¤å¼¹æ¡ uni.showModal({ title: "确认åºåº", content: statisticsText, confirmText: "确认", cancelText: "åæ¶", confirmColor: "#0d867f", success: async (res) => { if (res.confirm) { // æå»ºè¯·æ±æ°æ® const requestData = goodsList.value.map((item) => ({ outPutId: item.id, projectId: item.projectId || "", })); try { uni.showLoading({ title: "åºåºä¸...", mask: true, }); console.log("requestData", requestData); const { code, msg } = await OutboundApi.finishedOutbound(requestData); uni.hideLoading(); if (code === 200) { toast.success("åºåºæå"); // æ¸ ç©ºå表 goodsList.value = []; // è¿åä¸ä¸é¡µæå·æ°ç©æè¯¦æ setTimeout(() => { uni.navigateBack(); }, 1500); } else { toast.error(msg || "åºåºå¤±è´¥"); } } catch (error: any) { uni.hideLoading(); console.error("åºåºå¤±è´¥:", error); toast.error(error.msg || "åºåºå¤±è´¥"); } } }, }); }; // 设置æ«ç çå¬ const setupScanListener = () => { uni.$off("scanMaterial", getScanCode); uni.$on("scanMaterial", getScanCode); }; onLoad((options: any) => { materialId.value = options.id || ""; vbillcode.value = options.vbillcode || ""; getMaterialDetail(); }); onMounted(() => { setupScanListener(); }); onUnmounted(() => { uni.$off("scanMaterial", getScanCode); }); </script> <style lang="scss" scoped> .material-page { min-height: 100vh; background: #f3f9f8; padding-bottom: 200rpx; display: flex; flex-direction: column; } .material-header { position: sticky; top: 0; z-index: 10; background: #f3f9f8; padding-bottom: 10rpx; } .material-info { padding: 20rpx; } .info-card { box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05); margin-bottom: 20rpx; margin-left: 6px; margin-right: 6px; padding: 10px 18px; background: #f3f9f8; &:last-child { margin-bottom: 0; } } .info-row { margin-bottom: 20rpx; &:last-child { margin-bottom: 0; } } .icon_box { display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #e7f4ec99; border-radius: 50%; } .list_content { flex: 1; overflow-y: auto; padding-bottom: 120rpx; } .empty_tip { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100rpx 0; color: #999; .empty_text { font-size: 32rpx; margin-bottom: 20rpx; } } .outbound_item { display: flex; align-items: center; justify-content: space-between; padding: 30rpx; margin: 30rpx; background: #fff; border-radius: 16rpx; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); .outbound_item_left { display: flex; align-items: center; flex: 1; } .outbound_item_content { flex: 1; } .outbound_item_row { display: flex; align-items: center; margin-bottom: 12rpx; font-size: 28rpx; &:last-child { margin-bottom: 0; } } .outbound_item_label { color: #666; min-width: 140rpx; flex-shrink: 0; } .outbound_item_value { color: #333; flex: 1; word-break: break-all; } .outbound_item_action { display: flex; align-items: center; justify-content: center; margin-left: 20rpx; flex-shrink: 0; } } :deep(.delete-btn) { .wd-button__content { color: #ff4444 !important; } &:active { opacity: 0.7; } } .outbound_footer { position: fixed; bottom: 20rpx; left: 0; right: 0; padding: 0 30rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); z-index: 100; background: transparent; } .shipping-btn-wrapper { position: fixed; bottom: 0; left: 0; right: 0; padding: 20rpx 30rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); background: #fff; z-index: 100; box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05); } .shipping-dialog { background: #fff; border-radius: 24rpx 24rpx 0 0; padding-bottom: env(safe-area-inset-bottom); } .dialog-header { display: flex; justify-content: space-between; align-items: center; padding: 32rpx; border-bottom: 1px solid #e6e6e6; } .dialog-title { font-size: 32rpx; font-weight: 500; color: #333; } .close-icon { font-size: 40rpx; color: #999; padding: 8rpx; } .dialog-content { padding: 32rpx; } .scan-tip { text-align: center; color: #666; font-size: 28rpx; padding: 40rpx 0; } </style>