|
|
@@ -4,7 +4,7 @@
|
|
|
title="退货登记"
|
|
|
left-arrow
|
|
|
@click-left="goBack"
|
|
|
- @click-right="init"
|
|
|
+ @click-right="onReset"
|
|
|
>
|
|
|
<template #left>
|
|
|
<van-icon name="arrow-left" size="25" />
|
|
|
@@ -15,621 +15,55 @@
|
|
|
</template>
|
|
|
</van-nav-bar>
|
|
|
|
|
|
- <div class="content">
|
|
|
- <div v-if="showInitialPage" class="init-container">
|
|
|
- <div class="scan-returned-content with-fixed-notice">
|
|
|
- <div class="input-group">
|
|
|
- <van-field
|
|
|
- ref="scanExpressNoInputRef"
|
|
|
- autofocus
|
|
|
- v-model="expressNo"
|
|
|
- autocomplete="off"
|
|
|
- placeholder="输入快递单号"
|
|
|
- clearable
|
|
|
- @keyup.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>
|
|
|
- <div class="content-tips fixed-notice">
|
|
|
- <van-notice-bar color="#1989fa" background="#ecf9ff"
|
|
|
- >{{ ownerQualityInspection }}
|
|
|
- </van-notice-bar>
|
|
|
- </div>
|
|
|
- <!-- 退货信息区域 -->
|
|
|
- <div class="return-info-section with-fixed-notice">
|
|
|
- <div class="section-header">
|
|
|
- <van-icon name="orders-o" size="18" />
|
|
|
- <span class="section-title">退货信息</span>
|
|
|
- </div>
|
|
|
- <van-cell-group inset style="margin-top: 10px">
|
|
|
- <van-field
|
|
|
- v-model="params.returnNo"
|
|
|
- label="快递单号"
|
|
|
- readonly
|
|
|
- placeholder="请输入快递单号"
|
|
|
- />
|
|
|
-
|
|
|
- <van-field
|
|
|
- readonly
|
|
|
- clickable
|
|
|
- label="承运商"
|
|
|
- placeholder="选择承运商"
|
|
|
- :model-value="getLogisticName()"
|
|
|
- @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()"
|
|
|
- @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="showOwnerSelectFunc"
|
|
|
- />
|
|
|
-
|
|
|
- <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.remark"
|
|
|
- rows="1"
|
|
|
- autosize
|
|
|
- label="备注"
|
|
|
- type="textarea"
|
|
|
- placeholder="备注"
|
|
|
- />
|
|
|
- </van-cell-group>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 商品信息区域 -->
|
|
|
- <div class="product-info-section">
|
|
|
- <div class="section-header">
|
|
|
- <van-icon name="goods-collect-o" size="18" />
|
|
|
- <span class="section-title">商品信息</span>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 商品条码扫描输入框 -->
|
|
|
- <van-cell-group inset style="margin-top: 10px">
|
|
|
- <van-field
|
|
|
- ref="productTabScancodeInputRef"
|
|
|
- v-model="scancode"
|
|
|
- label="商品条码"
|
|
|
- autocomplete="off"
|
|
|
- placeholder="扫描或输入商品条码"
|
|
|
- clearable
|
|
|
- @keydown.enter="showQualityStatus"
|
|
|
- />
|
|
|
- </van-cell-group>
|
|
|
-
|
|
|
- <div class="returned-detail-list">
|
|
|
- <template v-if="!params.details || params.details.length === 0">
|
|
|
- <van-empty description="暂无信息请进行录入" />
|
|
|
- </template>
|
|
|
-
|
|
|
- <template v-for="(item, index) in params.details" :key="`detail-${item.sku || 'unknown'}-${item.barCode || 'nocode'}-${index}`">
|
|
|
- <div class="card-div">
|
|
|
- <div class="card-div-content">
|
|
|
- <div class="info-row">
|
|
|
- <div class="info-label">sku</div>
|
|
|
- <div class="info-value">{{ item.sku }}</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>
|
|
|
-
|
|
|
- <template v-if="hasBoxItems(item)">
|
|
|
- <div>
|
|
|
- <van-divider content-position="left">外箱图</van-divider>
|
|
|
- <van-row>
|
|
|
- <template v-for="(i, imgIndex) in getBoxItems(item)" :key="`box-${item.sku || 'unknown'}-${index}-${imgIndex}-${i.fileName || 'noname'}`">
|
|
|
- <van-col span="4">
|
|
|
- <van-image
|
|
|
- :key="`box-photos-${index}-${imgIndex}`"
|
|
|
- @click="
|
|
|
- previewImages(item.boxPhotos, imgIndex, '外箱图')
|
|
|
- "
|
|
|
- width="100%"
|
|
|
- height="50"
|
|
|
- :src="i.src"
|
|
|
- />
|
|
|
- </van-col>
|
|
|
- </template>
|
|
|
- </van-row>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <template v-if="hasProductItems(item)">
|
|
|
- <div>
|
|
|
- <van-divider content-position="left">内物图</van-divider>
|
|
|
- <van-row>
|
|
|
- <template v-for="(i, imgIndex) in getProductItems(item)" :key="`product-${item.sku || 'unknown'}-${index}-${imgIndex}-${i.fileName || 'noname'}`">
|
|
|
- <van-col span="4">
|
|
|
- <van-image
|
|
|
- :key="`product-photos-${index}-${imgIndex}`"
|
|
|
- @click="
|
|
|
- previewImages(
|
|
|
- item.productPhotos,
|
|
|
- imgIndex,
|
|
|
- '内物图',
|
|
|
- )
|
|
|
- "
|
|
|
- width="100%"
|
|
|
- height="60"
|
|
|
- :src="i.src"
|
|
|
- />
|
|
|
- </van-col>
|
|
|
- </template>
|
|
|
- </van-row>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <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>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 提交按钮区域 -->
|
|
|
- <div class="submit-section">
|
|
|
- <van-cell-group inset style="margin-top: 20px; margin-bottom: 50px">
|
|
|
- <van-button type="primary" block @click="submit">提交</van-button>
|
|
|
- </van-cell-group>
|
|
|
- </div>
|
|
|
+ <div class="init-container">
|
|
|
+ <div style="background-color: white">
|
|
|
+ <p style="margin: 3px;font-size: 14px">
|
|
|
+ 仓库:<span style="color: #333333">{{ workbench.warehouseCode}}</span>
|
|
|
+ </p>
|
|
|
+ <p style="margin: 3px;font-size: 14px">
|
|
|
+ 工作台:<span style="color: #333333">{{ workbench.workStation}}</span>
|
|
|
+ </p>
|
|
|
+ <van-button size="mini" type="primary" plain icon="replay" @click.stop="getWorkbench"></van-button>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <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" :key="`quality-${index}`">
|
|
|
- <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
|
|
|
- :lazy-render="true"
|
|
|
- :show-confirm-button="checkUploadImages()"
|
|
|
- @confirm="addDetails"
|
|
|
- @cancel="cancelReturnedDetailDialog"
|
|
|
- class="compact-dialog"
|
|
|
- >
|
|
|
- <div class="dialog-content">
|
|
|
- <div class="field-group">
|
|
|
- <van-field
|
|
|
- v-model="selectDetail.sku"
|
|
|
- label="SKU"
|
|
|
- placeholder="SKU"
|
|
|
- clearable
|
|
|
- class="compact-field"
|
|
|
- />
|
|
|
- <van-field
|
|
|
- readonly
|
|
|
- v-model="selectDetail.barCode"
|
|
|
- label="商品条码"
|
|
|
- placeholder="商品条码"
|
|
|
- clearable
|
|
|
- class="compact-field"
|
|
|
- />
|
|
|
- <van-field
|
|
|
- v-model="selectDetail.tradeName"
|
|
|
- label="商品名称"
|
|
|
- placeholder="商品名称"
|
|
|
- readonly
|
|
|
- class="compact-field"
|
|
|
+ <div class="scan-returned-content">
|
|
|
+ <div class="input-group">
|
|
|
+ <van-cell title="外箱图上传" label="支持 jpg/png" />
|
|
|
+ <van-uploader
|
|
|
+ v-model="outerImages"
|
|
|
+ :max-count="5"
|
|
|
+ :max-size="10 * 1024 * 1024"
|
|
|
+ :before-read="beforeReadImage"
|
|
|
+ :preview-full-image="true"
|
|
|
+ :deletable="true"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
- <div class="field-group">
|
|
|
- <van-field
|
|
|
- readonly
|
|
|
- clickable
|
|
|
- label="质量状态"
|
|
|
- placeholder="质量状态"
|
|
|
- v-model="selectDetail.qualityStatus"
|
|
|
- @click="selectedDetailQualityStatus = true"
|
|
|
- class="compact-field"
|
|
|
+ <div class="input-group">
|
|
|
+ <van-cell title="内物图上传" label="支持 jpg/png" />
|
|
|
+ <van-uploader
|
|
|
+ v-model="innerImages"
|
|
|
+ :max-count="5"
|
|
|
+ :max-size="10 * 1024 * 1024"
|
|
|
+ :before-read="beforeReadImage"
|
|
|
+ :preview-full-image="true"
|
|
|
+ :deletable="true"
|
|
|
/>
|
|
|
</div>
|
|
|
- <van-popup
|
|
|
- v-model:show="selectedDetailQualityStatus"
|
|
|
- position="bottom"
|
|
|
- destroy-on-close
|
|
|
- >
|
|
|
- <van-picker
|
|
|
- :columns="qualityStatusOptions"
|
|
|
- @cancel="selectedDetailQualityStatus = false"
|
|
|
- @confirm="selectedDetailQualityStatusFunc"
|
|
|
- />
|
|
|
- </van-popup>
|
|
|
-
|
|
|
- <div class="field-group date-fields">
|
|
|
- <van-field
|
|
|
- is-link
|
|
|
- readonly
|
|
|
- name="datePicker"
|
|
|
- label="生产日期"
|
|
|
- :placeholder="selectDetail.manufactureTime ? '' : '请选择生产日期'"
|
|
|
- :model-value="formatDateDisplay(selectDetail.manufactureTime)"
|
|
|
- @click="showManufactureTime = true"
|
|
|
- class="compact-field date-field"
|
|
|
- />
|
|
|
- <van-popup
|
|
|
- v-model:show="showManufactureTime"
|
|
|
- destroy-on-close
|
|
|
- position="bottom"
|
|
|
- >
|
|
|
- <van-date-picker
|
|
|
- :max-date="maxDate"
|
|
|
- :min-date="minDate"
|
|
|
- :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"
|
|
|
- class="compact-field date-field"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <van-popup
|
|
|
- v-model:show="showValidityTime"
|
|
|
- destroy-on-close
|
|
|
- position="bottom"
|
|
|
- >
|
|
|
- <van-date-picker
|
|
|
- :max-date="maxDate"
|
|
|
- :min-date="minDate"
|
|
|
- :model-value="parseDateValue(selectDetail.validityTime)"
|
|
|
- @confirm="validityTimeConfirm"
|
|
|
- @cancel="showValidityTime = false"
|
|
|
- />
|
|
|
- </van-popup>
|
|
|
-
|
|
|
- <div class="field-group">
|
|
|
- <van-field
|
|
|
- autocomplete="off"
|
|
|
- v-model="selectDetail.batchNumber"
|
|
|
- label="批次号"
|
|
|
- placeholder="批次号"
|
|
|
- clearable
|
|
|
- class="compact-field"
|
|
|
- />
|
|
|
- <van-field
|
|
|
- v-model="selectDetail.number"
|
|
|
- label="数量"
|
|
|
- type="digit"
|
|
|
- placeholder="数量"
|
|
|
- clearable
|
|
|
- class="compact-field"
|
|
|
- />
|
|
|
- <van-field
|
|
|
- v-model="selectDetail.remark"
|
|
|
- label="备注"
|
|
|
- placeholder="备注"
|
|
|
- label-align="top"
|
|
|
- class="compact-field"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div v-if="showAccessories" class="accessories-section">
|
|
|
- <van-divider content-position="left" class="compact-divider">配件信息</van-divider>
|
|
|
- <div class="accessories-content">
|
|
|
- <template v-for="(item, index) in accessories" :key="`accessory-${index}`">
|
|
|
- <div class="accessory-item">
|
|
|
- 配件条码: [<span class="accessory-highlight">{{
|
|
|
- item.accessory
|
|
|
- }}</span
|
|
|
- >] [<span class="accessory-desc">{{ item.descrC }}</span
|
|
|
- >] 数量: [<span class="accessory-highlight">{{ item.qty }}</span
|
|
|
- >]件
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <div class="accessory-notice">
|
|
|
- 请检查商品: 【<span class="accessory-warning">{{
|
|
|
- selectDetail.sku
|
|
|
- }}</span
|
|
|
- >】 {{ selectDetail.tradeName }} 配件
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div class="button-group">
|
|
|
+ <van-space>
|
|
|
+ <van-button type="warning" plain @click="previewAll">预览全部</van-button>
|
|
|
+ <van-button type="danger" plain @click="onReset">重置图片</van-button>
|
|
|
+ <van-button type="primary" @click="onSubmit">提交</van-button>
|
|
|
+ </van-space>
|
|
|
</div>
|
|
|
-
|
|
|
- <div v-if="selectDetail.boxPhotos && selectDetail.boxPhotos.length > 0" class="photo-section">
|
|
|
- <van-divider content-position="left" class="compact-divider">外箱图</van-divider>
|
|
|
- <div class="photo-grid">
|
|
|
- <template v-for="(item, index) in selectDetail.boxPhotos" :key="`detail-box-${index}`">
|
|
|
- <div class="photo-item">
|
|
|
- <van-image
|
|
|
- :key="`box-photos-${index}`"
|
|
|
- width="100%"
|
|
|
- height="40"
|
|
|
- :src="getImageUrl(item, '外箱图')"
|
|
|
- @click="showBoxImagePreview(index)"
|
|
|
- class="compact-image"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
-
|
|
|
- <van-image-preview
|
|
|
- v-model:show="showBoxPreview"
|
|
|
- :images="detailBoxImages"
|
|
|
- :start-position="startBoxPosition"
|
|
|
- @change="onBoxPreviewChange"
|
|
|
- closeable
|
|
|
- >
|
|
|
- <template #index>
|
|
|
- <div class="custom-toolbar">
|
|
|
- <span
|
|
|
- >{{ startPhotosPosition + 1 }}/{{
|
|
|
- selectDetail.boxPhotos.length
|
|
|
- }}</span
|
|
|
- >
|
|
|
- <van-button
|
|
|
- icon="delete"
|
|
|
- type="danger"
|
|
|
- @click.stop="handleBoxDelete"
|
|
|
- size="mini"
|
|
|
- >删除
|
|
|
- </van-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </van-image-preview>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div v-if="selectDetail.productPhotos && selectDetail.productPhotos.length > 0" class="photo-section">
|
|
|
- <van-divider content-position="left" class="compact-divider">内物图</van-divider>
|
|
|
- <div class="photo-grid">
|
|
|
- <template v-for="(item, index) in selectDetail.productPhotos" :key="`detail-product-${index}`">
|
|
|
- <div class="photo-item">
|
|
|
- <van-image
|
|
|
- :key="`product-photos-${index}`"
|
|
|
- width="100%"
|
|
|
- height="40"
|
|
|
- :src="getImageUrl(item, '内物图')"
|
|
|
- @click="showPhotosImagePreview(index)"
|
|
|
- class="compact-image"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
-
|
|
|
- <van-image-preview
|
|
|
- v-model:show="showPhotosPreview"
|
|
|
- :images="detailProductImages"
|
|
|
- :start-position="startPhotosPosition"
|
|
|
- @change="onPhotosPreviewChange"
|
|
|
- closeable
|
|
|
- >
|
|
|
- <template #index>
|
|
|
- <div class="custom-toolbar">
|
|
|
- <span>
|
|
|
- {{ startPhotosPosition + 1 }}/{{
|
|
|
- selectDetail.productPhotos.length
|
|
|
- }}
|
|
|
- </span>
|
|
|
- <van-button
|
|
|
- icon="delete"
|
|
|
- type="danger"
|
|
|
- @click.stop="handlePhotosDelete"
|
|
|
- size="mini"
|
|
|
- >删除
|
|
|
- </van-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </van-image-preview>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="camera-buttons">
|
|
|
- <van-button
|
|
|
- type="primary"
|
|
|
- class="camera-btn"
|
|
|
- @click="invokeCameraToCapture('外箱图')"
|
|
|
- size="small"
|
|
|
- >外箱图录入
|
|
|
- </van-button>
|
|
|
- <van-button
|
|
|
- type="primary"
|
|
|
- class="camera-btn"
|
|
|
- @click="invokeCameraToCapture('内物图')"
|
|
|
- size="small"
|
|
|
- >内物图录入
|
|
|
- </van-button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <input
|
|
|
- type="file"
|
|
|
- id="outer-carton-box-input"
|
|
|
- capture="user"
|
|
|
- accept="image/*"
|
|
|
- hidden
|
|
|
- @change="outerCartonInput"
|
|
|
- />
|
|
|
-
|
|
|
- <input
|
|
|
- type="file"
|
|
|
- id="inner-contents-input"
|
|
|
- capture="user"
|
|
|
- accept="image/*"
|
|
|
- hidden
|
|
|
- @change="innerContentsInput"
|
|
|
- />
|
|
|
</div>
|
|
|
- </van-dialog>
|
|
|
-
|
|
|
- <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>
|
|
|
- <owner ref="ownerRef" @onOwner="onOwner" />
|
|
|
-
|
|
|
- <!-- 浮动按钮:仅在非初始页面显示 -->
|
|
|
- <van-floating-bubble
|
|
|
- v-show="!showInitialPage"
|
|
|
- icon="checked"
|
|
|
- @click="handleSubmitWithConfirm"
|
|
|
- class="submit-floating-btn"
|
|
|
- />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-// ==================== Vue 核心导入 ====================
|
|
|
-import { ref, computed, nextTick, onMounted, watch } from 'vue'
|
|
|
-
|
|
|
-// ==================== Vant UI 组件导入 ====================
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
import {
|
|
|
showFailToast,
|
|
|
showNotify,
|
|
|
@@ -638,2714 +72,174 @@ import {
|
|
|
showConfirmDialog,
|
|
|
showImagePreview
|
|
|
} from 'vant'
|
|
|
-
|
|
|
-// ==================== 工具类导入 ====================
|
|
|
import { getHeader, goBack, scanError, scanSuccess } from '@/utils/android'
|
|
|
-import { useStore } from '@/store/modules/user'
|
|
|
-import { getStatus } from '@/utils/returned.ts'
|
|
|
-
|
|
|
-// ==================== API 接口导入 ====================
|
|
|
import {
|
|
|
- deleteDetails,
|
|
|
- getQualityInspection,
|
|
|
- getQualityInspectionBy,
|
|
|
- getQualityStatus,
|
|
|
- getReturnedByExpress,
|
|
|
- getTagColorBy,
|
|
|
- listAsn,
|
|
|
- matchAsnBy,
|
|
|
- matchCarrierCode,
|
|
|
- matchOrderBy,
|
|
|
- register,
|
|
|
- searchBarcode,
|
|
|
- searchOwnerBarcode,
|
|
|
- shops,
|
|
|
- validateDate
|
|
|
+ detailImageUpload,
|
|
|
+ returnedWorkbench
|
|
|
} from '@/api/returned/index.ts'
|
|
|
-import { carrierOptions, getOwner, getWarehouse } from '@/api/basic/index.ts'
|
|
|
|
|
|
-// ==================== 组件导入 ====================
|
|
|
-import Owner from '@/components/Owner.vue'
|
|
|
|
|
|
-// ==================== 初始化配置 ====================
|
|
|
try {
|
|
|
getHeader()
|
|
|
} catch (error) {
|
|
|
console.log(error)
|
|
|
}
|
|
|
|
|
|
-// ==================== 基础配置常量 ====================
|
|
|
-const store = useStore()
|
|
|
-const warehouse = store.warehouse
|
|
|
-
|
|
|
-// 日期范围配置
|
|
|
-const minDate = ref(new Date(new Date().getFullYear() - 5, 0, 1))
|
|
|
-const maxDate = ref(new Date((new Date().getFullYear() + 50, 0, 1)))
|
|
|
-
|
|
|
-// ==================== 页面状态管理 ====================
|
|
|
-// 页面显示控制
|
|
|
-const showInitialPage = ref(true) // 是否显示初始扫描页面
|
|
|
-const expressNo = ref(null) // 快递单号
|
|
|
-
|
|
|
-// 页面标题计算属性
|
|
|
-const title = computed(() => {
|
|
|
- if (expressNo.value && showInitialPage.value === false) {
|
|
|
- return '退货登记:' + params.value.returnNo
|
|
|
- }
|
|
|
- return '退货登记'
|
|
|
-})
|
|
|
-
|
|
|
-// ==================== 对话框状态管理 ====================
|
|
|
-// 选择器对话框状态
|
|
|
-const logisticPickerShow = ref(false) // 承运商选择器
|
|
|
-const ownerPickerShow = ref(false) // 货主选择器
|
|
|
-const warehousePickerShow = ref(false) // 仓库选择器
|
|
|
-const storePickerShow = ref(false) // 店铺选择器
|
|
|
-const showOwnerSelect = ref(false) // 多货主选择器
|
|
|
-
|
|
|
-// 业务对话框状态
|
|
|
-const qualityStatusDialog = ref(false) // 质量状态选择对话框
|
|
|
-const returnedDetailDialog = ref(false) // 商品详情对话框
|
|
|
-const selectedDetailQualityStatus = ref(false) // 详情中的质量状态选择器
|
|
|
-
|
|
|
-// 日期选择器状态
|
|
|
-const showManufactureTime = ref(false) // 生产日期选择器
|
|
|
-const showValidityTime = ref(false) // 失效日期选择器
|
|
|
-
|
|
|
-// 其他状态
|
|
|
-const showAccessories = ref(false) // 配件显示状态
|
|
|
+const workbench = ref({})
|
|
|
|
|
|
-// 检测是否有任何对话框显示
|
|
|
-const hasAnyDialogVisible = computed(() => {
|
|
|
- return qualityStatusDialog.value ||
|
|
|
- returnedDetailDialog.value ||
|
|
|
- logisticPickerShow.value ||
|
|
|
- warehousePickerShow.value ||
|
|
|
- storePickerShow.value ||
|
|
|
- ownerPickerShow.value ||
|
|
|
- showOwnerSelect.value ||
|
|
|
- showManufactureTime.value ||
|
|
|
- showValidityTime.value ||
|
|
|
- selectedDetailQualityStatus.value ||
|
|
|
- showAccessories.value
|
|
|
-})
|
|
|
-
|
|
|
-// ==================== 数据存储 ====================
|
|
|
-// 基础数据选项
|
|
|
-const qualityStatusOptions = ref([]) // 质量状态选项
|
|
|
-const owners = ref([]) // 货主列表
|
|
|
-const warehouses = ref([]) // 仓库列表
|
|
|
-const logistics = ref([]) // 承运商列表
|
|
|
-const storeOptions = ref([]) // 店铺选项
|
|
|
-const ownerSelectedOptions = ref([]) // 多货主选择选项
|
|
|
-
|
|
|
-// 数据映射对象
|
|
|
-const ownerMap = ref({}) // 货主编码到名称的映射
|
|
|
-const warehousesMap = ref({}) // 仓库编码到名称的映射
|
|
|
-const logisticsMap = ref({}) // 承运商映射
|
|
|
-
|
|
|
-// 业务数据
|
|
|
-const asnList = ref({}) // ASN单据列表
|
|
|
-const accessories = ref([]) // 配件信息
|
|
|
-const selectDetailIndex = ref(-1) // 选中的详情索引
|
|
|
-
|
|
|
-// 图片文件存储
|
|
|
-const boxFiles = ref([]) // 外箱图文件列表
|
|
|
-const productFiles = ref([]) // 内物图文件列表
|
|
|
-
|
|
|
-// ==================== 表单数据 ====================
|
|
|
-// 扫码相关
|
|
|
-const scancode = ref('') // 扫描的商品条码
|
|
|
-const qualityStatus = ref('') // 商品质量状态
|
|
|
-
|
|
|
-// 质检信息
|
|
|
-const qualityInspection = ref(null) // 质检配置
|
|
|
-const ownerQualityInspection = computed(() => {
|
|
|
- if (!qualityInspection.value || !params.value.ownerCode) {
|
|
|
- return '暂无质检信息'
|
|
|
- }
|
|
|
- if ('原单退回' === params.value.originalNo) {
|
|
|
- return '原单退回:快速质检'
|
|
|
- }
|
|
|
- return getQualityInspectionBy(qualityInspection.value) || '暂无质检信息'
|
|
|
-})
|
|
|
-
|
|
|
-// 其他表单字段
|
|
|
-const ownerName = ref('') // 货主名称
|
|
|
-
|
|
|
-// 主要提交参数
|
|
|
-const params = ref({
|
|
|
- id: null,
|
|
|
- returnNo: null,
|
|
|
- warehouseCode: warehouse,
|
|
|
- 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
|
|
|
-})
|
|
|
-
|
|
|
-// ==================== 组件引用 ====================
|
|
|
-const scanExpressNoInputRef = ref(null) // 快递单号输入框引用
|
|
|
-const productTabScancodeInputRef = ref(null) // 商品信息tab下的扫码输入框引用
|
|
|
-const ownerRef = ref(null) // 货主组件引用
|
|
|
+// 图片上传相关
|
|
|
+const outerImages = ref([]) // 外箱图
|
|
|
+const innerImages = ref([]) // 内物图
|
|
|
|
|
|
-// ==================== 图片预览相关状态 ====================
|
|
|
-const showPhotosPreview = ref(false) // 内物图预览状态
|
|
|
-const startPhotosPosition = ref(0) // 内物图预览起始位置
|
|
|
-const showBoxPreview = ref(false) // 外箱图预览状态
|
|
|
-const startBoxPosition = ref(0) // 外箱图预览起始位置
|
|
|
-
|
|
|
-// 图片预览计算属性
|
|
|
-const detailBoxImages = computed(() => {
|
|
|
- try {
|
|
|
- if (!selectDetail.value || !selectDetail.value.boxPhotos) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- const items = selectDetail.value.boxPhotos
|
|
|
- if (!Array.isArray(items) || items.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- if (!Array.isArray(boxFiles.value) || boxFiles.value.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- return boxFiles.value
|
|
|
- .filter((item) => {
|
|
|
- return item && item.fileName && items.includes(item.fileName)
|
|
|
- })
|
|
|
- .map((item) => item.src)
|
|
|
- .filter(Boolean) // 过滤掉可能的undefined值
|
|
|
- } catch (error) {
|
|
|
- console.error('detailBoxImages 计算错误:', error)
|
|
|
- return []
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-const detailProductImages = computed(() => {
|
|
|
- try {
|
|
|
- if (!selectDetail.value || !selectDetail.value.productPhotos) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- const items = selectDetail.value.productPhotos
|
|
|
- if (!Array.isArray(items) || items.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- if (!Array.isArray(productFiles.value) || productFiles.value.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- return productFiles.value
|
|
|
- .filter((item) => {
|
|
|
- return item && item.fileName && items.includes(item.fileName)
|
|
|
- })
|
|
|
- .map((item) => item.src)
|
|
|
- .filter(Boolean) // 过滤掉可能的undefined值
|
|
|
- } catch (error) {
|
|
|
- console.error('detailProductImages 计算错误:', error)
|
|
|
- return []
|
|
|
- }
|
|
|
+onMounted(()=>{
|
|
|
+ getWorkbench()
|
|
|
})
|
|
|
|
|
|
-// ==================== 生命周期钩子 ====================
|
|
|
-onMounted(() => {
|
|
|
- // 初始化日期范围
|
|
|
- const now = new Date()
|
|
|
- const currentYear = now.getFullYear()
|
|
|
- const currentCentury = Math.floor(currentYear / 100) * 100
|
|
|
- const endOfCenturyYear = currentCentury + 99
|
|
|
-
|
|
|
- maxDate.value = new Date(endOfCenturyYear, 0, 1)
|
|
|
- minDate.value = new Date(currentCentury, 0, 1)
|
|
|
- // 初始化基础数据
|
|
|
- initializeBaseData()
|
|
|
-
|
|
|
- // 初始化时自动聚焦快递单号输入框
|
|
|
- nextTick(() => {
|
|
|
- focusExpressNoInput()
|
|
|
+function getWorkbench(){
|
|
|
+ returnedWorkbench().then(res=>{
|
|
|
+ workbench.value = res.data
|
|
|
})
|
|
|
-})
|
|
|
-
|
|
|
-// ==================== 智能聚焦逻辑 ====================
|
|
|
-// 聚焦快递单号输入框
|
|
|
-function focusExpressNoInput() {
|
|
|
- if (scanExpressNoInputRef.value && showInitialPage.value) {
|
|
|
- setTimeout(() => {
|
|
|
- const input = scanExpressNoInputRef.value?.$el?.querySelector('input')
|
|
|
- if (input) {
|
|
|
- input.focus()
|
|
|
- input.setSelectionRange(0, input.value.length)
|
|
|
- console.log('快递单号输入框已聚焦')
|
|
|
- }
|
|
|
- }, 150) // 增加延迟时间确保 DOM 完全渲染
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 聚焦商品信息输入框
|
|
|
-function focusProductInput() {
|
|
|
- if (productTabScancodeInputRef.value && !showInitialPage.value) {
|
|
|
- setTimeout(() => {
|
|
|
- const input = productTabScancodeInputRef.value?.$el?.querySelector('input')
|
|
|
- if (input) {
|
|
|
- input.focus()
|
|
|
- input.setSelectionRange(0, input.value.length)
|
|
|
- console.log('商品条码输入框已聚焦')
|
|
|
- }
|
|
|
- }, 150) // 增加延迟时间确保DOM完全渲染
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 监听对话框状态变化,当所有对话框关闭时自动聚焦
|
|
|
-watch(hasAnyDialogVisible, (newVal) => {
|
|
|
- if (!newVal) {
|
|
|
- nextTick(() => {
|
|
|
- if (showInitialPage.value) {
|
|
|
- focusExpressNoInput()
|
|
|
- } else {
|
|
|
- focusProductInput()
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// 监听页面显示状态变化
|
|
|
-watch(showInitialPage, (newVal) => {
|
|
|
- nextTick(() => {
|
|
|
- if (newVal) {
|
|
|
- // 切换到初始扫描页面时,聚焦快递单号输入框
|
|
|
- setTimeout(() => {
|
|
|
- focusExpressNoInput()
|
|
|
- }, 300)
|
|
|
- } else {
|
|
|
- // 页面从初始扫描状态切换到详情页面时,延迟聚焦商品输入框
|
|
|
- setTimeout(() => {
|
|
|
- focusProductInput()
|
|
|
- }, 300)
|
|
|
- }
|
|
|
- })
|
|
|
-})
|
|
|
-
|
|
|
-// 监听商品详情对话框关闭,自动聚焦到商品条码输入框
|
|
|
-watch(returnedDetailDialog, (newVal) => {
|
|
|
- if (!newVal && !showInitialPage.value) {
|
|
|
- nextTick(() => {
|
|
|
- focusProductInput()
|
|
|
- })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// ==================== 初始化方法 ====================
|
|
|
-// 初始化基础数据
|
|
|
-async function initializeBaseData() {
|
|
|
- try {
|
|
|
- // 并行获取基础数据
|
|
|
- const [ownersRes, qualityStatusRes, warehousesRes, logisticsRes] = await Promise.all([
|
|
|
- getOwner(),
|
|
|
- getQualityStatus(),
|
|
|
- getWarehouse(),
|
|
|
- carrierOptions()
|
|
|
- ])
|
|
|
-
|
|
|
- // 处理货主数据
|
|
|
- const ownersData = ownersRes.data
|
|
|
- owners.value = ownersData.map((item) => ({ value: item.code, text: item.name }))
|
|
|
- const ownerMapping = {}
|
|
|
- ownersData.forEach((item) => {
|
|
|
- ownerMapping[item.code] = item.name
|
|
|
- })
|
|
|
- ownerMap.value = ownerMapping
|
|
|
-
|
|
|
- // 处理质量状态数据
|
|
|
- qualityStatusOptions.value = qualityStatusRes.data
|
|
|
- .filter((item) => item !== '机损')
|
|
|
- .map((item) => ({ value: item, text: item }))
|
|
|
-
|
|
|
- // 处理仓库数据
|
|
|
- const warehousesData = warehousesRes.data
|
|
|
- warehouses.value = warehousesData.map((item) => ({ value: item.code, text: item.name }))
|
|
|
- const warehouseMapping = {}
|
|
|
- warehousesData.forEach((item) => {
|
|
|
- warehouseMapping[item.code] = item.name
|
|
|
- })
|
|
|
- warehousesMap.value = warehouseMapping
|
|
|
-
|
|
|
- // 处理承运商数据
|
|
|
- const logisticsData = logisticsRes.data
|
|
|
- logistics.value = logisticsData.map((item) => ({ value: item.name, text: item.name }))
|
|
|
- const logisticsMapping = {}
|
|
|
- logisticsData.forEach((item) => {
|
|
|
- logisticsMapping[item.name] = item.name
|
|
|
- })
|
|
|
- logisticsMap.value = logisticsMapping
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- console.error('初始化基础数据失败:', error)
|
|
|
- showFailToast('初始化数据失败,请刷新页面重试')
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 初始化商品详情
|
|
|
-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: [],
|
|
|
- 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 initQualityInspection(owner) {
|
|
|
- getQualityInspection(owner).then((res) => {
|
|
|
- qualityInspection.value = res.data
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 重置提交,恢复到扫描快递单号页面
|
|
|
-function init(showInputPage = true) {
|
|
|
- ownerName.value = null
|
|
|
- clearImageFileCache()
|
|
|
- params.value = {
|
|
|
- orderUpstream: null,
|
|
|
- returnNo: null,
|
|
|
- warehouseCode: warehouse,
|
|
|
- logisticsName: null,
|
|
|
- storeName: null,
|
|
|
- orderUpSteam: null,
|
|
|
- arrivalPayment: null,
|
|
|
- buyerName: null,
|
|
|
- asnNo: null,
|
|
|
- upstreamNo: null,
|
|
|
- buyerPhone: null,
|
|
|
- originalNo: null,
|
|
|
- remark: null,
|
|
|
- details: []
|
|
|
- }
|
|
|
- storeOptions.value = []
|
|
|
- expressNo.value = null
|
|
|
- showInitialPage.value = showInputPage
|
|
|
- initDetail()
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 业务工具方法 ====================
|
|
|
-// 获取店铺信息
|
|
|
-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.name, text: item.name }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 获取承运商名称
|
|
|
-function getLogisticName() {
|
|
|
- return params.value?.logisticsName || ''
|
|
|
-}
|
|
|
-
|
|
|
-// 获取货主名称
|
|
|
-function getOwnerName(owner) {
|
|
|
- if (!owner || !ownerMap.value) {
|
|
|
- return ''
|
|
|
- }
|
|
|
- return ownerMap.value[owner] || ''
|
|
|
-}
|
|
|
-
|
|
|
-// 获取仓库名称
|
|
|
-function getWarehouseName() {
|
|
|
- if (!params.value?.warehouseCode || !warehousesMap.value) {
|
|
|
- return ''
|
|
|
- }
|
|
|
- console.log(params.value.warehouseCode)
|
|
|
- return warehousesMap.value[params.value.warehouseCode] || ''
|
|
|
-}
|
|
|
-
|
|
|
-// 获取质量状态标签颜色
|
|
|
-function getTagColor(qualityStatus) {
|
|
|
- if (!qualityStatus) {
|
|
|
- return '#999999' // 默认颜色
|
|
|
- }
|
|
|
- return getTagColorBy(qualityStatus)
|
|
|
-}
|
|
|
-
|
|
|
-// 转化加密文本(长文本显示为"加密")
|
|
|
-function handlerLongText(text) {
|
|
|
- return text && text.length > 15 ? '加密' : text ? text : null
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 选择器处理方法 ====================
|
|
|
-// 承运商选择处理
|
|
|
-const selectedLogistic = (row) => {
|
|
|
- const { selectedOptions } = row
|
|
|
- logisticPickerShow.value = false
|
|
|
- params.value.logisticsName = selectedOptions[0].text
|
|
|
-}
|
|
|
-
|
|
|
-// 货主选择处理
|
|
|
-const selectedOwner = (row) => {
|
|
|
- const { selectedValues, selectedOptions } = row
|
|
|
- ownerPickerShow.value = false
|
|
|
- params.value.ownerCode = selectedOptions[0].value
|
|
|
- ownerName.value = selectedOptions[0].text
|
|
|
- initQualityInspection(params.value.ownerCode)
|
|
|
-}
|
|
|
-
|
|
|
-// 仓库选择处理
|
|
|
-const selectedWarehouse = (row) => {
|
|
|
- const { selectedOptions } = row
|
|
|
- warehousePickerShow.value = false
|
|
|
- params.value.warehouseCode = selectedOptions[0].value
|
|
|
-}
|
|
|
-
|
|
|
-// 店铺选择处理
|
|
|
-const selectedStore = (row) => {
|
|
|
- const { selectedOptions } = row
|
|
|
- storePickerShow.value = false
|
|
|
- params.value.storeName = selectedOptions[0].text
|
|
|
-}
|
|
|
-
|
|
|
-// 货主选择(多选场景)
|
|
|
-function selectOwner({ selectedValues }) {
|
|
|
- const ownerCode = selectedValues[0]
|
|
|
- params.value.ownerCode = ownerCode
|
|
|
- showOwnerSelect.value = false
|
|
|
- ownerSelectedOptions.value = []
|
|
|
- getStoreOptionsBy(ownerCode)
|
|
|
- queryOwnerBarcode(params.value.ownerCode, scancode.value)
|
|
|
-}
|
|
|
-
|
|
|
-// 显示货主选择器
|
|
|
-function showOwnerSelectFunc() {
|
|
|
- ownerRef.value?.show(1)
|
|
|
-}
|
|
|
-
|
|
|
-// 货主组件回调
|
|
|
-const onOwner = (item, type) => {
|
|
|
- params.value.ownerCode = item.code
|
|
|
- getStoreOptionsBy(item.code)
|
|
|
-}
|
|
|
-
|
|
|
-// 多货主选择对话框
|
|
|
-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
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 快递单号处理方法 ====================
|
|
|
-// 输入快递单号处理
|
|
|
-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('快递单号异常')
|
|
|
- scanError()
|
|
|
- return
|
|
|
- }
|
|
|
- params.value.returnNo = express_no
|
|
|
- params.value.warehouseCode = warehouse
|
|
|
- listAsn(express_no).then((res) => {
|
|
|
- if (res.data) {
|
|
|
- asnList.value = res.data
|
|
|
- }
|
|
|
- })
|
|
|
- matchReturned(express_no)
|
|
|
-}
|
|
|
-
|
|
|
-// 校验快递单号格式
|
|
|
-function checkExpressNo(no) {
|
|
|
- if (!no || no.length === 0) {
|
|
|
- showFailToast('单号长度异常')
|
|
|
- scanError()
|
|
|
- return false
|
|
|
- }
|
|
|
- if (no.startsWith('http')) {
|
|
|
- showFailToast('扫描到了错误的条码')
|
|
|
- scanError()
|
|
|
- return false
|
|
|
- }
|
|
|
- if (no.endsWith('.com')) {
|
|
|
- showFailToast('扫描到了错误的条码')
|
|
|
- scanError()
|
|
|
- return false
|
|
|
- }
|
|
|
- const length = no.length
|
|
|
- if (length > 30 || length < 5) {
|
|
|
- showFailToast('单号长度异常')
|
|
|
- scanError()
|
|
|
- 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 matchReturned(express_no) {
|
|
|
- getReturnedByExpress(express_no).then((res) => {
|
|
|
- const { data } = res
|
|
|
- if (!data) {
|
|
|
- matchOrder(express_no)
|
|
|
- return
|
|
|
- }
|
|
|
- const { id, headStatus, returnNo, warehouseCode } = data
|
|
|
- if (!id) {
|
|
|
- matchOrder(express_no)
|
|
|
- expressNo.value = null
|
|
|
- showInitialPage.value = false
|
|
|
- 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(express_no)
|
|
|
- expressNo.value = null
|
|
|
- showInitialPage.value = false
|
|
|
- scanSuccess()
|
|
|
- } else {
|
|
|
- showNotify({
|
|
|
- type: 'danger',
|
|
|
- message: '当前退回单号已进行过登记 如需修改请前往PC端进行'
|
|
|
- })
|
|
|
- scanSuccess()
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 匹配原单信息
|
|
|
-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
|
|
|
- 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)
|
|
|
- getStoreOptionsBy(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 = '正品'
|
|
|
- } 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
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 匹配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
|
|
|
- params.value.storeName = shopName
|
|
|
- params.value.logisticsName = logisticName
|
|
|
- params.value.buyerName = handlerLongText(recipientName)
|
|
|
- params.value.buyerPhone = handlerLongText(phone)
|
|
|
- params.value.originalNo = null
|
|
|
- params.value.sendNo = null
|
|
|
- params.value.details = []
|
|
|
- matchCarrier(expressNo)
|
|
|
- getStoreOptionsBy(ownerCode)
|
|
|
- initQualityInspection(params.value.ownerCode)
|
|
|
- initDetail()
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 商品信息处理工具方法 ====================
|
|
|
-// 检查商品是否有外箱图
|
|
|
-function hasBoxItems(detail) {
|
|
|
- try {
|
|
|
- if (!detail || typeof detail !== 'object') {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- const { boxPhotos } = detail
|
|
|
- if (!Array.isArray(boxPhotos) || boxPhotos.length === 0) {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- if (!Array.isArray(boxFiles.value) || boxFiles.value.length === 0) {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- return boxFiles.value.some((item) => {
|
|
|
- return item && item.fileName && boxPhotos.includes(item.fileName)
|
|
|
- })
|
|
|
- } catch (error) {
|
|
|
- console.error('hasBoxItems 错误:', error)
|
|
|
- return false
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 获取商品外箱图列表
|
|
|
-function getBoxItems(detail) {
|
|
|
- try {
|
|
|
- if (!detail || typeof detail !== 'object') {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- const { boxPhotos } = detail
|
|
|
- if (!Array.isArray(boxPhotos) || boxPhotos.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- if (!Array.isArray(boxFiles.value) || boxFiles.value.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- return boxFiles.value.filter((item) => {
|
|
|
- return item && item.fileName && boxPhotos.includes(item.fileName)
|
|
|
- })
|
|
|
- } catch (error) {
|
|
|
- console.error('getBoxItems 错误:', error)
|
|
|
- return []
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 检查商品是否有内物图
|
|
|
-function hasProductItems(detail) {
|
|
|
- try {
|
|
|
- if (!detail || typeof detail !== 'object') {
|
|
|
+const beforeReadImage = (file) => {
|
|
|
+ const files = Array.isArray(file) ? file : [file]
|
|
|
+ for (const f of files) {
|
|
|
+ const isImage = /^image\//.test(f.type)
|
|
|
+ if (!isImage) {
|
|
|
+ showFailToast('仅支持图片文件')
|
|
|
return false
|
|
|
}
|
|
|
-
|
|
|
- const { productPhotos } = detail
|
|
|
- if (!Array.isArray(productPhotos) || productPhotos.length === 0) {
|
|
|
+ if (f.size > 10 * 1024 * 1024) {
|
|
|
+ showFailToast('图片大小不能超过10MB')
|
|
|
return false
|
|
|
}
|
|
|
-
|
|
|
- if (!Array.isArray(productFiles.value) || productFiles.value.length === 0) {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- return productFiles.value.some((item) => {
|
|
|
- return item && item.fileName && productPhotos.includes(item.fileName)
|
|
|
- })
|
|
|
- } catch (error) {
|
|
|
- console.error('hasProductItems 错误:', error)
|
|
|
- return false
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-// 获取商品内物图列表
|
|
|
-function getProductItems(detail) {
|
|
|
- try {
|
|
|
- if (!detail || typeof detail !== 'object') {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- const { productPhotos } = detail
|
|
|
- if (!Array.isArray(productPhotos) || productPhotos.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- if (!Array.isArray(productFiles.value) || productFiles.value.length === 0) {
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- return productFiles.value.filter((item) => {
|
|
|
- return item && item.fileName && productPhotos.includes(item.fileName)
|
|
|
- })
|
|
|
- } catch (error) {
|
|
|
- console.error('getProductItems 错误:', error)
|
|
|
- return []
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 数据查询方法 ====================
|
|
|
-// 选择质量状态
|
|
|
-function showQualityStatus() {
|
|
|
- const barcode = scancode.value
|
|
|
- if (!barcode) {
|
|
|
- showFailToast('商品条码异常')
|
|
|
- return
|
|
|
- }
|
|
|
- scanSuccess()
|
|
|
- qualityStatusDialog.value = true
|
|
|
- qualityStatus.value = '正品'
|
|
|
-}
|
|
|
-
|
|
|
-// 查询商品条码信息
|
|
|
-async function queryBarcode() {
|
|
|
- showLoadingToast({
|
|
|
- duration: 0,
|
|
|
- forbidClick: true,
|
|
|
- message: '查询商品信息中.....'
|
|
|
- })
|
|
|
- const barcode = scancode.value
|
|
|
- const { ownerCode, details } = params.value
|
|
|
- if (ownerCode) {
|
|
|
- const result = await queryOwnerBarcode(ownerCode, barcode)
|
|
|
- if (result) {
|
|
|
- scanSuccess()
|
|
|
- qualityStatusDialog.value = false
|
|
|
- closeToast()
|
|
|
- // 查询成功后清空条码输入框并聚焦
|
|
|
- scancode.value = ''
|
|
|
- setTimeout(() => {
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- return
|
|
|
- }
|
|
|
- }
|
|
|
- if (!details || details.length === 0) {
|
|
|
- await queryBarcodeBy(barcode)
|
|
|
- qualityStatusDialog.value = false
|
|
|
- closeToast()
|
|
|
- // 查询完成后清空条码输入框并聚焦
|
|
|
- scancode.value = ''
|
|
|
- setTimeout(() => {
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- }
|
|
|
-}
|
|
|
-// 根据货主查询商品条码信息
|
|
|
-async function queryOwnerBarcode(ownerCode, barcode) {
|
|
|
- try {
|
|
|
- // 参数验证
|
|
|
- if (!ownerCode || !barcode) {
|
|
|
- console.error('queryOwnerBarcode: 参数不完整', { ownerCode, barcode })
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- const results = await searchOwnerBarcode({ ownerCode, barcode })
|
|
|
-
|
|
|
- // 响应数据验证
|
|
|
- if (!results || !results.data) {
|
|
|
- console.error('queryOwnerBarcode: 响应数据格式错误', results)
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- const { data: { basSku } } = results
|
|
|
-
|
|
|
- if (!basSku) {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- addDetailByBasSku(basSku)
|
|
|
- return true
|
|
|
- } catch (err) {
|
|
|
- closeToast()
|
|
|
- console.error('queryOwnerBarcode 错误:', err)
|
|
|
- showFailToast('查询商品信息失败')
|
|
|
- }
|
|
|
- return false
|
|
|
-}
|
|
|
-
|
|
|
-// 查询商品条码信息(适用于没有指定货主的情况)
|
|
|
-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) {
|
|
|
- scanError()
|
|
|
- return null
|
|
|
- }
|
|
|
- addDetailByBasSku(basSku)
|
|
|
- scanSuccess()
|
|
|
- return null
|
|
|
- } catch (err) {
|
|
|
- closeToast()
|
|
|
- scanError()
|
|
|
- console.log(err.message)
|
|
|
- }
|
|
|
- scanError()
|
|
|
- return null
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 商品详情处理方法 ====================
|
|
|
-// 根据Sku信息添加商品详情
|
|
|
-function addDetailByBasSku(basSku) {
|
|
|
- // 参数安全性检查
|
|
|
- if (!basSku || typeof basSku !== 'object') {
|
|
|
- console.error('addDetailByBasSku: basSku参数无效', basSku)
|
|
|
- showFailToast('商品信息获取失败')
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const { sku, barCode, ownerCode, tradeName, accessories } = basSku
|
|
|
-
|
|
|
- // 必要字段验证
|
|
|
- if (!sku || !barCode) {
|
|
|
- console.error('addDetailByBasSku: 缺少必要的商品信息', { sku, barCode })
|
|
|
- showFailToast('商品信息不完整')
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- selectDetailIndex.value = -1
|
|
|
- selectDetail.value.sku = sku
|
|
|
-
|
|
|
- if (
|
|
|
- !params.value.ownerCode ||
|
|
|
- !params.value.details ||
|
|
|
- params.value.details.length === 0
|
|
|
- ) {
|
|
|
- params.value.ownerCode = ownerCode
|
|
|
- if (ownerCode) {
|
|
|
- initQualityInspection(params.value.ownerCode)
|
|
|
- getStoreOptionsBy(params.value.ownerCode)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- selectDetail.value.barCode = barCode
|
|
|
- selectDetail.value.qualityStatus = qualityStatus.value || '正品'
|
|
|
- selectDetail.value.tradeName = tradeName || ''
|
|
|
- selectDetail.value.number = 1
|
|
|
- qualityStatusDialog.value = false
|
|
|
- returnedDetailDialog.value = true
|
|
|
-
|
|
|
- // 显示配件信息
|
|
|
- showAccessoriesItems(accessories)
|
|
|
-}
|
|
|
-
|
|
|
-// 显示配件信息
|
|
|
-function showAccessoriesItems(items) {
|
|
|
- if (!items || items.length === 0) {
|
|
|
- accessories.value = []
|
|
|
- showAccessories.value = false
|
|
|
- return
|
|
|
- }
|
|
|
- accessories.value = items
|
|
|
- showAccessories.value = true
|
|
|
-}
|
|
|
-
|
|
|
-// 编辑商品详情
|
|
|
-function editDetails(index) {
|
|
|
- selectDetailIndex.value = index
|
|
|
- const { details } = params.value
|
|
|
- selectDetail.value = JSON.parse(JSON.stringify(details[index]))
|
|
|
- returnedDetailDialog.value = true
|
|
|
-}
|
|
|
-
|
|
|
-// 删除商品详情
|
|
|
-function removeDetails(index) {
|
|
|
- if (null === index) {
|
|
|
- showFailToast('未找到对应的详情')
|
|
|
- return
|
|
|
- }
|
|
|
- showConfirmDialog({
|
|
|
- title: '系统提示',
|
|
|
- message: '是否删除当前登记信息'
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- const { id } = params.value.details[index]
|
|
|
- if (!id) {
|
|
|
- params.value.details.splice(index, 1)
|
|
|
- showNotify({ type: 'success', message: '删除成功' })
|
|
|
- } else {
|
|
|
- deleteDetails(id).then((res) => {
|
|
|
- if (res.data) {
|
|
|
- params.value.details.splice(index, 1)
|
|
|
- showNotify({ type: 'success', message: '删除成功' })
|
|
|
- } else {
|
|
|
- showNotify({ type: 'danger', message: '删除失败' })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- showNotify({ type: 'primary', message: '取消删除' })
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 添加商品详情到列表
|
|
|
-const addDetails = async () => {
|
|
|
- const isCheck = checkUploadImages()
|
|
|
- if (!isCheck) {
|
|
|
- returnedDetailDialog.value = true
|
|
|
- return false
|
|
|
- }
|
|
|
- const index = selectDetailIndex.value
|
|
|
- const detail = JSON.parse(JSON.stringify(selectDetail.value))
|
|
|
- if (index > -1) {
|
|
|
- params.value.details[index] = detail
|
|
|
- initDetail()
|
|
|
- // 编辑完成后聚焦到商品条码输入框
|
|
|
- setTimeout(() => {
|
|
|
- scancode.value = ''
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- return true
|
|
|
- }
|
|
|
- const {
|
|
|
- sku: detail_sku,
|
|
|
- batchNumber: detail_batch_number,
|
|
|
- validityTime: detail_validity_time,
|
|
|
- manufactureTime: detail_manufacture_time,
|
|
|
- qualityStatus: detail_quality_status,
|
|
|
- number: detailNumber
|
|
|
- } = selectDetail.value
|
|
|
- if (['次品', '待修复'].includes(detail_quality_status)) {
|
|
|
- params.value.details.push(detail)
|
|
|
- initDetail()
|
|
|
- // 添加完成后聚焦到商品条码输入框
|
|
|
- setTimeout(() => {
|
|
|
- scancode.value = ''
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- return
|
|
|
- }
|
|
|
- const searchIndex = params.value.details.findIndex((item) => {
|
|
|
- const { sku, batchNumber, qualityStatus, validityTime, manufactureTime } =
|
|
|
- item
|
|
|
- return (
|
|
|
- sku === detail_sku &&
|
|
|
- batchNumber === detail_batch_number &&
|
|
|
- detail_validity_time === validityTime &&
|
|
|
- detail_manufacture_time === manufactureTime &&
|
|
|
- detail_quality_status === qualityStatus
|
|
|
- )
|
|
|
- })
|
|
|
- if (searchIndex > -1) {
|
|
|
- showConfirmDialog({
|
|
|
- title: '系统提示',
|
|
|
- message: '查询到有对应的商品详情是否进行合并'
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- const { number } = params.value.details[searchIndex]
|
|
|
- params.value.details[searchIndex].number =
|
|
|
- Number(detailNumber) + Number(number)
|
|
|
- showNotify({ type: 'success', message: '合并成功' })
|
|
|
- // 合并完成后聚焦
|
|
|
- setTimeout(() => {
|
|
|
- scancode.value = ''
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- params.value.details.push(detail)
|
|
|
- showNotify({ type: 'success', message: '添加成功' })
|
|
|
- // 添加完成后聚焦
|
|
|
- setTimeout(() => {
|
|
|
- scancode.value = ''
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- })
|
|
|
- } else {
|
|
|
- params.value.details.push(detail)
|
|
|
- showNotify({ type: 'success', message: '添加成功' })
|
|
|
- // 添加完成后聚焦
|
|
|
- setTimeout(() => {
|
|
|
- scancode.value = ''
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
- }
|
|
|
- initDetail()
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
-// 取消商品详情对话框
|
|
|
-const cancelReturnedDetailDialog = async () => {
|
|
|
- returnedDetailDialog.value = false
|
|
|
- initDetail()
|
|
|
- // 取消对话框后自动聚焦到商品条码输入框
|
|
|
- setTimeout(() => {
|
|
|
- focusProductInput()
|
|
|
- }, 100)
|
|
|
+// 提交时上传:这里提供示例函数,后续可替换为真实上传API
|
|
|
+const simulateUpload = async (file,type) => {
|
|
|
+ // 模拟网络耗时
|
|
|
+ const data = new FormData()
|
|
|
+ data.set('file',file)
|
|
|
+ data.set('type',type)
|
|
|
+ await detailImageUpload(data)
|
|
|
+ // 返回可预览URL(示例)
|
|
|
+ return { url: URL.createObjectURL(file) }
|
|
|
}
|
|
|
|
|
|
-// ==================== 质量状态和日期处理方法 ====================
|
|
|
-// 质量状态选择处理
|
|
|
-const selectedDetailQualityStatusFunc = (row) => {
|
|
|
- const { selectedOptions } = row
|
|
|
- selectedDetailQualityStatus.value = false
|
|
|
- selectDetail.value.qualityStatus = selectedOptions[0].text
|
|
|
-
|
|
|
- // 质量状态变化后重新检查图片上传状态
|
|
|
- nextTick(() => {
|
|
|
- // 触发响应式更新,让对话框重新计算确认按钮状态
|
|
|
- checkUploadImages()
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 日期显示格式化
|
|
|
-const formatDateDisplay = (dateString) => {
|
|
|
- if (!dateString) return ''
|
|
|
- return dateString.replace(/-/g, '/') // 将 2023-05-01 显示为 2023/05/01
|
|
|
-}
|
|
|
-
|
|
|
-// 日期字符串转数组格式 [year, month, day]
|
|
|
-const parseDateValue = (dateString) => {
|
|
|
- if (!dateString) return []
|
|
|
- const [year, month, day] = dateString.split('-')
|
|
|
- return [year, month, day]
|
|
|
-}
|
|
|
-
|
|
|
-// 生产日期确认
|
|
|
-const manufactureTimeConfirm = async (result) => {
|
|
|
- if (!result.selectedValues) {
|
|
|
- showManufactureTime.value = false
|
|
|
- return
|
|
|
- }
|
|
|
- // 正确解构参数
|
|
|
- const { selectedValues } = result
|
|
|
- const [year, month, day] = selectedValues
|
|
|
- const formatted_month = month.padStart(2, '0')
|
|
|
- const formatted_day = day.padStart(2, '0')
|
|
|
- const manufactureTime = `${year}-${formatted_month}-${formatted_day}`
|
|
|
- const { sku } = selectDetail.value
|
|
|
- const { ownerCode } = params.value
|
|
|
- const check_validate_date = await checkValidateDate(
|
|
|
- ownerCode,
|
|
|
- sku,
|
|
|
- manufactureTime,
|
|
|
- 'manufactureTime'
|
|
|
- )
|
|
|
- if (!check_validate_date) {
|
|
|
- console.log('检验出现异常')
|
|
|
- return
|
|
|
- }
|
|
|
- selectDetail.value.manufactureTime = manufactureTime
|
|
|
- showManufactureTime.value = false
|
|
|
-}
|
|
|
-
|
|
|
-// 失效日期确认
|
|
|
-const validityTimeConfirm = async (result) => {
|
|
|
- if (!result.selectedValues) {
|
|
|
- showValidityTime.value = false
|
|
|
- return
|
|
|
- }
|
|
|
- // 正确解构参数
|
|
|
- const { selectedValues } = result
|
|
|
- const [year, month, day] = selectedValues
|
|
|
- const formatted_month = month.padStart(2, '0')
|
|
|
- const formatted_day = day.padStart(2, '0')
|
|
|
- const validityTime = `${year}-${formatted_month}-${formatted_day}`
|
|
|
- const { sku } = selectDetail.value
|
|
|
- const { ownerCode } = params.value
|
|
|
- const check_validate_date = await checkValidateDate(
|
|
|
- ownerCode,
|
|
|
- sku,
|
|
|
- validityTime,
|
|
|
- 'validityTime'
|
|
|
- )
|
|
|
- if (!check_validate_date) {
|
|
|
- return
|
|
|
- }
|
|
|
- selectDetail.value.validityTime = `${year}-${formatted_month}-${formatted_day}`
|
|
|
- showValidityTime.value = false
|
|
|
-}
|
|
|
-
|
|
|
-// 校验日期合法性
|
|
|
-async function checkValidateDate(ownerCode, sku, newDate, fieldName) {
|
|
|
- if (['NOSKU', 'NOBARCODE'].includes(sku)) {
|
|
|
- return true
|
|
|
- }
|
|
|
- const type = fieldName === 'manufactureTime' ? '生产日期' : '失效日期'
|
|
|
- try {
|
|
|
- await validateDate({
|
|
|
- newDate: newDate,
|
|
|
- fieldName: fieldName,
|
|
|
- ownerCode: ownerCode,
|
|
|
- sku: sku
|
|
|
- })
|
|
|
- showNotify({
|
|
|
- type: 'success',
|
|
|
- message: `${type} 状态校验成功 `
|
|
|
- })
|
|
|
- return true
|
|
|
- } catch (error) {
|
|
|
- showNotify({
|
|
|
- type: 'danger',
|
|
|
- message: `校验${type}出现异常`
|
|
|
- })
|
|
|
- return false
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 图片处理方法 ====================
|
|
|
-// Blob转Base64
|
|
|
-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'))
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 获取图片URL
|
|
|
-function getImageUrl(file_name, 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 (file_name === fileName) {
|
|
|
- return src
|
|
|
+const uploadCategory = async (list, categoryName) => {
|
|
|
+ // 逐张上传,保持与“单张选择”一致
|
|
|
+ for (const item of list) {
|
|
|
+ const file = item.file || item
|
|
|
+ try {
|
|
|
+ const res = await simulateUpload(file,categoryName)
|
|
|
+ item.url = res.url
|
|
|
+ } catch (e) {
|
|
|
+ throw new Error(`${categoryName}上传失败`)
|
|
|
}
|
|
|
}
|
|
|
- return null
|
|
|
}
|
|
|
|
|
|
-// 处理图片文件输入
|
|
|
-function pushImageItem(event, type) {
|
|
|
- const file = event.target.files[0]
|
|
|
- if (!file) return
|
|
|
-
|
|
|
- if (!file.type.startsWith('image/')) {
|
|
|
- alert('请选择图片文件!')
|
|
|
+const previewAll = () => {
|
|
|
+ const images = [...outerImages.value, ...innerImages.value]
|
|
|
+ .map((it) => (it.url || it.content))
|
|
|
+ .filter(Boolean)
|
|
|
+ if (!images.length) {
|
|
|
+ showNotify({ type: 'warning', message: '暂无可预览的图片' })
|
|
|
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 outerCartonInput(event) {
|
|
|
- pushImageItem(event, '外箱图')
|
|
|
-}
|
|
|
-
|
|
|
-// 内物图文件输入事件
|
|
|
-function innerContentsInput(event) {
|
|
|
- pushImageItem(event, '内物图')
|
|
|
-}
|
|
|
-
|
|
|
-// 清除图片文件缓存
|
|
|
-function clearImageFileCache() {
|
|
|
- if (productFiles.value && productFiles.value.length > 0) {
|
|
|
- productFiles.value
|
|
|
- .map((item) => item.src)
|
|
|
- .forEach((item) => URL.revokeObjectURL(item))
|
|
|
- }
|
|
|
- if (boxFiles.value && boxFiles.value.length > 0) {
|
|
|
- boxFiles.value
|
|
|
- .map((item) => item.src)
|
|
|
- .forEach((item) => URL.revokeObjectURL(item))
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 校验图片上传
|
|
|
-function checkUploadImages() {
|
|
|
- const status = ['次品', '待修复']
|
|
|
- const { productPhotos, boxPhotos, qualityStatus } = selectDetail.value
|
|
|
- if (!status.includes(qualityStatus)) {
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
- if (!qualityStatus) {
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
- const boxPhotoIsNull = !boxPhotos || boxPhotos.length === 0
|
|
|
- const productPhotoIsNull = !productPhotos || productPhotos.length === 0
|
|
|
-
|
|
|
- if (boxPhotoIsNull) {
|
|
|
- showNotify({
|
|
|
- type: 'warning',
|
|
|
- message: '请传入对应的外箱图'
|
|
|
- })
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- if (productPhotoIsNull) {
|
|
|
- showNotify({
|
|
|
- type: 'warning',
|
|
|
- message: '请传入对应的内物图'
|
|
|
- })
|
|
|
- return false
|
|
|
- }
|
|
|
- return true
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 图片预览方法 ====================
|
|
|
-// 显示内物图预览
|
|
|
-const showPhotosImagePreview = (index) => {
|
|
|
- startPhotosPosition.value = index
|
|
|
- showPhotosPreview.value = true
|
|
|
-}
|
|
|
-
|
|
|
-// 内物图预览位置变化
|
|
|
-const onPhotosPreviewChange = (index) => {
|
|
|
- startPhotosPosition.value = index
|
|
|
-}
|
|
|
-
|
|
|
-// 删除内物图
|
|
|
-const handlePhotosDelete = () => {
|
|
|
- showConfirmDialog({
|
|
|
- title: '提示',
|
|
|
- message: '确定要删除这张图片吗?'
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- selectDetail.value.productPhotos.splice(startPhotosPosition.value, 1)
|
|
|
- showNotify({ type: 'success', message: '删除成功' })
|
|
|
- if (selectDetail.value.productPhotos.length === 0) {
|
|
|
- showPhotosPreview.value = false
|
|
|
- } else if (
|
|
|
- startPhotosPosition.value >= selectDetail.value.productPhotos.length
|
|
|
- ) {
|
|
|
- startPhotosPosition.value = selectDetail.value.productPhotos.length - 1
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- showNotify({ type: 'primary', message: '取消删除' })
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 显示外箱图预览
|
|
|
-const showBoxImagePreview = (index) => {
|
|
|
- startBoxPosition.value = index
|
|
|
- showBoxPreview.value = true
|
|
|
-}
|
|
|
-
|
|
|
-// 外箱图预览位置变化
|
|
|
-const onBoxPreviewChange = (index) => {
|
|
|
- startBoxPosition.value = index
|
|
|
+ showImagePreview(images)
|
|
|
}
|
|
|
|
|
|
-// 删除外箱图
|
|
|
-const handleBoxDelete = () => {
|
|
|
- showConfirmDialog({
|
|
|
- title: '提示',
|
|
|
- message: '确定要删除这张图片吗?'
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- selectDetail.value.boxPhotos.splice(startBoxPosition.value, 1)
|
|
|
- showNotify({ type: 'success', message: '删除成功' })
|
|
|
- if (selectDetail.value.boxPhotos.length === 0) {
|
|
|
- showBoxPreview.value = false
|
|
|
- } else if (
|
|
|
- startBoxPosition.value >= selectDetail.value.boxPhotos.length
|
|
|
- ) {
|
|
|
- startBoxPosition.value = selectDetail.value.boxPhotos.length - 1
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- showNotify({ type: 'primary', message: '取消删除' })
|
|
|
- })
|
|
|
+const onReset = () => {
|
|
|
+ outerImages.value = []
|
|
|
+ innerImages.value = []
|
|
|
+ showNotify({ type: 'primary', message: '已重置图片' })
|
|
|
}
|
|
|
|
|
|
-// 预览图片列表
|
|
|
-function previewImages(filenames, index, type) {
|
|
|
- let list = []
|
|
|
- if (type === '外箱图') {
|
|
|
- list = boxFiles.value
|
|
|
- } else if (type === '内物图') {
|
|
|
- list = productFiles.value
|
|
|
- }
|
|
|
- const images = []
|
|
|
- for (let i = 0; i < list.length; i++) {
|
|
|
- const item = list[i]
|
|
|
- const { src, fileName } = item
|
|
|
- if (filenames.includes(fileName)) {
|
|
|
- images.push(src)
|
|
|
- }
|
|
|
- }
|
|
|
- if (!images || images.length === 0) {
|
|
|
+const onSubmit = async () => {
|
|
|
+ if (!outerImages.value.length && !innerImages.value.length) {
|
|
|
+ showFailToast('请先上传外箱图或内物图')
|
|
|
return
|
|
|
}
|
|
|
- showImagePreview({
|
|
|
- images: images,
|
|
|
- startPosition: index
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 表单提交和验证方法 ====================
|
|
|
-// 浮动按钮提交处理(带确认对话框)
|
|
|
-const handleSubmitWithConfirm = () => {
|
|
|
- showConfirmDialog({
|
|
|
- title: '提交确认',
|
|
|
- message: '确认提交退货登记信息吗?\n\n提交后将无法修改,请仔细检查所有信息。',
|
|
|
- confirmButtonText: '确认提交',
|
|
|
- cancelButtonText: '取消',
|
|
|
- confirmButtonColor: '#1989fa'
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- // 用户点击确认后执行提交
|
|
|
- submit()
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- // 用户点击取消,不执行任何操作
|
|
|
- showNotify({
|
|
|
- type: 'primary',
|
|
|
- message: '已取消提交'
|
|
|
- })
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 表单提交
|
|
|
-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: '请选择登记仓库'
|
|
|
+ try {
|
|
|
+ const outerCount = outerImages.value.length
|
|
|
+ const innerCount = innerImages.value.length
|
|
|
+ await showConfirmDialog({
|
|
|
+ title: '确认提交',
|
|
|
+ message: `外箱图:${outerCount} 张\n内物图:${innerCount} 张\n是否确认提交?`
|
|
|
})
|
|
|
+ } catch (e) {
|
|
|
+ showNotify({ type: 'warning', message: '已取消提交' })
|
|
|
return
|
|
|
}
|
|
|
- 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 filenames = []
|
|
|
-
|
|
|
- if (files && files.length > 0) {
|
|
|
- files.forEach((item) => {
|
|
|
- const { file, fileName } = item
|
|
|
- if (!filenames.includes(fileName)) {
|
|
|
- formData.append('files', file)
|
|
|
- filenames.push(fileName)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- handleDetails(params.value.details)
|
|
|
- formData.append('body', JSON.stringify(JSON.parse(JSON.stringify(params.value))))
|
|
|
- showLoadingToast({
|
|
|
- duration: 0,
|
|
|
- forbidClick: true,
|
|
|
- message: '提交登记中.....'
|
|
|
- })
|
|
|
-
|
|
|
- register(formData)
|
|
|
- .then((res) => {
|
|
|
- const { data } = res
|
|
|
- const { accumulateTaskMap } = data
|
|
|
- closeToast()
|
|
|
- if (data) {
|
|
|
- if (accumulateTaskMap && accumulateTaskMap.length > 0) {
|
|
|
- let messages = []
|
|
|
- const ownerName = getOwnerName(params.value.ownerCode)
|
|
|
- for (let i = 0; i < accumulateTaskMap.length; i++) {
|
|
|
- const { quality, taskCode } = accumulateTaskMap[i]
|
|
|
- messages.push(`进入${ownerName}新的${quality}攒单任务号${taskCode}`)
|
|
|
- }
|
|
|
|
|
|
- showConfirmDialog({
|
|
|
- title: '提交成功',
|
|
|
- message: messages.join('\r\n'),
|
|
|
- theme: 'round-button'
|
|
|
- })
|
|
|
- } else {
|
|
|
- showNotify({
|
|
|
- type: 'success',
|
|
|
- message: '成功提交'
|
|
|
- })
|
|
|
- }
|
|
|
- init()
|
|
|
- params.value.ownerCode = ownerCode
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- closeToast()
|
|
|
- })
|
|
|
-}
|
|
|
+ const toast = showLoadingToast({ duration: 0, message: '上传中...' })
|
|
|
+ try {
|
|
|
+ // 分开上传:先外箱图,再内物图(或根据需要并行)
|
|
|
+ await uploadCategory(outerImages.value, 'RETURNED_BOX_IMAGE')
|
|
|
+ await uploadCategory(innerImages.value, 'RETURNED_INNER_IMAGE')
|
|
|
|
|
|
-// 检查详情数量
|
|
|
-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
|
|
|
-}
|
|
|
+ closeToast()
|
|
|
+ showNotify({ type: 'success', message: '提交成功' })
|
|
|
|
|
|
-// 处理详情数据
|
|
|
-function handleDetails(details) {
|
|
|
- for (let i = 0; i < details.length; i++) {
|
|
|
- const { qualityStatus } = details[i]
|
|
|
- const { isGenuineValue, qualityMark } = getStatus(qualityStatus)
|
|
|
- details[i].isGenuine = isGenuineValue
|
|
|
- details[i].qualityMark = qualityMark
|
|
|
+ // 清空列表
|
|
|
+ outerImages.value = []
|
|
|
+ innerImages.value = []
|
|
|
+ } catch (e) {
|
|
|
+ closeToast()
|
|
|
+ showFailToast(e?.message || '提交失败,请重试')
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-// ==================== 图片处理方法 ====================
|
|
|
-// ==================== 全局事件处理 ====================
|
|
|
-// 页面刷新事件
|
|
|
-window.onRefresh = async () => {
|
|
|
- console.log('window.onRefresh')
|
|
|
-}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="sass">
|
|
|
-// ==================== 导航栏样式 ====================
|
|
|
.van-nav-bar
|
|
|
- position: fixed
|
|
|
- top: 0
|
|
|
- left: 0
|
|
|
- right: 0
|
|
|
- width: 100%
|
|
|
- z-index: 1000
|
|
|
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
|
|
|
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3)
|
|
|
-
|
|
|
.left-btn
|
|
|
color: #fff
|
|
|
height: 46px
|
|
|
padding-right: 20px
|
|
|
line-height: 46px
|
|
|
- font-size: 15px
|
|
|
- font-weight: 500
|
|
|
- transition: opacity 0.3s ease
|
|
|
-
|
|
|
- &:active
|
|
|
- opacity: 0.7
|
|
|
|
|
|
.right-btn
|
|
|
color: #fff
|
|
|
- font-size: 15px
|
|
|
- font-weight: 500
|
|
|
- transition: opacity 0.3s ease
|
|
|
-
|
|
|
- &:active
|
|
|
- opacity: 0.7
|
|
|
|
|
|
-// ==================== 主容器样式 ====================
|
|
|
.container
|
|
|
- min-height: 100vh
|
|
|
- background: linear-gradient(180deg, #f7f8fa 0%, #ffffff 100%)
|
|
|
- overflow-x: hidden // 防止横向滚动
|
|
|
- max-width: 100vw // 限制最大宽度
|
|
|
- box-sizing: border-box // 确保padding不增加总宽度
|
|
|
-
|
|
|
.init-container
|
|
|
width: 100%
|
|
|
- max-width: 100%
|
|
|
- padding: 16px 8px
|
|
|
- box-sizing: border-box
|
|
|
- margin-top: 10px // 确保不被导航栏遮挡
|
|
|
- min-height: calc(100vh - 100px) // 确保有足够的显示空间
|
|
|
- flex-direction: column
|
|
|
- justify-content: center // 垂直居中显示内容
|
|
|
-
|
|
|
- .content-tips
|
|
|
- margin-bottom: 10px
|
|
|
- border-radius: 8px
|
|
|
- overflow: hidden
|
|
|
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08)
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
- display: block // 确保块级显示
|
|
|
- width: 100% // 明确设置宽度
|
|
|
-
|
|
|
- :deep(.van-notice-bar)
|
|
|
- width: 100%
|
|
|
- box-sizing: border-box
|
|
|
- word-wrap: break-word
|
|
|
- white-space: normal // 允许文字换行
|
|
|
-
|
|
|
- .van-notice-bar__content
|
|
|
- flex: 1
|
|
|
- min-width: 0 // 允许flex元素正确缩小
|
|
|
- word-break: break-word
|
|
|
- overflow-wrap: break-word
|
|
|
-
|
|
|
- .van-notice-bar__text
|
|
|
- word-break: break-word
|
|
|
- overflow-wrap: break-word
|
|
|
- white-space: normal
|
|
|
-
|
|
|
- // 固定定位的提示条样式
|
|
|
- &.fixed-notice
|
|
|
- position: fixed
|
|
|
- top: 46px // 导航栏高度
|
|
|
- left: 0
|
|
|
- right: 0
|
|
|
- z-index: 999
|
|
|
- background: white
|
|
|
- margin: 0
|
|
|
- padding: 8px 16px
|
|
|
- border-bottom: 1px solid #ebedf0
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)
|
|
|
- border-radius: 0
|
|
|
- max-width: 100vw
|
|
|
- box-sizing: border-box
|
|
|
|
|
|
.scan-returned-content
|
|
|
- padding: 16px
|
|
|
- background: #fff
|
|
|
- border-radius: 16px
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08)
|
|
|
- margin-top: 8px
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- // 当有固定提示条时的上边距
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 65px // 与退货信息区域保持一致
|
|
|
+ padding: 10px
|
|
|
|
|
|
.input-group
|
|
|
- padding: 4px 0
|
|
|
- margin-bottom: 16px
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- :deep(.van-field)
|
|
|
- border-radius: 12px
|
|
|
- border: 1px solid #ebedf0
|
|
|
- transition: border-color 0.3s ease, box-shadow 0.3s ease
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- &:focus-within
|
|
|
- border-color: #1989fa
|
|
|
- box-shadow: 0 0 0 2px rgba(25, 137, 250, 0.1)
|
|
|
-
|
|
|
- .van-field__control
|
|
|
- word-break: break-all
|
|
|
- overflow-wrap: break-word
|
|
|
+ padding: 5px
|
|
|
|
|
|
.button-group
|
|
|
- padding: 4px 0
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- .confirm-btn
|
|
|
- height: 44px
|
|
|
- border-radius: 12px
|
|
|
- font-size: 16px
|
|
|
- font-weight: 600
|
|
|
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
|
|
|
- border: none
|
|
|
- box-shadow: 0 4px 12px rgba(25, 137, 250, 0.3)
|
|
|
- transition: all 0.3s ease
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- &:active
|
|
|
- transform: translateY(1px)
|
|
|
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.4)
|
|
|
+ padding: 5px
|
|
|
|
|
|
.content
|
|
|
width: 100%
|
|
|
- max-width: 100vw
|
|
|
- margin-top: 46px
|
|
|
- min-height: calc(100vh - 46px)
|
|
|
- background: #f7f8fa
|
|
|
- padding-bottom: 20px
|
|
|
- overflow-x: hidden
|
|
|
- box-sizing: border-box
|
|
|
- padding-top: 10px // 为初始扫描页面添加上边距
|
|
|
-
|
|
|
- // ==================== 固定定位提示条样式 ====================
|
|
|
- .content-tips.fixed-notice
|
|
|
- position: fixed
|
|
|
- top: 46px // 导航栏高度
|
|
|
- left: 0
|
|
|
- right: 0
|
|
|
- z-index: 999
|
|
|
- background: white
|
|
|
- margin: 0
|
|
|
- padding: 8px 16px
|
|
|
- border-bottom: 1px solid #ebedf0
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)
|
|
|
- border-radius: 0
|
|
|
- max-width: 100vw
|
|
|
- box-sizing: border-box
|
|
|
- min-height: auto // 允许高度自适应
|
|
|
-
|
|
|
- :deep(.van-notice-bar)
|
|
|
- width: 100%
|
|
|
- box-sizing: border-box
|
|
|
- word-wrap: break-word
|
|
|
- white-space: normal // 允许文字换行
|
|
|
-
|
|
|
- .van-notice-bar__content
|
|
|
- flex: 1
|
|
|
- min-width: 0
|
|
|
- word-break: break-word
|
|
|
- overflow-wrap: break-word
|
|
|
-
|
|
|
- .van-notice-bar__text
|
|
|
- word-break: break-word
|
|
|
- overflow-wrap: break-word
|
|
|
- white-space: normal
|
|
|
- line-height: 1.4
|
|
|
-
|
|
|
- // ==================== 区块头部样式 ====================
|
|
|
- .section-header
|
|
|
- display: flex
|
|
|
- align-items: center
|
|
|
- padding: 12px 16px
|
|
|
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
|
|
|
- color: white
|
|
|
- margin: 8px
|
|
|
- border-radius: 12px 12px 0 0
|
|
|
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3)
|
|
|
-
|
|
|
- .section-title
|
|
|
- margin-left: 8px
|
|
|
- font-size: 16px
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- // ==================== 退货信息区域 ====================
|
|
|
- .return-info-section
|
|
|
- margin-bottom: 20px
|
|
|
|
|
|
- // 当有固定提示条时的上边距
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 65px // 优化上边距:导航栏46px + 提示条约15px + 安全间距4px
|
|
|
-
|
|
|
- .content-tips
|
|
|
- margin: 8px
|
|
|
- border-radius: 0 0 12px 12px
|
|
|
- overflow: hidden
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06)
|
|
|
-
|
|
|
- // ==================== 商品信息区域 ====================
|
|
|
- .product-info-section
|
|
|
- margin-bottom: 20px
|
|
|
-
|
|
|
- // ==================== 提交区域 ====================
|
|
|
- .submit-section
|
|
|
- margin-top: 20px
|
|
|
-
|
|
|
- // ==================== 内容提示区域 ====================
|
|
|
- .content-tips
|
|
|
- border-radius: 12px
|
|
|
- overflow: hidden
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06)
|
|
|
- width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- :deep(.van-notice-bar)
|
|
|
- border-radius: 12px
|
|
|
- font-size: 14px
|
|
|
- font-weight: 500
|
|
|
- width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- .van-notice-bar__content
|
|
|
- flex: 1
|
|
|
- min-width: 0
|
|
|
- word-break: break-word
|
|
|
- overflow-wrap: break-word
|
|
|
- white-space: normal
|
|
|
-
|
|
|
- .van-notice-bar__text
|
|
|
- word-break: break-word
|
|
|
- overflow-wrap: break-word
|
|
|
- white-space: normal
|
|
|
- line-height: 1.5
|
|
|
-
|
|
|
- // ==================== 表单组件优化 ====================
|
|
|
- :deep(.van-cell-group)
|
|
|
- border-radius: 16px
|
|
|
- overflow: hidden
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06)
|
|
|
- margin: 8px
|
|
|
- max-width: calc(100% - 16px)
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- .van-cell
|
|
|
- padding: 12px 16px
|
|
|
- border-bottom: 1px solid #f5f7fa
|
|
|
- transition: background-color 0.3s ease
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
- overflow: hidden
|
|
|
-
|
|
|
- &:last-child
|
|
|
- border-bottom: none
|
|
|
-
|
|
|
- &:active
|
|
|
- background-color: #f8f9fa
|
|
|
-
|
|
|
- .van-field__label
|
|
|
- font-weight: 600
|
|
|
- color: #323233
|
|
|
- font-size: 14px
|
|
|
- white-space: nowrap
|
|
|
- overflow: hidden
|
|
|
- text-overflow: ellipsis
|
|
|
-
|
|
|
- .van-field__control
|
|
|
- font-size: 14px
|
|
|
- color: #646566
|
|
|
- word-break: break-all
|
|
|
- overflow-wrap: break-word
|
|
|
-
|
|
|
- // ==================== 商品卡片样式优化 ====================
|
|
|
.scan-returned-no
|
|
|
align-items: center
|
|
|
- padding: 12px
|
|
|
+ padding: 15px
|
|
|
|
|
|
.returned-detail-list
|
|
|
- padding: 0 8px
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
.card-div
|
|
|
background: #fff
|
|
|
- border-radius: 16px
|
|
|
+ border-radius: 12px
|
|
|
overflow: hidden
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08)
|
|
|
- margin: 8px 0
|
|
|
- border: 1px solid #f0f0f0
|
|
|
- transition: all 0.3s ease
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- &:hover
|
|
|
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12)
|
|
|
- transform: translateY(-1px)
|
|
|
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05)
|
|
|
+ margin: 5px 0
|
|
|
+ padding: 5px 0
|
|
|
|
|
|
.card-div-content
|
|
|
- padding: 12px
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
+ padding: 3px
|
|
|
|
|
|
.info-row
|
|
|
display: flex
|
|
|
- align-items: center
|
|
|
- margin-bottom: 10px
|
|
|
- font-size: 13px
|
|
|
- line-height: 1.4
|
|
|
- max-width: 100%
|
|
|
- overflow: hidden
|
|
|
-
|
|
|
- &:last-child
|
|
|
- margin-bottom: 0
|
|
|
-
|
|
|
- .info-label
|
|
|
- width: 80px
|
|
|
- min-width: 80px
|
|
|
- color: #969799
|
|
|
- font-weight: 500
|
|
|
- font-size: 12px
|
|
|
- flex-shrink: 0
|
|
|
- white-space: nowrap
|
|
|
- overflow: hidden
|
|
|
- text-overflow: ellipsis
|
|
|
-
|
|
|
- .info-value
|
|
|
- flex: 1
|
|
|
- color: #323233
|
|
|
- font-weight: 600
|
|
|
- font-size: 13px
|
|
|
- margin-left: 8px
|
|
|
- word-break: break-all
|
|
|
- overflow-wrap: break-word
|
|
|
- min-width: 0 // 允许flex元素缩小到内容大小
|
|
|
-
|
|
|
- :deep(.van-tag)
|
|
|
- font-size: 11px
|
|
|
- padding: 2px 8px
|
|
|
- border-radius: 6px
|
|
|
- font-weight: 600
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- :deep(.van-text-ellipsis)
|
|
|
- font-size: 13px
|
|
|
- line-height: 1.4
|
|
|
- max-width: 100%
|
|
|
-
|
|
|
- .card-div-footer
|
|
|
- padding: 10px 12px
|
|
|
- background: linear-gradient(135deg, #f8f9fa 0%, #f5f7fa 100%)
|
|
|
- border-top: 1px solid #ebedf0
|
|
|
- color: #646566
|
|
|
- font-size: 13px
|
|
|
- line-height: 1.5
|
|
|
-
|
|
|
- .product-description
|
|
|
- color: #646566
|
|
|
- font-style: italic
|
|
|
-
|
|
|
- .card-div-footer-images
|
|
|
- padding: 10px 12px
|
|
|
- background: #fafbfc
|
|
|
- border-top: 1px solid #ebedf0
|
|
|
-
|
|
|
- :deep(.van-divider)
|
|
|
- margin: 6px 0
|
|
|
- font-size: 12px
|
|
|
- color: #969799
|
|
|
-
|
|
|
- :deep(.van-image)
|
|
|
- border-radius: 8px
|
|
|
- overflow: hidden
|
|
|
- border: 1px solid #ebedf0
|
|
|
- transition: all 0.3s ease
|
|
|
-
|
|
|
- &:hover
|
|
|
- transform: scale(1.05)
|
|
|
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15)
|
|
|
-
|
|
|
- .card-div-footer-options
|
|
|
- background: #f8f9fa
|
|
|
- border-top: 1px solid #ebedf0
|
|
|
- padding: 10px 12px
|
|
|
-
|
|
|
- .options-row
|
|
|
- display: flex
|
|
|
- gap: 12px
|
|
|
-
|
|
|
- .info-value
|
|
|
- flex: 1
|
|
|
-
|
|
|
- :deep(.van-button)
|
|
|
- height: 32px
|
|
|
- border-radius: 8px
|
|
|
- font-size: 13px
|
|
|
- font-weight: 600
|
|
|
- transition: all 0.3s ease
|
|
|
-
|
|
|
- &.van-button--danger
|
|
|
- border-color: #ee0a24
|
|
|
- color: #ee0a24
|
|
|
-
|
|
|
- &:active
|
|
|
- background-color: #fee
|
|
|
-
|
|
|
- &.van-button--default
|
|
|
- border-color: #1989fa
|
|
|
- color: #1989fa
|
|
|
-
|
|
|
- &:active
|
|
|
- background-color: #e6f4ff
|
|
|
-
|
|
|
- // ==================== 对话框优化 ====================
|
|
|
- :deep(.van-dialog)
|
|
|
- border-radius: 16px
|
|
|
- overflow: hidden
|
|
|
-
|
|
|
- .van-dialog__header
|
|
|
- padding: 16px 20px 12px
|
|
|
- font-size: 18px
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- .van-dialog__content
|
|
|
- padding: 0 20px 16px
|
|
|
- max-height: 70vh
|
|
|
- overflow-y: auto
|
|
|
-
|
|
|
- &::-webkit-scrollbar
|
|
|
- width: 4px
|
|
|
-
|
|
|
- &::-webkit-scrollbar-thumb
|
|
|
- background: #c8c9cc
|
|
|
- border-radius: 2px
|
|
|
-
|
|
|
- .van-dialog__footer
|
|
|
- padding: 12px 20px 16px
|
|
|
- border-top: 1px solid #ebedf0
|
|
|
-
|
|
|
- .van-button
|
|
|
- height: 40px
|
|
|
- border-radius: 8px
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- // ==================== 对话框优化 ====================
|
|
|
- :deep(.van-dialog)
|
|
|
- border-radius: 16px
|
|
|
- overflow: hidden
|
|
|
-
|
|
|
- &.compact-dialog
|
|
|
- .van-dialog__header
|
|
|
- padding: 12px 16px
|
|
|
- font-size: 16px
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- .van-dialog__content
|
|
|
- padding: 0
|
|
|
-
|
|
|
- .van-dialog__footer
|
|
|
- padding: 8px 16px
|
|
|
-
|
|
|
- .van-button
|
|
|
- height: 36px
|
|
|
- font-size: 14px
|
|
|
- border-radius: 8px
|
|
|
-
|
|
|
- // ==================== 紧凑型对话框内容样式 ====================
|
|
|
- .dialog-content
|
|
|
- max-height: 75vh
|
|
|
- overflow-y: auto
|
|
|
- padding: 8px 12px
|
|
|
-
|
|
|
- .field-group
|
|
|
- margin-bottom: 8px
|
|
|
-
|
|
|
- &.date-fields
|
|
|
- display: flex
|
|
|
- flex-direction: column
|
|
|
- gap: 4px
|
|
|
-
|
|
|
- .compact-field
|
|
|
- margin-bottom: 4px !important
|
|
|
-
|
|
|
- :deep(.van-cell)
|
|
|
- padding: 8px 12px
|
|
|
- min-height: 40px
|
|
|
-
|
|
|
- :deep(.van-field__label)
|
|
|
- font-size: 13px
|
|
|
- font-weight: 500
|
|
|
- width: 60px
|
|
|
- color: #646566
|
|
|
-
|
|
|
- :deep(.van-field__control)
|
|
|
- font-size: 13px
|
|
|
-
|
|
|
- &.date-field
|
|
|
- :deep(.van-field__label)
|
|
|
- width: 65px
|
|
|
-
|
|
|
- .compact-divider
|
|
|
- margin: 8px 0 6px 0 !important
|
|
|
- font-size: 13px
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- :deep(.van-divider__content)
|
|
|
- padding: 0 8px
|
|
|
-
|
|
|
- // 配件信息样式
|
|
|
- .accessories-section
|
|
|
- margin: 8px 0
|
|
|
-
|
|
|
- .accessories-content
|
|
|
- padding: 6px 8px
|
|
|
- background: #f8f9fa
|
|
|
- border-radius: 8px
|
|
|
-
|
|
|
- .accessory-item
|
|
|
- font-size: 11px
|
|
|
- line-height: 1.4
|
|
|
- margin-bottom: 4px
|
|
|
-
|
|
|
- .accessory-highlight
|
|
|
- color: #2ca547
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- .accessory-desc
|
|
|
- color: #277b39
|
|
|
-
|
|
|
- .accessory-notice
|
|
|
- font-size: 11px
|
|
|
- margin-top: 6px
|
|
|
- padding-top: 6px
|
|
|
- border-top: 1px solid #ebedf0
|
|
|
-
|
|
|
- .accessory-warning
|
|
|
- color: #ff2020
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- // 图片区域样式
|
|
|
- .photo-section
|
|
|
- margin: 8px 0
|
|
|
-
|
|
|
- .photo-grid
|
|
|
- display: grid
|
|
|
- grid-template-columns: repeat(6, 1fr)
|
|
|
- gap: 6px
|
|
|
- padding: 6px 0
|
|
|
-
|
|
|
- .photo-item
|
|
|
- .compact-image
|
|
|
- border-radius: 6px
|
|
|
- border: 1px solid #ebedf0
|
|
|
- transition: transform 0.2s ease, box-shadow 0.2s ease
|
|
|
-
|
|
|
- &:hover
|
|
|
- transform: scale(1.05)
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15)
|
|
|
-
|
|
|
- // 拍照按钮样式
|
|
|
- .camera-buttons
|
|
|
- display: flex
|
|
|
- gap: 8px
|
|
|
- margin-top: 12px
|
|
|
- padding-top: 8px
|
|
|
- border-top: 1px solid #f0f0f0
|
|
|
-
|
|
|
- .camera-btn
|
|
|
- flex: 1
|
|
|
- height: 36px
|
|
|
- font-size: 13px
|
|
|
- border-radius: 8px
|
|
|
- font-weight: 500
|
|
|
-
|
|
|
- // ==================== 图片预览优化 ====================
|
|
|
- :deep(.van-image-preview)
|
|
|
- .custom-toolbar
|
|
|
- position: absolute
|
|
|
- top: 20px
|
|
|
- right: 20px
|
|
|
- display: flex
|
|
|
- align-items: center
|
|
|
- gap: 12px
|
|
|
- padding: 8px 12px
|
|
|
- background: rgba(0, 0, 0, 0.6)
|
|
|
- border-radius: 20px
|
|
|
- color: white
|
|
|
- font-size: 14px
|
|
|
- z-index: 1000
|
|
|
-
|
|
|
- .van-button
|
|
|
- border-radius: 6px
|
|
|
- font-size: 12px
|
|
|
- padding: 4px 8px
|
|
|
-
|
|
|
- // ==================== 浮动按钮样式 ====================
|
|
|
- .submit-floating-btn
|
|
|
- position: fixed
|
|
|
- bottom: 80px
|
|
|
- right: 20px
|
|
|
- z-index: 999
|
|
|
-
|
|
|
- :deep(.van-floating-bubble)
|
|
|
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
|
|
|
- box-shadow: 0 4px 12px rgba(25, 137, 250, 0.4)
|
|
|
- transition: all 0.3s ease
|
|
|
-
|
|
|
- &:hover
|
|
|
- transform: scale(1.05)
|
|
|
- box-shadow: 0 6px 16px rgba(25, 137, 250, 0.5)
|
|
|
-
|
|
|
- &:active
|
|
|
- transform: scale(0.95)
|
|
|
-
|
|
|
- .van-icon
|
|
|
- color: white
|
|
|
- font-size: 18px
|
|
|
-
|
|
|
- // ==================== 弹出层优化 ====================
|
|
|
- :deep(.van-popup)
|
|
|
- border-radius: 16px 16px 0 0
|
|
|
- overflow: hidden
|
|
|
-
|
|
|
- :deep(.van-picker)
|
|
|
- .van-picker__toolbar
|
|
|
- padding: 12px 16px
|
|
|
- background: #f8f9fa
|
|
|
-
|
|
|
- .van-picker__confirm
|
|
|
- color: #1989fa
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- .van-picker__cancel
|
|
|
- color: #969799
|
|
|
- font-weight: 600
|
|
|
-
|
|
|
- // ==================== 响应式优化 ====================
|
|
|
- // 小屏幕设备适配 (iPhone SE, 小屏幕手机)
|
|
|
- @media (max-width: 375px)
|
|
|
- // 初始扫描页面优化
|
|
|
- .init-container
|
|
|
- padding: 12px 6px
|
|
|
- margin-top: 15px // 增加上边距确保不被遮挡
|
|
|
- min-height: calc(100vh - 120px) // 调整最小高度
|
|
|
-
|
|
|
- .content-tips
|
|
|
- margin-bottom: 12px
|
|
|
-
|
|
|
- :deep(.van-notice-bar)
|
|
|
- font-size: 13px
|
|
|
- padding: 8px 12px
|
|
|
-
|
|
|
- .van-notice-bar__content
|
|
|
- min-width: 0
|
|
|
-
|
|
|
- .van-notice-bar__text
|
|
|
- font-size: 13px
|
|
|
- line-height: 1.4
|
|
|
-
|
|
|
- .scan-returned-content
|
|
|
- padding: 16px 12px
|
|
|
- margin-top: 8px
|
|
|
- border-radius: 12px
|
|
|
-
|
|
|
- // 小屏幕固定提示条适配
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 60px // 小屏幕适配:减少上边距
|
|
|
-
|
|
|
- .input-group
|
|
|
margin-bottom: 12px
|
|
|
-
|
|
|
- :deep(.van-field)
|
|
|
- .van-field__label
|
|
|
- font-size: 13px
|
|
|
-
|
|
|
- .van-field__control
|
|
|
- font-size: 14px
|
|
|
-
|
|
|
- .button-group
|
|
|
- .confirm-btn
|
|
|
- height: 40px
|
|
|
- font-size: 15px
|
|
|
-
|
|
|
- // 退货信息区域优化
|
|
|
- .return-info-section
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 60px // 小屏幕适配:减少上边距
|
|
|
-
|
|
|
- // 商品卡片优化
|
|
|
- .returned-detail-list
|
|
|
- .card-div
|
|
|
- margin: 6px 0
|
|
|
-
|
|
|
- .card-div-content
|
|
|
- padding: 10px
|
|
|
-
|
|
|
- .info-row
|
|
|
- font-size: 12px
|
|
|
- margin-bottom: 8px
|
|
|
-
|
|
|
- .info-label
|
|
|
- width: 70px
|
|
|
- font-size: 11px
|
|
|
-
|
|
|
- .info-value
|
|
|
- font-size: 12px
|
|
|
-
|
|
|
- // 导航栏优化
|
|
|
- .van-nav-bar
|
|
|
- .left-btn
|
|
|
- font-size: 14px
|
|
|
- padding-right: 16px
|
|
|
-
|
|
|
- .right-btn
|
|
|
- font-size: 14px
|
|
|
-
|
|
|
- // 区块头部优化
|
|
|
- .section-header
|
|
|
- padding: 10px 14px
|
|
|
- margin: 6px
|
|
|
-
|
|
|
- .section-title
|
|
|
- font-size: 15px
|
|
|
-
|
|
|
- // 紧凑型对话框小屏幕优化
|
|
|
- .dialog-content
|
|
|
- padding: 6px 8px
|
|
|
-
|
|
|
- .field-group
|
|
|
- margin-bottom: 6px
|
|
|
-
|
|
|
- .compact-field
|
|
|
- :deep(.van-cell)
|
|
|
- padding: 6px 8px
|
|
|
- min-height: 36px
|
|
|
-
|
|
|
- :deep(.van-field__label)
|
|
|
- font-size: 12px
|
|
|
- width: 55px
|
|
|
-
|
|
|
- :deep(.van-field__control)
|
|
|
font-size: 12px
|
|
|
-
|
|
|
- .photo-section
|
|
|
- .photo-grid
|
|
|
- grid-template-columns: repeat(5, 1fr)
|
|
|
- gap: 4px
|
|
|
-
|
|
|
- .camera-buttons
|
|
|
- gap: 6px
|
|
|
-
|
|
|
- .camera-btn
|
|
|
- height: 32px
|
|
|
- font-size: 12px
|
|
|
-
|
|
|
- // 浮动按钮小屏幕适配
|
|
|
- .submit-floating-btn
|
|
|
- bottom: 70px
|
|
|
- right: 16px
|
|
|
-
|
|
|
- :deep(.van-floating-bubble)
|
|
|
- width: 50px
|
|
|
- height: 50px
|
|
|
-
|
|
|
- .van-icon
|
|
|
- font-size: 16px
|
|
|
-
|
|
|
- // 中等屏幕设备适配 (iPhone 6/7/8)
|
|
|
- @media (max-width: 414px) and (min-width: 376px)
|
|
|
- .init-container
|
|
|
- padding: 16px 8px
|
|
|
- margin-top: 20px // 适当增加上边距
|
|
|
- min-height: calc(100vh - 110px)
|
|
|
-
|
|
|
- .scan-returned-content
|
|
|
- padding: 18px 14px
|
|
|
-
|
|
|
- // 中等屏幕固定提示条适配
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 62px // 中等屏幕适中的上边距
|
|
|
-
|
|
|
- .input-group
|
|
|
- :deep(.van-field)
|
|
|
- .van-field__control
|
|
|
- font-size: 15px
|
|
|
-
|
|
|
- .button-group
|
|
|
- .confirm-btn
|
|
|
- height: 42px
|
|
|
- font-size: 16px
|
|
|
-
|
|
|
- // 中等屏幕退货信息区域优化
|
|
|
- .return-info-section
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 62px // 中等屏幕适中的上边距
|
|
|
-
|
|
|
- // 紧凑型对话框中等屏幕优化
|
|
|
- .dialog-content
|
|
|
- padding: 8px 10px
|
|
|
-
|
|
|
- .compact-field
|
|
|
- :deep(.van-field__label)
|
|
|
- font-size: 13px
|
|
|
- width: 60px
|
|
|
-
|
|
|
- :deep(.van-field__control)
|
|
|
- font-size: 13px
|
|
|
-
|
|
|
- // 中等屏幕浮动按钮适配
|
|
|
- .submit-floating-btn
|
|
|
- bottom: 75px
|
|
|
- right: 18px
|
|
|
-
|
|
|
- // 大屏幕设备适配 (iPhone Plus, 大屏手机)
|
|
|
- @media (min-width: 415px)
|
|
|
- .init-container
|
|
|
- max-width: 500px
|
|
|
- margin: 25px auto 0 auto // 增加上边距并居中
|
|
|
- padding: 20px 10px
|
|
|
- min-height: calc(100vh - 130px)
|
|
|
-
|
|
|
- .scan-returned-content
|
|
|
- padding: 24px 18px
|
|
|
-
|
|
|
- // 大屏幕固定提示条适配
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 65px // 大屏幕标准上边距
|
|
|
-
|
|
|
- .input-group
|
|
|
- :deep(.van-field)
|
|
|
- .van-field__control
|
|
|
- font-size: 16px
|
|
|
-
|
|
|
- .button-group
|
|
|
- .confirm-btn
|
|
|
- height: 46px
|
|
|
- font-size: 17px
|
|
|
- max-width: 300px
|
|
|
- margin: 0 auto
|
|
|
-
|
|
|
- // 大屏幕浮动按钮适配
|
|
|
- .submit-floating-btn
|
|
|
- bottom: 80px
|
|
|
- right: 20px
|
|
|
-
|
|
|
- :deep(.van-floating-bubble)
|
|
|
- width: 56px
|
|
|
- height: 56px
|
|
|
-
|
|
|
- .van-icon
|
|
|
- font-size: 20px
|
|
|
-
|
|
|
- // 横屏适配
|
|
|
- @media (orientation: landscape) and (max-height: 500px)
|
|
|
- .content
|
|
|
- padding-top: 5px // 横屏时减少上边距
|
|
|
-
|
|
|
- .init-container
|
|
|
- padding: 8px 4px
|
|
|
- margin-top: 5px
|
|
|
- min-height: calc(100vh - 80px)
|
|
|
- justify-content: flex-start // 横屏时顶部对齐而非居中
|
|
|
-
|
|
|
- .content-tips
|
|
|
- margin-bottom: 8px
|
|
|
-
|
|
|
- .scan-returned-content
|
|
|
- padding: 12px 10px
|
|
|
-
|
|
|
- .input-group
|
|
|
- margin-bottom: 8px
|
|
|
-
|
|
|
- .button-group
|
|
|
- .confirm-btn
|
|
|
- height: 36px
|
|
|
-
|
|
|
- // 横屏退货信息区域优化
|
|
|
- .return-info-section
|
|
|
- &.with-fixed-notice
|
|
|
- margin-top: 55px // 横屏模式使用更紧凑的上边距
|
|
|
-
|
|
|
- // ==================== 空状态优化 ====================
|
|
|
- :deep(.van-empty)
|
|
|
- padding: 30px 20px
|
|
|
-
|
|
|
- .van-empty__image
|
|
|
- width: 100px
|
|
|
- height: 100px
|
|
|
-
|
|
|
- .van-empty__description
|
|
|
- color: #969799
|
|
|
- font-size: 14px
|
|
|
- margin-top: 12px
|
|
|
-
|
|
|
- // ==================== 按钮通用样式 ====================
|
|
|
- :deep(.van-button)
|
|
|
- transition: all 0.3s ease
|
|
|
- max-width: 100%
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
- &:active
|
|
|
- transform: scale(0.98)
|
|
|
-
|
|
|
- &.van-button--primary
|
|
|
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
|
|
|
- border: none
|
|
|
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3)
|
|
|
-
|
|
|
- &:active
|
|
|
- box-shadow: 0 4px 12px rgba(25, 137, 250, 0.4)
|
|
|
|
|
|
-// ==================== 全局防溢出规则 ====================
|
|
|
-*
|
|
|
- box-sizing: border-box
|
|
|
-
|
|
|
-body, html
|
|
|
- overflow-x: hidden
|
|
|
- max-width: 100vw
|
|
|
-
|
|
|
-// 防止长文本溢出
|
|
|
-.van-field__control input,
|
|
|
-.van-field__control textarea,
|
|
|
-:deep(.van-field__control input),
|
|
|
-:deep(.van-field__control textarea)
|
|
|
- word-break: break-all
|
|
|
- overflow-wrap: break-word
|
|
|
- max-width: 100%
|
|
|
-
|
|
|
-// 防止图片溢出
|
|
|
-:deep(.van-image img)
|
|
|
- max-width: 100%
|
|
|
- height: auto
|
|
|
-
|
|
|
-// 防止容器溢出
|
|
|
-.van-cell-group,
|
|
|
-.van-cell,
|
|
|
-.card-div,
|
|
|
-:deep(.van-cell-group),
|
|
|
-:deep(.van-cell),
|
|
|
-:deep(.card-div)
|
|
|
- max-width: 100%
|
|
|
- overflow: hidden
|
|
|
- box-sizing: border-box
|
|
|
</style>
|