| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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" v-if="currentMaterial"> |
| | | <wd-card custom-class="info-card"> |
| | | <view class="info-compact"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <view class="info-text"> |
| | | <view class="info-line"> |
| | | <text class="label">ç©æåç§°ï¼</text> |
| | | <text class="value">{{ currentMaterial.materialname || "-" }}</text> |
| | | </view> |
| | | <view class="info-line"> |
| | | <text class="label">ç©æè§æ ¼ï¼</text> |
| | | <text class="value">{{ currentMaterial.materialspec || "-" }}</text> |
| | | </view> |
| | | <view class="info-line"> |
| | | <text class="label">å¾
è´§æ°éï¼</text> |
| | | <text class="value">{{ currentMaterial.shippedQuantity || 0 }} å¨</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </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 > 0" 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 vsrccode = ref<string>(""); |
| | | |
| | | // å½åç©æä¿¡æ¯ |
| | | const currentMaterial = ref<any | null>(null); |
| | | |
| | | // è´§ç©å表 |
| | | const goodsList = ref<any[]>([]); |
| | | |
| | | // æ ¼å¼åæ¶é´ |
| | | const formatTime = (date: Date) => { |
| | | return dayjs(date).format("YYYY-MM-DD HH:mm:ss"); |
| | | }; |
| | | |
| | | // ç´æ¥æ«ç |
| | | 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; |
| | | } |
| | | |
| | | // ååå·æ ¡éªï¼æ£æ¥æ«ç çååå·æ¯å¦çäºåè´§åçvsrccode |
| | | const scannedContractNo = tagData?.contractno || ""; |
| | | if (scannedContractNo && vsrccode.value && scannedContractNo !== vsrccode.value) { |
| | | toast.error(`ååå·"${scannedContractNo}"ä¸å½ååè´§å为"${vsrccode.value}"ä¸å¹é
`); |
| | | 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 || "", |
| | | })); |
| | | |
| | | // æåºåºåæç»æå»ºè¯·æ±ä½ |
| | | const payload = { |
| | | finishedOutDtos: requestData, |
| | | cdeliveryid: currentMaterial.value?.cdeliveryid || "", |
| | | cdeliverybid: currentMaterial.value?.cdeliverybid || "", |
| | | materialcode: currentMaterial.value?.materialcode || "", |
| | | }; |
| | | |
| | | try { |
| | | uni.showLoading({ |
| | | title: "åºåºä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | console.log("request payload", payload); |
| | | const { code, msg } = await OutboundApi.finishedOutboundByOutboundId(payload); |
| | | |
| | | 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) => { |
| | | // ä»è·¯ç±åæ°ä¸è·åç©æåºæ¬ä¿¡æ¯ãåºåºåä¿¡æ¯å vsrccode |
| | | currentMaterial.value = { |
| | | materialname: options.materialname || "-", |
| | | materialspec: options.materialspec || "-", |
| | | shippedQuantity: Number(options.shippedQuantity || 0), |
| | | cdeliveryid: options.cdeliveryid || "", |
| | | cdeliverybid: options.cdeliverybid || "", |
| | | materialcode: options.materialcode || "", |
| | | }; |
| | | vsrccode.value = options.vsrccode || ""; |
| | | }); |
| | | |
| | | 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: 8rpx; |
| | | } |
| | | |
| | | .material-info { |
| | | /* åå°ä¸ä¸çç½ï¼è®©å¡çæ´è´´è¿å¯¼èªæ */ |
| | | padding: 4rpx 16rpx 0 16rpx; |
| | | } |
| | | |
| | | .info-card { |
| | | border-radius: 16rpx; |
| | | background: linear-gradient(135deg, #ffffff, #f2fbfa); |
| | | /* åå¼±é´å½±ï¼æ´ä½æ´æå¹³ç´§å */ |
| | | box-shadow: 0 4rpx 10rpx rgba(3, 112, 102, 0.05); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .info-row { |
| | | /* æ¶ç´§æ¯è¡é«åº¦ */ |
| | | padding: 6rpx 0; |
| | | } |
| | | |
| | | .info-compact { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 8rpx 12rpx; |
| | | } |
| | | |
| | | .info-text { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | row-gap: 4rpx; |
| | | } |
| | | |
| | | .info-line { |
| | | display: flex; |
| | | font-size: 24rpx; |
| | | margin-right: 24rpx; |
| | | } |
| | | |
| | | .info-line .label { |
| | | color: #646874; |
| | | } |
| | | |
| | | .info-line .value { |
| | | color: #252525; |
| | | max-width: 260rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .icon_box { |
| | | /* 缩å°å¾æ 容å¨å°ºå¯¸ */ |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | border-radius: 10rpx; |
| | | background: rgba(13, 134, 127, 0.08); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 8rpx; |
| | | } |
| | | |
| | | .list_content { |
| | | flex: 1; |
| | | overflow-y: auto; |
| | | padding: 24rpx; |
| | | padding-bottom: 220rpx; |
| | | } |
| | | |
| | | .empty_tip { |
| | | margin-top: 120rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .empty_text { |
| | | color: #999; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .outbound_item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | padding: 20rpx 24rpx; |
| | | background: #fff; |
| | | border-radius: 16rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .outbound_item_left { |
| | | flex: 1; |
| | | } |
| | | |
| | | .outbound_item_content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 6rpx; |
| | | } |
| | | |
| | | .outbound_item_row { |
| | | display: flex; |
| | | font-size: 26rpx; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .outbound_item_label { |
| | | color: #646874; |
| | | min-width: 160rpx; |
| | | } |
| | | |
| | | .outbound_item_value { |
| | | color: #252525; |
| | | } |
| | | |
| | | .outbound_item_action { |
| | | margin-left: 16rpx; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .delete-btn { |
| | | --wd-button-default-border-color: #f5f5f5; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | </style> |