|
|
@@ -0,0 +1,1714 @@
|
|
|
+<template>
|
|
|
+ <div class="container">
|
|
|
+ <van-nav-bar
|
|
|
+ title="退货登记"
|
|
|
+ left-arrow
|
|
|
+ @click-left="goBack"
|
|
|
+ @click-right="init"
|
|
|
+ >
|
|
|
+ <template #left>
|
|
|
+ <van-icon name="arrow-left" size="25" />
|
|
|
+ <div class="left-btn">返回</div>
|
|
|
+ </template>
|
|
|
+ <template #right>
|
|
|
+ <div class="nav-right right-btn">重置</div>
|
|
|
+ </template>
|
|
|
+ </van-nav-bar>
|
|
|
+
|
|
|
+ <div class="content">
|
|
|
+ <div v-if="showInitialPage" class="init-container">
|
|
|
+ <div class="content-tips">
|
|
|
+ <div style="flex: 1">
|
|
|
+ <van-notice-bar left-icon="volume-o">请扫描退回单号</van-notice-bar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="scan-returned-content">
|
|
|
+ <div class="input-group">
|
|
|
+ <van-field
|
|
|
+ ref="scan-express-no-input"
|
|
|
+ autofocus
|
|
|
+ v-model="expressNo"
|
|
|
+ autocomplete="off"
|
|
|
+ placeholder="输入快递单号"
|
|
|
+ clearable
|
|
|
+ @keydown.enter="inputExpressNo"
|
|
|
+ >
|
|
|
+ </van-field>
|
|
|
+ </div>
|
|
|
+ <div class="button-group">
|
|
|
+ <van-button
|
|
|
+ @click="inputExpressNo"
|
|
|
+ style="width: 100%"
|
|
|
+ type="primary"
|
|
|
+ class="confirm-btn"
|
|
|
+ >确认
|
|
|
+ </van-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else>
|
|
|
+ <van-tabs>
|
|
|
+ <van-tab title="退货信息">
|
|
|
+ <div>
|
|
|
+ <div class="content-tips">
|
|
|
+ <div style="flex: 1">
|
|
|
+ <van-notice-bar color="#1989fa" background="#ecf9ff">{{ ownerQualityInspection }}</van-notice-bar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <van-cell-group inset style="margin-top: 10px">
|
|
|
+ <van-field
|
|
|
+ v-model="params.returnNo"
|
|
|
+ label="快递单号"
|
|
|
+ readonly
|
|
|
+ placeholder="请输入快递单号"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ readonly
|
|
|
+ clickable
|
|
|
+ label="承运商"
|
|
|
+ placeholder="选择承运商"
|
|
|
+ v-model="params.logisticsName"
|
|
|
+ @click="logisticPickerShow = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="logisticPickerShow"
|
|
|
+ position="bottom"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ :columns="logistics"
|
|
|
+ @cancel="logisticPickerShow = false"
|
|
|
+ @confirm="selectedLogistic"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ readonly
|
|
|
+ clickable
|
|
|
+ label="仓库"
|
|
|
+ placeholder="选择仓库"
|
|
|
+ :model-value="getWarehouseName(warehouse)"
|
|
|
+ v-model="params.warehouseCode"
|
|
|
+ @click="warehousePickerShow = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="warehousePickerShow"
|
|
|
+ position="bottom"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ :columns="warehouses"
|
|
|
+ @cancel="warehousePickerShow = false"
|
|
|
+ @confirm="selectedWarehouse"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ readonly
|
|
|
+ clickable
|
|
|
+ label="货主"
|
|
|
+ placeholder="选择货主"
|
|
|
+ :model-value="getOwnerName(params.ownerCode)"
|
|
|
+ @click="ownerPickerShow = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="ownerPickerShow"
|
|
|
+ position="bottom"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ :columns="owners"
|
|
|
+ @cancel="ownerPickerShow = false"
|
|
|
+ @confirm="selectedOwner"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ readonly
|
|
|
+ clickable
|
|
|
+ label="店铺"
|
|
|
+ placeholder="选择店铺"
|
|
|
+ v-model="params.storeName"
|
|
|
+ @click="storePickerShow = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="storePickerShow"
|
|
|
+ position="bottom"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ :columns="storeOptions"
|
|
|
+ @cancel="storePickerShow = false"
|
|
|
+ @confirm="selectedStore"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="params.orderUpstream"
|
|
|
+ label="上游平台"
|
|
|
+ placeholder="上游平台"
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="params.arrivalPayment"
|
|
|
+ label="到付费用"
|
|
|
+ placeholder="到付费用"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="params.remark"
|
|
|
+ rows="1"
|
|
|
+ autosize
|
|
|
+ label="备注"
|
|
|
+ type="textarea"
|
|
|
+ placeholder="备注"
|
|
|
+ />
|
|
|
+ </van-cell-group>
|
|
|
+
|
|
|
+ <van-cell-group inset style="margin-top: 10px">
|
|
|
+ <van-field
|
|
|
+ :model-value="params.originalNo"
|
|
|
+ label="是否原单"
|
|
|
+ readonly
|
|
|
+ placeholder="是否原单"
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="params.upstreamNo"
|
|
|
+ label="客户订单号"
|
|
|
+ placeholder="客户订单号"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="params.asnNo"
|
|
|
+ label="ASN单号"
|
|
|
+ readonly
|
|
|
+ placeholder="ASN单号"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="params.buyerName"
|
|
|
+ label="客户姓名"
|
|
|
+ readonly
|
|
|
+ placeholder="客户姓名"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="params.buyerPhone"
|
|
|
+ label="电话号码"
|
|
|
+ readonly
|
|
|
+ placeholder="电话号码"
|
|
|
+ />
|
|
|
+ </van-cell-group>
|
|
|
+
|
|
|
+ <van-cell-group inset style="margin-top: 20px; margin-bottom: 50px">
|
|
|
+ <van-button type="primary" block @click="submit">提交</van-button>
|
|
|
+ </van-cell-group>
|
|
|
+ </van-tab>
|
|
|
+ <van-tab title="商品信息" class="returned-detail-list">
|
|
|
+ <div>
|
|
|
+ <div class="content-tips">
|
|
|
+ <div style="flex: 1">
|
|
|
+ <van-notice-bar color="#1989fa" background="#ecf9ff">{{ ownerQualityInspection }}</van-notice-bar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template v-if="!params.details || params.details.length === 0">
|
|
|
+ <van-empty description="暂无信息请进行录入" />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-for="(item, index) in params.details">
|
|
|
+ <div class="card-div">
|
|
|
+ <div class="card-div-content">
|
|
|
+ <div class="info-row">
|
|
|
+ <div class="info-label">sku</div>
|
|
|
+ <div class="info-value">{{ item.barCode }}</div>
|
|
|
+ <div class="info-label">质量状态</div>
|
|
|
+ <div class="info-value">
|
|
|
+ <van-tag :color="getTagColor(item.qualityStatus)">
|
|
|
+ {{ item.qualityStatus }}
|
|
|
+ </van-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="info-row">
|
|
|
+ <div class="info-label">商品编号</div>
|
|
|
+ <div class="info-value">{{ item.barCode }}</div>
|
|
|
+ <div class="info-label">商品名称</div>
|
|
|
+ <div class="info-value">
|
|
|
+ <van-text-ellipsis
|
|
|
+ :content="item.tradeName"
|
|
|
+ rows="1"
|
|
|
+ expand-text="展开"
|
|
|
+ collapse-text="收起"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="info-row">
|
|
|
+ <div class="info-label">生产日期</div>
|
|
|
+ <div class="info-value">
|
|
|
+ {{ item.manufactureTime }}
|
|
|
+ </div>
|
|
|
+ <div class="info-label">失效日期</div>
|
|
|
+ <div class="info-value">{{ item.validityTime }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="info-row">
|
|
|
+ <div class="info-label">批次号</div>
|
|
|
+ <div class="info-value">{{ item.batchNumber }}</div>
|
|
|
+ <div class="info-label">数量</div>
|
|
|
+ <div class="info-value">{{ item.number }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-div-footer">
|
|
|
+ <div class="product-description">
|
|
|
+ {{ item.remark }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-div-footer-images">
|
|
|
+ <van-image
|
|
|
+ width="100"
|
|
|
+ height="100"
|
|
|
+ src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="card-div-footer-options">
|
|
|
+ <div class="options-row">
|
|
|
+ <div class="info-value">
|
|
|
+ <van-button
|
|
|
+ size="mini"
|
|
|
+ type="danger"
|
|
|
+ block
|
|
|
+ plain
|
|
|
+ @click="removeDetails(index)"
|
|
|
+ >删除
|
|
|
+ </van-button>
|
|
|
+ </div>
|
|
|
+ <div class="info-value">
|
|
|
+ <van-button
|
|
|
+ size="mini"
|
|
|
+ type="default"
|
|
|
+ block
|
|
|
+ plain
|
|
|
+ @click="editDetails(index)"
|
|
|
+ >编辑
|
|
|
+ </van-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <van-floating-bubble
|
|
|
+ axis="xy"
|
|
|
+ icon="add"
|
|
|
+ magnetic="x"
|
|
|
+ @click="showScancode"
|
|
|
+ />
|
|
|
+ </van-tab>
|
|
|
+
|
|
|
+ <van-dialog
|
|
|
+ v-model:show="scancodeDialog"
|
|
|
+ title="扫描条码"
|
|
|
+ @open="openScanCode"
|
|
|
+ @confirm="showQualityStatus"
|
|
|
+ show-cancel-button
|
|
|
+ >
|
|
|
+ <van-field
|
|
|
+ ref="scancodeInputRef"
|
|
|
+ v-model="scancode"
|
|
|
+ label="商品条码"
|
|
|
+ autocomplete="off"
|
|
|
+ @keyup.enter="showQualityStatus"
|
|
|
+ placeholder="商品条码"
|
|
|
+ />
|
|
|
+ </van-dialog>
|
|
|
+
|
|
|
+ <van-dialog
|
|
|
+ v-model:show="qualityStatusDialog"
|
|
|
+ title="质量状态"
|
|
|
+ @confirm="queryBarcode"
|
|
|
+ show-cancel-button
|
|
|
+ >
|
|
|
+ <van-radio-group v-model="qualityStatus">
|
|
|
+ <template v-for="(item, index) in qualityStatusOptions">
|
|
|
+ <van-cell
|
|
|
+ :title="item.text"
|
|
|
+ clickable
|
|
|
+ @click="qualityStatus = item.value"
|
|
|
+ >
|
|
|
+ <template #right-icon>
|
|
|
+ <van-radio :name="item.value" />
|
|
|
+ </template>
|
|
|
+ </van-cell>
|
|
|
+ </template>
|
|
|
+ </van-radio-group>
|
|
|
+ </van-dialog>
|
|
|
+
|
|
|
+ <van-dialog
|
|
|
+ v-model:show="returnedDetailDialog"
|
|
|
+ title="商品详情"
|
|
|
+ show-cancel-button
|
|
|
+ @confirm="addDetails"
|
|
|
+ >
|
|
|
+ <div style="max-height: 70vh; overflow-y: auto">
|
|
|
+ <van-field
|
|
|
+ v-model="selectDetail.sku"
|
|
|
+ placeholder="SKU"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ readonly
|
|
|
+ v-model="selectDetail.barCode"
|
|
|
+ placeholder="商品条码"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="selectDetail.tradeName"
|
|
|
+ placeholder="商品名称"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ readonly
|
|
|
+ clickable
|
|
|
+ label="质量状态"
|
|
|
+ placeholder="质量状态"
|
|
|
+ v-model="selectDetail.qualityStatus"
|
|
|
+ @click="selectedDetailQualityStatus = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="selectedDetailQualityStatus"
|
|
|
+ position="bottom"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ :columns="qualityStatusOptions"
|
|
|
+ @cancel="selectedDetailQualityStatus = false"
|
|
|
+ @confirm="selectedDetailQualityStatusFunc"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ is-link
|
|
|
+ readonly
|
|
|
+ name="datePicker"
|
|
|
+ label="生产日期"
|
|
|
+ :placeholder="
|
|
|
+ selectDetail.manufactureTime ? '' : '请选择生产日期'
|
|
|
+ "
|
|
|
+ :model-value="formatDateDisplay(selectDetail.manufactureTime)"
|
|
|
+ @click="showManufactureTime = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="showManufactureTime"
|
|
|
+ destroy-on-close
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-date-picker
|
|
|
+ :model-value="parseDateValue(selectDetail.manufactureTime)"
|
|
|
+ @confirm="manufactureTimeConfirm"
|
|
|
+ @cancel="showManufactureTime = false"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ :model-value="formatDateDisplay(selectDetail.validityTime)"
|
|
|
+ is-link
|
|
|
+ readonly
|
|
|
+ name="datePicker"
|
|
|
+ label="失效日期"
|
|
|
+ :placeholder="selectDetail.validityTime ? '' : '请选择失效日期'"
|
|
|
+ @click="showValidityTime = true"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-model:show="showValidityTime"
|
|
|
+ destroy-on-close
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-date-picker
|
|
|
+ :model-value="parseDateValue(selectDetail.validityTime)"
|
|
|
+ @confirm="validityTimeConfirm"
|
|
|
+ @cancel="showValidityTime = false"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ autocomplete="off"
|
|
|
+ v-model="selectDetail.batchNumber"
|
|
|
+ placeholder="批次号"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="selectDetail.number"
|
|
|
+ type="digit"
|
|
|
+ placeholder="数量"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="selectDetail.remark"
|
|
|
+ label="备注"
|
|
|
+ placeholder="备注"
|
|
|
+ label-align="top"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-row>
|
|
|
+ <template
|
|
|
+ v-if="
|
|
|
+ selectDetail.boxPhotos && selectDetail.boxPhotos.length > 0
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template v-for="(item, index) in selectDetail.boxPhotos">
|
|
|
+ <van-col span="4">
|
|
|
+ <van-image
|
|
|
+ :key="`box-photos-${index}`"
|
|
|
+ width="100"
|
|
|
+ height="100"
|
|
|
+ :src="getImageUrl(item, '外箱图')"
|
|
|
+ />
|
|
|
+ </van-col>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </van-row>
|
|
|
+ <van-row>
|
|
|
+ <template
|
|
|
+ v-if="
|
|
|
+ selectDetail.productPhotos &&
|
|
|
+ selectDetail.productPhotos.length > 0
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template v-for="(item, index) in selectDetail.productPhotos">
|
|
|
+ <van-col span="12">
|
|
|
+ <van-image
|
|
|
+ :key="`product-photos-${index}`"
|
|
|
+ width="100"
|
|
|
+ height="100"
|
|
|
+ :src="getImageUrl(item, '内物图')"
|
|
|
+ />
|
|
|
+ </van-col>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </van-row>
|
|
|
+ <van-row>
|
|
|
+ <van-col span="12">
|
|
|
+ <van-button
|
|
|
+ type="primary"
|
|
|
+ block
|
|
|
+ @click="invokeCameraToCapture('外箱图')"
|
|
|
+ >外箱图录入
|
|
|
+ </van-button>
|
|
|
+ </van-col>
|
|
|
+ <van-col span="12">
|
|
|
+ <van-button
|
|
|
+ type="primary"
|
|
|
+ block
|
|
|
+ @click="invokeCameraToCapture('内物图')"
|
|
|
+ >内物图录入
|
|
|
+ </van-button>
|
|
|
+ </van-col>
|
|
|
+ </van-row>
|
|
|
+ <input
|
|
|
+ type="file"
|
|
|
+ id="outer-carton-box-input"
|
|
|
+ capture="user"
|
|
|
+ accept="image/*"
|
|
|
+ hidden
|
|
|
+ :onchange="outerCartonInput"
|
|
|
+ />
|
|
|
+
|
|
|
+ <input
|
|
|
+ type="file"
|
|
|
+ id="inner-contents-input"
|
|
|
+ capture="user"
|
|
|
+ accept="image/*"
|
|
|
+ hidden
|
|
|
+ :onchange="innerContentsInput"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </van-dialog>
|
|
|
+ </van-tabs>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <van-popup
|
|
|
+ v-model:show="showOwnerSelect"
|
|
|
+ destroy-on-close
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ :model-value="getOwnerName(params.ownerCode)"
|
|
|
+ :columns="ownerSelectedOptions"
|
|
|
+ @cancel="showOwnerSelect = false"
|
|
|
+ @confirm="selectOwner"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, nextTick, onMounted } from 'vue'
|
|
|
+import {
|
|
|
+ showFailToast,
|
|
|
+ showSuccessToast,
|
|
|
+ showNotify,
|
|
|
+ showLoadingToast,
|
|
|
+ closeToast,
|
|
|
+} from 'vant'
|
|
|
+import { getHeader, goBack } from '@/utils/android'
|
|
|
+import {
|
|
|
+ deleteDetails, getQualityInspection, getQualityInspectionBy,
|
|
|
+ getQualityStatus,
|
|
|
+ getReturnedByExpress,
|
|
|
+ getTagColorBy,
|
|
|
+ listAsn,
|
|
|
+ matchAsnBy,
|
|
|
+ matchCarrierCode,
|
|
|
+ matchOrderBy,
|
|
|
+ register,
|
|
|
+ searchBarcode,
|
|
|
+ searchOwnerBarcode,
|
|
|
+ shops,
|
|
|
+ validateDate
|
|
|
+} from '@/api/returned/index.ts'
|
|
|
+import { getOwner, getWarehouse } from '@/api/basic/index.ts'
|
|
|
+import { useStore } from '@/store/modules/user'
|
|
|
+import { getStatus } from '@/utils/returned.ts'
|
|
|
+
|
|
|
+try {
|
|
|
+ getHeader()
|
|
|
+} catch (error) {
|
|
|
+ console.log(error)
|
|
|
+}
|
|
|
+
|
|
|
+const showInitialPage = ref(true)
|
|
|
+const expressNo = ref(null)
|
|
|
+const warehouse = ref(useStore.warehouse)
|
|
|
+// 当前选择的货主
|
|
|
+
|
|
|
+const title = computed(() => {
|
|
|
+ if (expressNo.value && showInitialPage.value === false) {
|
|
|
+ return '退货登记:' + params.value.returnNo
|
|
|
+ }
|
|
|
+ return '退货登记'
|
|
|
+})
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const qualityStatusOptions = ref([])
|
|
|
+// 货主
|
|
|
+const owners = ref([])
|
|
|
+// 仓库
|
|
|
+const warehouses = ref([])
|
|
|
+// 承运商
|
|
|
+const logistics = ref([])
|
|
|
+// 店铺
|
|
|
+const storeOptions = ref([])
|
|
|
+// 选中相关信息的下标
|
|
|
+const selectDetailIndex = ref(-1)
|
|
|
+
|
|
|
+const ownerMap = ref({})
|
|
|
+
|
|
|
+const warehousesMap = ref({})
|
|
|
+
|
|
|
+const asnList = ref({})
|
|
|
+// 外箱图
|
|
|
+const boxFiles = ref([])
|
|
|
+// 内物图
|
|
|
+const productFiles = ref([])
|
|
|
+onMounted(() => {
|
|
|
+ getOwner().then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ owners.value = data.map((item) => {
|
|
|
+ return { value: item.code, text: item.name }
|
|
|
+ })
|
|
|
+ const map = {}
|
|
|
+ data.forEach((item) => {
|
|
|
+ map[item.code] = item.name
|
|
|
+ })
|
|
|
+ ownerMap.value = map
|
|
|
+ })
|
|
|
+ getQualityStatus().then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ qualityStatusOptions.value = data.map((item) => {
|
|
|
+ return { value: item, text: item }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ getWarehouse().then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ warehouses.value = data.map((item) => {
|
|
|
+ return { value: item.code, text: item.name }
|
|
|
+ })
|
|
|
+ const map = {}
|
|
|
+ data.forEach((item) => {
|
|
|
+ map[item.code] = item.name
|
|
|
+ })
|
|
|
+ warehousesMap.value = map
|
|
|
+ })
|
|
|
+ try {
|
|
|
+ getHeader()
|
|
|
+ } catch (error) {
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 获取对应的店铺信息
|
|
|
+async function getStoreOptionsBy(owner) {
|
|
|
+ const results = await shops(owner)
|
|
|
+ const { data } = results
|
|
|
+ if (!data || data.length === 0) {
|
|
|
+ storeOptions.value = []
|
|
|
+ return
|
|
|
+ }
|
|
|
+ storeOptions.value = data.map((item) => {
|
|
|
+ return { value: item, text: item }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const qualityInspection =ref(null)
|
|
|
+
|
|
|
+// 初始化质检状态
|
|
|
+function initQualityInspection(owner){
|
|
|
+ getQualityInspection(owner).then(res=>{
|
|
|
+ qualityInspection.value = res.data
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const ownerQualityInspection = computed(() => {
|
|
|
+ return getQualityInspectionBy(qualityInspection.value)
|
|
|
+})
|
|
|
+
|
|
|
+// 扫描商品条码
|
|
|
+const scancode = ref('')
|
|
|
+// 商品质量状态
|
|
|
+const qualityStatus = ref('')
|
|
|
+// 提交的商品信息
|
|
|
+const params = ref({
|
|
|
+ id: null,
|
|
|
+ returnNo: null,
|
|
|
+ warehouseCode: null,
|
|
|
+ logisticsName: null,
|
|
|
+ storeName: null,
|
|
|
+ upstreamNo: null,
|
|
|
+ ownerCode: null,
|
|
|
+ orderUpstream: null,
|
|
|
+ orderUpSteam: null,
|
|
|
+ arrivalPayment: null,
|
|
|
+ buyerName: null,
|
|
|
+ asnNo: null,
|
|
|
+ buyerPhone: null,
|
|
|
+ originalNo: null,
|
|
|
+ remark: null,
|
|
|
+ details: [],
|
|
|
+})
|
|
|
+
|
|
|
+// 商品信息
|
|
|
+const selectDetail = ref({
|
|
|
+ id: null,
|
|
|
+ rejectHeadId: null,
|
|
|
+ rejectPushTaskNo: null,
|
|
|
+ status: null,
|
|
|
+ detailStatus: null,
|
|
|
+ isGenuine: null,
|
|
|
+ qualityMark: null,
|
|
|
+ sku: '',
|
|
|
+ barCode: '',
|
|
|
+ tradeName: '',
|
|
|
+ qualityStatus: '',
|
|
|
+ manufactureTime: '',
|
|
|
+ validityTime: '',
|
|
|
+ batchNumber: '',
|
|
|
+ remark: '',
|
|
|
+ asnNo: null,
|
|
|
+ number: 0,
|
|
|
+ warehouse: null,
|
|
|
+ warehouseBin: null,
|
|
|
+ boxPhotos: [],
|
|
|
+ productPhotos: [],
|
|
|
+ files: null,
|
|
|
+ pieceTag: null,
|
|
|
+ createTime: null,
|
|
|
+ creatorId: null,
|
|
|
+ updateTime: null,
|
|
|
+ updaterId: null,
|
|
|
+ deleteTime: null,
|
|
|
+ version: null,
|
|
|
+ repairableType: null,
|
|
|
+ repairableTypes: null,
|
|
|
+ operatorId: null,
|
|
|
+ operatorName: null,
|
|
|
+ operatorTime: null,
|
|
|
+ skuImage: null,
|
|
|
+ orginSkuImage: null,
|
|
|
+})
|
|
|
+
|
|
|
+function initDetail() {
|
|
|
+ selectDetail.value = {
|
|
|
+ id: null,
|
|
|
+ rejectHeadId: null,
|
|
|
+ rejectPushTaskNo: null,
|
|
|
+ status: null,
|
|
|
+ detailStatus: null,
|
|
|
+ isGenuine: null,
|
|
|
+ qualityMark: null,
|
|
|
+ sku: '',
|
|
|
+ barCode: '',
|
|
|
+ tradeName: '',
|
|
|
+ qualityStatus: '',
|
|
|
+ manufactureTime: '',
|
|
|
+ validityTime: '',
|
|
|
+ batchNumber: '',
|
|
|
+ remark: '',
|
|
|
+ asnNo: null,
|
|
|
+ number: null,
|
|
|
+ warehouse: null,
|
|
|
+ warehouseBin: null,
|
|
|
+ boxPhotos: null,
|
|
|
+ productPhotos: null,
|
|
|
+ files: null,
|
|
|
+ pieceTag: null,
|
|
|
+ createTime: null,
|
|
|
+ creatorId: null,
|
|
|
+ updateTime: null,
|
|
|
+ updaterId: null,
|
|
|
+ deleteTime: null,
|
|
|
+ version: null,
|
|
|
+ repairableType: null,
|
|
|
+ repairableTypes: null,
|
|
|
+ operatorId: null,
|
|
|
+ operatorName: null,
|
|
|
+ operatorTime: null,
|
|
|
+ skuImage: null,
|
|
|
+ orginSkuImage: null,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 承运商选择
|
|
|
+const logisticPickerShow = ref(false)
|
|
|
+const selectedLogistic = (row) => {
|
|
|
+ const { selectedOptions } = row
|
|
|
+ logisticPickerShow.value = false
|
|
|
+ params.value.logisticsName = selectedOptions[0].text
|
|
|
+}
|
|
|
+
|
|
|
+// 货主选择
|
|
|
+const ownerPickerShow = ref(false)
|
|
|
+const ownerName = ref('')
|
|
|
+const selectedOwner = (row) => {
|
|
|
+ const { selectedValues, selectedOptions } = row
|
|
|
+ ownerPickerShow.value = false
|
|
|
+ params.value.ownerCode = selectedValues
|
|
|
+ ownerName.value = selectedOptions[0].text
|
|
|
+ initQualityInspection(params.value.ownerCode)
|
|
|
+}
|
|
|
+
|
|
|
+// 仓库选择
|
|
|
+const warehousePickerShow = ref(false)
|
|
|
+const warehouseName = ref('')
|
|
|
+const selectedWarehouse = (row) => {
|
|
|
+ const { selectedValues, selectedOptions } = row
|
|
|
+ warehousePickerShow.value = false
|
|
|
+ params.value.warehouseCode = selectedValues
|
|
|
+ warehouseName.value = selectedOptions[0].text
|
|
|
+}
|
|
|
+
|
|
|
+// 店铺选择
|
|
|
+const storePickerShow = ref(false)
|
|
|
+const selectedStore = (row) => {
|
|
|
+ const { selectedOptions } = row
|
|
|
+ storePickerShow.value = false
|
|
|
+ params.value.storeName = selectedOptions[0].text
|
|
|
+}
|
|
|
+
|
|
|
+// 货主名
|
|
|
+function getOwnerName(owner) {
|
|
|
+ return ownerMap.value[owner]
|
|
|
+}
|
|
|
+
|
|
|
+// 仓库名
|
|
|
+function getWarehouseName(code) {
|
|
|
+ return warehousesMap.value[code]
|
|
|
+}
|
|
|
+
|
|
|
+// 输入快递单号
|
|
|
+function inputExpressNo() {
|
|
|
+ const no = expressNo.value
|
|
|
+ const isSuccess = checkExpressNo(no)
|
|
|
+ if (!isSuccess) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const express_no = initExpressNo(no)
|
|
|
+ if (!express_no || express_no.length === 0) {
|
|
|
+ showFailToast('快递单号异常')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ params.value.returnNo = express_no
|
|
|
+ params.value.warehouseCode = warehouse.value
|
|
|
+ listAsn(express_no).then((res) => {
|
|
|
+ if (res.data) {
|
|
|
+ asnList.value = res.data
|
|
|
+ }
|
|
|
+ })
|
|
|
+ matchReturned(express_no)
|
|
|
+ expressNo.value = null
|
|
|
+ showInitialPage.value = false
|
|
|
+}
|
|
|
+
|
|
|
+function matchReturned(expressNo) {
|
|
|
+ getReturnedByExpress(expressNo).then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ if (!data) {
|
|
|
+ matchOrder(expressNo)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const { id, headStatus, returnNo, warehouseCode } = data
|
|
|
+ if (!id) {
|
|
|
+ console.log('没有退件记录')
|
|
|
+ matchOrder(expressNo)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (id) {
|
|
|
+ if (['已入仓', '已拆包'].includes(headStatus)) {
|
|
|
+ showNotify({
|
|
|
+ type: 'primary',
|
|
|
+ message: `当前退回单号${returnNo}--${headStatus}`,
|
|
|
+ })
|
|
|
+ init(false)
|
|
|
+ params.value.id = id
|
|
|
+ params.value.returnNo = returnNo
|
|
|
+ params.value.warehouseCode = warehouseCode
|
|
|
+ params.value.warehousingStatus = null
|
|
|
+ selectDetailIndex.value = -1
|
|
|
+ matchOrder(expressNo)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 匹配原单信息
|
|
|
+function matchOrder(expressNo) {
|
|
|
+ matchOrderBy(expressNo).then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ if (!data) {
|
|
|
+ console.log('未匹配到订单信息')
|
|
|
+ matchAsn(expressNo)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ console.log('匹配到订单信息')
|
|
|
+ if (data) {
|
|
|
+ const {
|
|
|
+ upstreamNo,
|
|
|
+ shopName,
|
|
|
+ ownerCode,
|
|
|
+ recipientName,
|
|
|
+ phone,
|
|
|
+ logisticName,
|
|
|
+ details,
|
|
|
+ } = data
|
|
|
+ showNotify({ type: 'success', message: '匹配到原单信息' })
|
|
|
+ params.value.upstreamNo = upstreamNo
|
|
|
+ params.value.ownerCode = ownerCode
|
|
|
+ params.value.warehouseCode = warehouse.value
|
|
|
+ params.value.storeName = shopName
|
|
|
+ params.value.logisticsName = logisticName
|
|
|
+ params.value.buyerName = handlerLongText(recipientName)
|
|
|
+ params.value.buyerPhone = handlerLongText(phone)
|
|
|
+ params.value.originalNo = '原单退回'
|
|
|
+ params.value.sendNo = null
|
|
|
+ initQualityInspection(params.value.ownerCode)
|
|
|
+ if (!details || details.length === 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const list = []
|
|
|
+ details.forEach((item) => {
|
|
|
+ const {
|
|
|
+ sku,
|
|
|
+ barCode,
|
|
|
+ detailAmount,
|
|
|
+ name,
|
|
|
+ productionDate,
|
|
|
+ expirationDate,
|
|
|
+ batchNumber,
|
|
|
+ quality,
|
|
|
+ attributeBin,
|
|
|
+ } = item
|
|
|
+ let qualityStatus
|
|
|
+ if (quality) {
|
|
|
+ if (quality === 'ZP') {
|
|
|
+ qualityStatus = '正品'
|
|
|
+ qualityStatus = '正品'
|
|
|
+ } else if (quality === 'CP') {
|
|
|
+ qualityStatus = '次品'
|
|
|
+ } else {
|
|
|
+ qualityStatus = '正品'
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ qualityStatus = '正品'
|
|
|
+ }
|
|
|
+ list.push({
|
|
|
+ sku: sku,
|
|
|
+ tradeName: name,
|
|
|
+ barCode: barCode,
|
|
|
+ number: detailAmount,
|
|
|
+ manufactureTime: productionDate,
|
|
|
+ validityTime: expirationDate,
|
|
|
+ batchNumber: batchNumber,
|
|
|
+ warehouse: attributeBin,
|
|
|
+ qualityStatus: qualityStatus,
|
|
|
+ isOriginal: true,
|
|
|
+ })
|
|
|
+ params.value.details = list
|
|
|
+ matchCarrier(expressNo)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ matchAsn(expressNo)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function matchCarrier(expressNo) {
|
|
|
+ matchCarrierCode(expressNo).then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ if (!data || data.length === 0) {
|
|
|
+ showNotify({
|
|
|
+ type: 'primary',
|
|
|
+ message: '请手动选择承运商',
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ params.value.logisticsName = data
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 转化加密
|
|
|
+function handlerLongText(text) {
|
|
|
+ return text && text.length > 15 ? '加密' : text ? text : null
|
|
|
+}
|
|
|
+
|
|
|
+// 匹配对应的ASN
|
|
|
+function matchAsn(expressNo) {
|
|
|
+ matchAsnBy(expressNo).then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ if (!data) {
|
|
|
+ console.log('未匹配asn单信息')
|
|
|
+ matchCarrier(expressNo)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const {
|
|
|
+ ownerCode,
|
|
|
+ phone,
|
|
|
+ shopName,
|
|
|
+ recipientName,
|
|
|
+ logisticName,
|
|
|
+ upstreamNo,
|
|
|
+ } = data
|
|
|
+ params.value.returnNo = expressNo
|
|
|
+ params.value.upstreamNo = upstreamNo
|
|
|
+ params.value.ownerCode = ownerCode
|
|
|
+ params.value.warehouseCode = warehouse.value
|
|
|
+ params.value.storeName = shopName
|
|
|
+ params.value.logisticsName = logisticName
|
|
|
+ params.value.buyerName = handlerLongText(recipientName)
|
|
|
+ params.value.buyerPhone = handlerLongText(phone)
|
|
|
+ params.value.originalNo = ''
|
|
|
+ params.value.sendNo = null
|
|
|
+ matchCarrier(expressNo)
|
|
|
+ params.value.details = []
|
|
|
+ initDetail()
|
|
|
+ initQualityInspection(params.value.ownerCode)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 校验快递单号格式
|
|
|
+function checkExpressNo(no) {
|
|
|
+ if (!no || no.length === 0) {
|
|
|
+ showFailToast('单号长度异常')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (no.startsWith('http')) {
|
|
|
+ showFailToast('扫描到了错误的条码')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (no.endsWith('.com')) {
|
|
|
+ showFailToast('扫描到了错误的条码')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ const length = no.length
|
|
|
+ if (length > 30 || length < 5) {
|
|
|
+ showFailToast('单号长度异常')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+// 处理快递单号相关问题
|
|
|
+function initExpressNo(no) {
|
|
|
+ // 处理扫描快递单号
|
|
|
+ if (!no) {
|
|
|
+ return no
|
|
|
+ }
|
|
|
+ if (no.includes('-')) {
|
|
|
+ no = no.substring(0, no.indexOf('-')) // 京东快递异常
|
|
|
+ }
|
|
|
+ return no
|
|
|
+ .replaceAll('-', '')
|
|
|
+ .replaceAll(' ', '')
|
|
|
+ .replaceAll('R02Z', '')
|
|
|
+ .replaceAll('R02T', '') // 圆通退回单号异常
|
|
|
+}
|
|
|
+
|
|
|
+// 重置提交 恢复到扫描快递单号页面
|
|
|
+function init(showInputPage = true) {
|
|
|
+ warehouseName.value = null
|
|
|
+ ownerName.value = null
|
|
|
+ params.value = {
|
|
|
+ orderUpstream: null,
|
|
|
+ returnNo: null,
|
|
|
+ warehouseCode: null,
|
|
|
+ logisticsName: null,
|
|
|
+ storeName: null,
|
|
|
+ orderUpSteam: null,
|
|
|
+ arrivalPayment: null,
|
|
|
+ buyerName: null,
|
|
|
+ asnNo: null,
|
|
|
+ upstreamNo: null,
|
|
|
+ buyerPhone: null,
|
|
|
+ originalNo: null,
|
|
|
+ remark: null,
|
|
|
+ details: [],
|
|
|
+ }
|
|
|
+ expressNo.value = null
|
|
|
+ showInitialPage.value = showInputPage
|
|
|
+ initDetail()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 扫码弹框
|
|
|
+const scancodeDialog = ref(false)
|
|
|
+const scancodeInputRef = ref(false)
|
|
|
+
|
|
|
+const qualityStatusDialog = ref(false)
|
|
|
+
|
|
|
+// 扫描条码dialog
|
|
|
+function showScancode() {
|
|
|
+ scancodeDialog.value = true
|
|
|
+}
|
|
|
+
|
|
|
+function openScanCode() {
|
|
|
+ nextTick(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ const input = scancodeInputRef.value?.$el?.querySelector('input')
|
|
|
+ input?.focus()
|
|
|
+ input?.setSelectionRange(0, input.value.length)
|
|
|
+ }, 150)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 选择质量状态
|
|
|
+function showQualityStatus() {
|
|
|
+ const barcode = scancode.value
|
|
|
+ if (!barcode) {
|
|
|
+ showFailToast('商品条码异常')
|
|
|
+ scancodeDialog.value = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+ qualityStatusDialog.value = true
|
|
|
+ qualityStatus.value = '正品'
|
|
|
+}
|
|
|
+
|
|
|
+// 查询商品
|
|
|
+async function queryBarcode() {
|
|
|
+ showLoadingToast({
|
|
|
+ duration: 0,
|
|
|
+ forbidClick: true,
|
|
|
+ message: '查询商品信息中.....',
|
|
|
+ })
|
|
|
+ const barcode = scancode.value
|
|
|
+ const { ownerCode } = params.value
|
|
|
+ if (ownerCode) {
|
|
|
+ const result = await queryOwnerBarcode(ownerCode, barcode)
|
|
|
+ if (!result) {
|
|
|
+ scancodeDialog.value = false
|
|
|
+ qualityStatusDialog.value = false
|
|
|
+ closeToast()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const { details } = params.value
|
|
|
+ if (!details || details.length === 0) {
|
|
|
+ await queryBarcodeBy(barcode)
|
|
|
+ scancodeDialog.value = false
|
|
|
+ qualityStatusDialog.value = false
|
|
|
+ closeToast()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 更具货主查询商品条码
|
|
|
+ * @param ownerCode
|
|
|
+ * @param barcode
|
|
|
+ * @returns {Promise<boolean>}
|
|
|
+ */
|
|
|
+async function queryOwnerBarcode(ownerCode, barcode) {
|
|
|
+ try {
|
|
|
+ const results = await searchOwnerBarcode({ ownerCode, barcode })
|
|
|
+ const { basSku } = results
|
|
|
+ if (!basSku) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ addDetailByBasSku(basSku)
|
|
|
+ return true
|
|
|
+ } catch (err) {
|
|
|
+ console.log(err.message)
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 查询对应的
|
|
|
+ * @param barcode
|
|
|
+ * @returns {Promise<boolean>}
|
|
|
+ */
|
|
|
+async function queryBarcodeBy(barcode) {
|
|
|
+ try {
|
|
|
+ const results = await searchBarcode({ barcode })
|
|
|
+ const { data } = results
|
|
|
+ const { basSku, ownerCodes } = data
|
|
|
+ if (ownerCodes && ownerCodes.length > 1) {
|
|
|
+ showOwnerSelectDialog(ownerCodes)
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ if (!basSku) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ addDetailByBasSku(basSku)
|
|
|
+ return null
|
|
|
+ } catch (err) {
|
|
|
+ console.log(err.message)
|
|
|
+ }
|
|
|
+ return null
|
|
|
+}
|
|
|
+
|
|
|
+// 添加详情
|
|
|
+function addDetailByBasSku(basSku) {
|
|
|
+ const { sku, barCode, ownerCode, tradeName } = basSku
|
|
|
+ selectDetailIndex.value = -1
|
|
|
+ selectDetail.value.sku = sku
|
|
|
+ if (!params.value.ownerCode) {
|
|
|
+ params.value.ownerCode = ownerCode
|
|
|
+ initQualityInspection(params.value.ownerCode)
|
|
|
+ }
|
|
|
+ selectDetail.value.barCode = barCode
|
|
|
+ selectDetail.value.qualityStatus = qualityStatus.value
|
|
|
+ selectDetail.value.tradeName = tradeName
|
|
|
+ selectDetail.value.number = 1
|
|
|
+ scancodeDialog.value = false
|
|
|
+ qualityStatusDialog.value = false
|
|
|
+ returnedDetailDialog.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 质量状态
|
|
|
+const returnedDetailDialog = ref(false)
|
|
|
+const selectedDetailQualityStatus = ref(false)
|
|
|
+const selectedDetailQualityStatusFunc = (row) => {
|
|
|
+ const { selectedOptions } = row
|
|
|
+ selectedDetailQualityStatus.value = false
|
|
|
+ selectDetail.value.qualityStatus = selectedOptions[0].text
|
|
|
+}
|
|
|
+
|
|
|
+// 辅助函数:格式化日期显示(可选)
|
|
|
+const formatDateDisplay = (dateString) => {
|
|
|
+ if (!dateString) return ''
|
|
|
+ return dateString.replace(/-/g, '/') // 将 2023-05-01 显示为 2023/05/01
|
|
|
+}
|
|
|
+// 辅助函数:将日期字符串转换为数组格式 [年, 月, 日]
|
|
|
+const parseDateValue = (dateString) => {
|
|
|
+ if (!dateString) return []
|
|
|
+ const [year, month, day] = dateString.split('-')
|
|
|
+ return [year, month, day]
|
|
|
+}
|
|
|
+
|
|
|
+// 生产日期
|
|
|
+const showManufactureTime = ref(false)
|
|
|
+const manufactureTimeConfirm = async (result) => {
|
|
|
+ if (!result.selectedValues) {
|
|
|
+ showManufactureTime.value = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 正确解构参数
|
|
|
+ const { selectedValues } = result
|
|
|
+ const [year, month, day] = selectedValues
|
|
|
+ const formattedMonth = month.padStart(2, '0')
|
|
|
+ const formattedDay = day.padStart(2, '0')
|
|
|
+ const manufactureTime = `${year}-${formattedMonth}-${formattedDay}`
|
|
|
+ const { sku } = selectDetail.value
|
|
|
+ const { owner: ownerCode } = params.value
|
|
|
+ const isValid = await checkValidateDate(
|
|
|
+ ownerCode,
|
|
|
+ sku,
|
|
|
+ manufactureTime,
|
|
|
+ 'manufactureTime',
|
|
|
+ )
|
|
|
+ if (!isValid) {
|
|
|
+ console.log('检验出现异常')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ selectDetail.value.manufactureTime = manufactureTime
|
|
|
+ showManufactureTime.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 失效日期
|
|
|
+const showValidityTime = ref(false)
|
|
|
+const validityTimeConfirm = async (result) => {
|
|
|
+ if (!result.selectedValues) {
|
|
|
+ showValidityTime.value = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 正确解构参数
|
|
|
+ const { selectedValues } = result
|
|
|
+ const [year, month, day] = selectedValues
|
|
|
+ const formattedMonth = month.padStart(2, '0')
|
|
|
+ const formattedDay = day.padStart(2, '0')
|
|
|
+ const validityTime = `${year}-${formattedMonth}-${formattedDay}`
|
|
|
+ const { sku } = selectDetail.value
|
|
|
+ const { owner: ownerCode } = params.value
|
|
|
+ const isValid = await checkValidateDate(
|
|
|
+ ownerCode,
|
|
|
+ sku,
|
|
|
+ validityTime,
|
|
|
+ 'validityTime',
|
|
|
+ )
|
|
|
+ if (!isValid) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ selectDetail.value.validityTime = `${year}-${formattedMonth}-${formattedDay}`
|
|
|
+ showValidityTime.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 质量状态标签
|
|
|
+function getTagColor(qualityStatus) {
|
|
|
+ return getTagColorBy(qualityStatus)
|
|
|
+}
|
|
|
+
|
|
|
+// 添加详情
|
|
|
+function addDetails() {
|
|
|
+ console.log(addDetails)
|
|
|
+ const index = selectDetailIndex.value
|
|
|
+ console.log(index)
|
|
|
+ const detail = JSON.parse(JSON.stringify(selectDetail.value))
|
|
|
+ console.log(detail)
|
|
|
+ if (index > -1) {
|
|
|
+ params.value.details[index] = detail
|
|
|
+ initDetail()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 查找对应的详情进行合并
|
|
|
+ params.value.details.push(detail)
|
|
|
+ // 重置详情
|
|
|
+ initDetail()
|
|
|
+}
|
|
|
+
|
|
|
+// 删除对应的详情
|
|
|
+function removeDetails(index) {
|
|
|
+ if (null === index) {
|
|
|
+ showFailToast('未找到对应的详情')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const { id } = params.value.details[index]
|
|
|
+ if (!id) {
|
|
|
+ params.value.details.splice(index, 1)
|
|
|
+ showSuccessToast('删除成功')
|
|
|
+ } else {
|
|
|
+ deleteDetails(id).then((res) => {
|
|
|
+ if (res.data) {
|
|
|
+ params.value.details.splice(index, 1)
|
|
|
+ showSuccessToast('删除成功')
|
|
|
+ } else {
|
|
|
+ showFailToast('删除失败')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function selectOwner({ selectedValues }) {
|
|
|
+ const ownerCode = selectedValues[0].value
|
|
|
+ params.value.ownerCode = selectedValues[0]
|
|
|
+ showOwnerSelect.value = false
|
|
|
+ ownerSelectedOptions.value = []
|
|
|
+ const barcode = scancode.value
|
|
|
+ searchOwnerBarcode({ ownerCode, barcode })
|
|
|
+ getStoreOptionsBy(ownerCode)
|
|
|
+ // 货主
|
|
|
+}
|
|
|
+
|
|
|
+const showOwnerSelect = ref(false)
|
|
|
+const ownerSelectedOptions = ref([])
|
|
|
+
|
|
|
+// 多货主选择
|
|
|
+function showOwnerSelectDialog(items) {
|
|
|
+ if (!items || items.length === 0) {
|
|
|
+ ownerSelectedOptions.value = owners.value.map((item) => {
|
|
|
+ JSON.parse(JSON.stringify(item))
|
|
|
+ })
|
|
|
+ showOwnerSelect.value = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ownerSelectedOptions.value = owners.value
|
|
|
+ .filter((item) => {
|
|
|
+ const { value } = item
|
|
|
+ return items.includes(value)
|
|
|
+ })
|
|
|
+ .map((item) => JSON.parse(JSON.stringify(item)))
|
|
|
+ showOwnerSelect.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 校验日期
|
|
|
+async function checkValidateDate(ownerCode, sku, newDate, fieldName) {
|
|
|
+ if (['NOSKU', 'NOBARCODE'].includes(sku)) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ const params = {
|
|
|
+ newDate: newDate,
|
|
|
+ fieldName: fieldName,
|
|
|
+ ownerCode: ownerCode,
|
|
|
+ sku: sku,
|
|
|
+ }
|
|
|
+ const type = fieldName === 'manufactureTime' ? '生产日期' : '失效日期'
|
|
|
+ try {
|
|
|
+ await validateDate(params)
|
|
|
+ showNotify({
|
|
|
+ type: 'success',
|
|
|
+ message: `${type} 状态校验成功 `,
|
|
|
+ })
|
|
|
+ return true
|
|
|
+ } catch (error) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: `校验${type}出现异常`,
|
|
|
+ })
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function blobToBase64(blob) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const fileReader = new FileReader()
|
|
|
+ fileReader.onload = (e) => {
|
|
|
+ resolve(e.target.result)
|
|
|
+ }
|
|
|
+ fileReader.readAsDataURL(blob)
|
|
|
+ fileReader.onerror = () => {
|
|
|
+ reject(new Error('blobToBase64 error'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 编辑等级详情
|
|
|
+function editDetails(index) {
|
|
|
+ selectDetailIndex.value = index
|
|
|
+ const { details } = params.value
|
|
|
+ selectDetail.value = JSON.parse(JSON.stringify(details[index]))
|
|
|
+ returnedDetailDialog.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 写入当前商品详情
|
|
|
+function pushImageItem(event, type) {
|
|
|
+ const file = event.target.files[0]
|
|
|
+ if (!file) return
|
|
|
+
|
|
|
+ if (!file.type.startsWith('image/')) {
|
|
|
+ alert('请选择图片文件!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const filename = file.name
|
|
|
+ const item = {
|
|
|
+ src: URL.createObjectURL(file),
|
|
|
+ file: file,
|
|
|
+ fileName: filename,
|
|
|
+ type,
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type === '外箱图') {
|
|
|
+ if (!selectDetail.value.boxPhotos) {
|
|
|
+ selectDetail.value.boxPhotos = []
|
|
|
+ }
|
|
|
+
|
|
|
+ blobToBase64(file).then((_) => {
|
|
|
+ const some = boxFiles.value.some((item) => {
|
|
|
+ return filename === item.fileName
|
|
|
+ })
|
|
|
+ if (some) {
|
|
|
+ showFailToast('录入重复图片')
|
|
|
+ } else {
|
|
|
+ boxFiles.value.push(item)
|
|
|
+ selectDetail.value.boxPhotos.push(filename)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else if (type === '内物图') {
|
|
|
+ if (!selectDetail.value.productPhotos) {
|
|
|
+ selectDetail.value.productPhotos = []
|
|
|
+ }
|
|
|
+ blobToBase64(file).then((_) => {
|
|
|
+ const some = productFiles.value.some((item) => {
|
|
|
+ return filename === item.fileName
|
|
|
+ })
|
|
|
+ if (some) {
|
|
|
+ showFailToast('录入重复图片')
|
|
|
+ } else {
|
|
|
+ productFiles.value.push(item)
|
|
|
+ selectDetail.value.productPhotos.push(filename)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 触发拍照
|
|
|
+function invokeCameraToCapture(type) {
|
|
|
+ const { sku } = selectDetail.value
|
|
|
+ if (!sku) {
|
|
|
+ showFailToast('当前没有可录入的商品信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (type === '外箱图') {
|
|
|
+ document.getElementById('outer-carton-box-input').click()
|
|
|
+ } else if (type === '内物图') {
|
|
|
+ document.getElementById('inner-contents-input').click()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getImageUrl(filename, type) {
|
|
|
+ let list = []
|
|
|
+ if (type === '外箱图') {
|
|
|
+ list = boxFiles.value
|
|
|
+ } else if (type === '内物图') {
|
|
|
+ list = productFiles.value
|
|
|
+ }
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ const item = list[i]
|
|
|
+ const { src, fileName } = item
|
|
|
+ if (fileName === fileName) {
|
|
|
+ return src
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null
|
|
|
+}
|
|
|
+
|
|
|
+// 外箱图事件
|
|
|
+function outerCartonInput(event) {
|
|
|
+ pushImageItem(event, '外箱图')
|
|
|
+}
|
|
|
+
|
|
|
+// 内物图异常
|
|
|
+function innerContentsInput(event) {
|
|
|
+ pushImageItem(event, '内物图')
|
|
|
+}
|
|
|
+
|
|
|
+function submit() {
|
|
|
+ const { ownerCode, logisticsName, returnNo, warehouseCode } = params.value
|
|
|
+ if (!ownerCode || ownerCode.length === 0) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '没有对应的退件详情!请录入对应的退件详情',
|
|
|
+ })
|
|
|
+ return
|
|
|
+ } else if (!logisticsName || logisticsName.length === 0) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '请录入承运商',
|
|
|
+ })
|
|
|
+ return
|
|
|
+ } else if (!returnNo || ownerCode.length === 0) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '请录入退件单号',
|
|
|
+ })
|
|
|
+ return
|
|
|
+ } else if (!warehouseCode || warehouseCode.length === 0) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '请选择登记仓库',
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ console.log('checkDetailNumber')
|
|
|
+ if (checkDetailNumber()) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const formData = new FormData()
|
|
|
+ const boxItems = (boxFiles && boxFiles.value) ? boxFiles.value : []
|
|
|
+ const productItems =
|
|
|
+ (productFiles && productFiles.value) ? productFiles.value : []
|
|
|
+ const files = [...boxItems, ...productItems]
|
|
|
+ const fileName = []
|
|
|
+ if (files && files.length > 0) {
|
|
|
+ files.forEach((item) => {
|
|
|
+ const { file, filename } = item
|
|
|
+ if (!fileName.includes(filename)) {
|
|
|
+ formData.append('files', file)
|
|
|
+ fileName.push(filename)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ handleDetails(params.value.details)
|
|
|
+ formData.append('body', JSON.stringify(JSON.parse(JSON.stringify(params.value))))
|
|
|
+ showLoadingToast({
|
|
|
+ duration: 0,
|
|
|
+ forbidClick: true,
|
|
|
+ message: '提交登记中.....',
|
|
|
+ })
|
|
|
+ console.log("register")
|
|
|
+ register(formData)
|
|
|
+ .then((res) => {
|
|
|
+ const { data } = res
|
|
|
+ closeToast()
|
|
|
+ if (data) {
|
|
|
+ showNotify({
|
|
|
+ type: 'success',
|
|
|
+ message: '成功提交',
|
|
|
+ })
|
|
|
+ init()
|
|
|
+ params.value.ownerCode = ownerCode
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ closeToast()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function checkDetailNumber() {
|
|
|
+ const { details } = params.value
|
|
|
+ if (!details || details.length === 0) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '登记详情不能为空',
|
|
|
+ })
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ const check = details.some((item) => {
|
|
|
+ const { number } = item
|
|
|
+ return Number.isNaN(number) || Number(number) <= 0
|
|
|
+ })
|
|
|
+ if (check) {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '登记数量不能为 0 和 空 ',
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return check
|
|
|
+}
|
|
|
+
|
|
|
+function handleDetails(details){
|
|
|
+ for (let i = 0; i < details.length; i++) {
|
|
|
+ const {qualityStatus} = details[i]
|
|
|
+ console.log(qualityStatus)
|
|
|
+ const {isGenuineValue,qualityMark} = getStatus(qualityStatus)
|
|
|
+ details[i].isGenuine = isGenuineValue
|
|
|
+ details[i].qualityMark = qualityMark
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+window.onRefresh = async ()=>{
|
|
|
+ console.log("window.onRefresh")
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="sass">
|
|
|
+.van-nav-bar
|
|
|
+ .left-btn
|
|
|
+ color: #fff
|
|
|
+ height: 46px
|
|
|
+ padding-right: 20px
|
|
|
+ line-height: 46px
|
|
|
+
|
|
|
+ .right-btn
|
|
|
+ color: #fff
|
|
|
+
|
|
|
+.container
|
|
|
+ .init-container
|
|
|
+ width: 100%
|
|
|
+
|
|
|
+ .scan-returned-content
|
|
|
+ padding: 10px
|
|
|
+
|
|
|
+ .input-group
|
|
|
+ padding: 5px
|
|
|
+
|
|
|
+ .button-group
|
|
|
+ padding: 5px
|
|
|
+
|
|
|
+ .content
|
|
|
+ width: 100%
|
|
|
+
|
|
|
+ .scan-returned-no
|
|
|
+ align-items: center
|
|
|
+ padding: 15px
|
|
|
+
|
|
|
+ .returned-detail-list
|
|
|
+ .card-div
|
|
|
+ background: #fff
|
|
|
+ border-radius: 12px
|
|
|
+ overflow: hidden
|
|
|
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05)
|
|
|
+ margin: 5px 0
|
|
|
+ padding: 5px 0
|
|
|
+
|
|
|
+ .card-div-content
|
|
|
+ padding: 3px
|
|
|
+
|
|
|
+ .info-row
|
|
|
+ display: flex
|
|
|
+ margin-bottom: 12px
|
|
|
+ font-size: 12px
|
|
|
+
|
|
|
+ .info-label
|
|
|
+ width: 100px
|
|
|
+ color: #969799
|
|
|
+
|
|
|
+ .info-value
|
|
|
+ flex: 1
|
|
|
+ color: #333333
|
|
|
+ font-weight: 500
|
|
|
+
|
|
|
+ .card-div-footer
|
|
|
+ padding: 5px
|
|
|
+ background: #fafafa
|
|
|
+ border-top: 1px solid #f5f5f5
|
|
|
+ color: #646566
|
|
|
+ font-size: 14px
|
|
|
+ line-height: 1.5
|
|
|
+
|
|
|
+ .card-div-footer-images
|
|
|
+ padding: 5px
|
|
|
+ background: #fafafa
|
|
|
+ border-top: 1px solid #f5f5f5
|
|
|
+ color: #646566
|
|
|
+ font-size: 14px
|
|
|
+ line-height: 1.5
|
|
|
+
|
|
|
+ .card-div-footer-options
|
|
|
+ background: #fafafa
|
|
|
+ border-top: 1px solid #f5f5f5
|
|
|
+ color: #646566
|
|
|
+ font-size: 14px
|
|
|
+
|
|
|
+ .options-row
|
|
|
+ display: flex
|
|
|
+ font-size: 12px
|
|
|
+
|
|
|
+ .info-label
|
|
|
+ width: 100px
|
|
|
+ color: #969799
|
|
|
+
|
|
|
+ .info-value
|
|
|
+ flex: 1
|
|
|
+ color: #333333
|
|
|
+ font-weight: 500
|
|
|
+
|
|
|
+ .returned-details
|
|
|
+ .van-field
|
|
|
+ padding: 0
|
|
|
+</style>
|