Commit eb72b9a0 by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !119
parents bbbe0ed5 f84c5115
......@@ -63,6 +63,8 @@ export interface ILogisticsCompany {
authCode: string | null // varchar(500)
redirectUri: string | null // varchar(256)
createTime: string | null // timestamp
orderStatus: string | null // varchar(60)
signTime: string | null // timestamp
vat: string | null // varchar(60)
ioss: string | null // varchar(60)
basicType: number // int(11)
......
......@@ -38,6 +38,11 @@ export function syncReceiverAddress(data: number[]) {
)
}
export function getEmployeeListApi() {
return axios.get(`/factory/factoryUser/list`)
}
// 播种墙配货 扫码放入箱子
export function getPackingCnDataApi(
code: string,
......@@ -147,19 +152,13 @@ export function getCardOrderList(
)
}
export function batchCheckPrintPodCn(
ids:string
) {
export function batchCheckPrintPodCn(ids: string) {
return axios.get<never, BaseRespData<PrintData[]>>(
`/factory/podJomallOrderCn/batchCheckPrintPodCn?ids=${ids}`,
)
}
export function batchCheckPrintPodUs(
ids:string
) {
export function batchCheckPrintPodUs(ids: string) {
return axios.get<never, BaseRespData<PrintData[]>>(
`/factory/podJomallOrderUs/batchCheckPrintPodUs?ids=${ids}`,
)
......@@ -565,10 +564,11 @@ export function getListCraftApi() {
}
// 批量下载 列表
export function batchDownloadApi(currentPage: number, pageSize: number) {
export function batchDownloadApi(params: SearchForm, currentPage: number, pageSize: number) {
return axios.post<never, BaseRespData<never>>(
`factory/podBatchDownload/cn/list_page`,
{
...params,
currentPage,
pageSize,
},
......@@ -723,3 +723,29 @@ export function getCustomTagListCnApi() {
`factory/podJomallOrderCn/getCustomTagList`,
)
}
// 根据店铺单号或跟踪号查询订单
export function listByNoApi(params: {
type: string
no: string
logisticsCompanyCode?: string
}) {
return axios.get<never, BaseRespData<never>>(
`factory/podJomallOrderCn/listByNo`,
{ params },
)
}
//保存称重分拣
export function orderWeighingApi(params: {
podCnWeighingParams: { id?: string; outWarehouseWeight?: string }[]
}) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderCn/orderWeighing',
params,
)
}
//获取物流公司
export function allErpCodeListApi() {
return axios.get<never, BaseRespData<never>>(
'/logisticsCompany/allErpCodeList',
)
}
......@@ -477,10 +477,11 @@ export function getListCraftApi() {
}
// 批量下载 列表
export function batchDownloadApi(currentPage: number, pageSize: number) {
export function batchDownloadApi(params: SearchForm, currentPage: number, pageSize: number) {
return axios.post<never, BaseRespData<never>>(
`factory/podBatchDownload/us/list_page`,
{
...params,
currentPage,
pageSize,
},
......@@ -625,6 +626,11 @@ export function getAccountCodeByFactoryIdApi(params: { token: string }) {
export function getLogisticsWayApi() {
return axios.get(`logisticsWay/usableAllList`)
}
export function getEmployeeListApi() {
return axios.get(`/factory/factoryUser/list`)
}
// 打印拣货单item
export function printPickPdfByBatchNumberApi(params: {
batchArrangeNumber: string
......
......@@ -46,7 +46,14 @@ export interface SearchForm {
tagsId?: string
source?: string
size?: string
logisticsCompanyCode?: string
tagsIdArr?: (number | null)[]
craftType?: string
downloadStatus?: number
syntheticStatus?: number
automaticComposing?: number
employeeId?: number
blocking?: boolean
}
export interface PodCnOrderListData {
id: number
......@@ -113,6 +120,7 @@ export interface ProductList {
variantImage?: string
craftPrice?: number
craftCode?: string
craftType?: string
previewImgs?: []
platform?: string
imageAry?: string
......
......@@ -29,6 +29,7 @@ export interface OrderData {
remark?: string
version?: number
factoryOrderNumber?: number | string
replaceShipment?: number | string
orderParamList?: IorderItem[]
}
export interface IorderItem {
......
......@@ -44,6 +44,12 @@ export interface SearchForm {
size?: string
tagsIdArr?: (number | null)[]
replaceShipment?: number | null
craftType?: string
downloadStatus?: number
syntheticStatus?: number
automaticComposing?: number
employeeId?: number
blocking?: boolean
}
export interface PodUsOrderListData {
id: number
......@@ -113,6 +119,7 @@ export interface ProductList {
variantImage?: string
craftPrice?: number
craftCode?: string
craftType?: string
platform?: string
imageAry?: string
previewImgs?: []
......
......@@ -27,6 +27,23 @@
<template #top>
<el-card>
<el-form inline :model="searchForm" ref="searchFormRef">
<el-form-item label="创建时间">
<el-date-picker
v-model="tradingTime"
:shortcuts="pickerOptions.shortcuts"
:default-time="[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
style="width: 260px"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="物流跟踪号">
<el-input
v-model="searchForm.trackNumber"
......@@ -87,6 +104,9 @@
<template #shipmentType="{ row }">
{{ ['自有物流', '工厂物流'][row.shipmentType] }}
</template>
<template #shippingAge="{ row }">
{{ getShippingAge(row) }}
</template>
</TableView>
</div>
<div class="pagination">
......@@ -139,7 +159,124 @@ const searchForm = ref<SearchForm>({
shopNumber: '',
trackNumber: '',
})
function getStartTime() {
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${month}-${day} 00:00:00`
}
const tradingTime = ref<string[]>([])
const pickerOptions = {
shortcuts: [
{
text: '今日',
value: () => {
const start = new Date(new Date(getStartTime()).getTime())
const end = new Date()
return [start, end]
},
},
{
text: '昨天',
value: () => {
const start = new Date()
const end = new Date(new Date(getStartTime()).getTime() - 1)
start.setTime(end.getTime() - 3600 * 1000 * 24 * 1 + 1)
return [start, end]
},
},
{
text: '最近7天',
value: () => {
const end = new Date()
const start = new Date(getStartTime())
start.setTime(start.getTime() - 3600 * 1000 * 24 * 6)
return [start, end]
},
},
{
text: '最近14天',
value: () => {
const end = new Date()
const start = new Date(getStartTime())
start.setTime(start.getTime() - 3600 * 1000 * 24 * 13)
return [start, end]
},
},
{
text: '最近30天',
value: () => {
const end = new Date()
const start = new Date(getStartTime())
start.setTime(start.getTime() - 3600 * 1000 * 24 * 29)
return [start, end]
},
},
{
text: '本星期',
value: () => {
const end = new Date()
const start = new Date()
const nowDay = new Date().getDay() - 1
start.setTime(
new Date(getStartTime()).getTime() - 3600 * 1000 * 24 * nowDay,
)
return [start, end]
},
},
{
text: '上星期',
value: () => {
const end = new Date()
const start = new Date()
const nowDay = new Date().getDay() - 1
end.setTime(
new Date(getStartTime()).getTime() - 3600 * 1000 * 24 * nowDay - 1,
)
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7 + 1)
return [start, end]
},
},
{
text: '这个月',
value: () => {
const end = new Date()
const start = new Date()
const nowDate = new Date().getDate() - 1
start.setTime(
new Date(getStartTime()).getTime() - 3600 * 1000 * 24 * nowDate,
)
return [start, end]
},
},
{
text: '上个月',
value: () => {
const date = new Date()
let year = date.getFullYear()
let month = date.getMonth()
const end = new Date(
new Date(`${year}-${month + 1}-1 00:00:00`).getTime() - 1,
)
if (month === 0) {
month = 12
year = year - 1
}
const start = new Date(
new Date(`${year}-${month}-1 00:00:00`).getTime(),
)
return [start, end]
},
},
{
text: '历史',
value: () => {
return ['', '']
},
},
],
}
const treeData = ref<LogisticsTrackingTree[]>()
const treeRef = ref<InstanceType<typeof ElTree>>()
const tableRef = ref<{ internalIsMore?: boolean }>()
......@@ -183,6 +320,19 @@ const tableColumns = computed(() => {
align: 'center',
},
{
label: '创建时间',
prop: 'createTime',
width: 200,
align: 'center',
},
{
label: '发货时效(天)',
prop: 'shippingAge',
slot: 'shippingAge',
width: 120,
align: 'center',
},
{
label: '订单状态',
prop: 'orderStatus',
slot: 'orderStatus',
......@@ -261,13 +411,38 @@ const getTree = async () => {
console.error(e)
}
}
/** 计算发货时效(天)
* 已签收 → 停止计时
* <12h = 0天;≥12h & <24h = 1天;≥48h = 2天 ...
*/
function getShippingAge(row: ILogisticsCompany): number {
// 成功签收就固定: 已签收时间 - 创建时间
if (row.orderStatus === 'COMPLETE') {
const signTime = new Date(row.signTime ?? 0).getTime()
const createTime = new Date(row.createTime ?? 0).getTime()
return msToDays(signTime - createTime)
}
// 未签收:当前时间 - 创建时间
const now = Date.now()
const create = new Date(row.createTime ?? 0).getTime()
return msToDays(now - create)
}
/** 毫秒 → 天数(≥12h 向下取整) */
function msToDays(ms: number): number {
const hours = ms / (1000 * 60 * 60)
if (hours < 12) return 0
return Math.floor(hours / 24)
}
// 列表查询
async function getData() {
const res = await logisticsTrackingPage({
trackingStatus: nodeId.value,
shopNumber: searchForm.value.shopNumber,
trackNumber: searchForm.value.trackNumber,
startTime: tradingTime.value && tradingTime.value[0],
endTime: tradingTime.value && tradingTime.value[1],
queryDateType: tradingTime.value && 1,
})
leftData.value = res.data.records
pagination.value.total = res.data.total
......
......@@ -59,4 +59,7 @@ export interface LogisticsTrackingParams {
trackNumber?: number | string
shopNumber?: string | number
trackingStatus?: number
startTime?: string
endTime?: string
queryDateType?: number | string
}
......@@ -399,6 +399,11 @@ watch(visible, async (value: boolean) => {
_warehouseId.value = hit ? localId : props.warehouseList[0].id
initPrintDevice()
inputActive()
const locaclPrinter = localStorage.getItem('sheetPrinter')
if (locaclPrinter) sheetPrinter.value = JSON.parse(locaclPrinter)
emit('set-printer', sheetPrinter.value)
}
})
watch(
......
......@@ -375,6 +375,8 @@ watch(visible, async (value: boolean) => {
if (value) {
podOrderDetailsData.value = {}
currentCode = ''
currentItem.value = {}
const localRaw = sessionStorage.getItem('locaclCnWarehouseId')
const localId = localRaw ? JSON.parse(localRaw) : ''
/* 先找一次,确认本地值是否存在于列表 */
......@@ -400,6 +402,13 @@ watch(visible, async (value: boolean) => {
console.error(error)
}
}
console.log(
8888,
currentItem.value,
currentCode,
podOrderDetailsData.value,
boxIndex.value,
)
initOrderDetailBox()
initPrintDevice()
......@@ -422,6 +431,11 @@ watch(boxIndex, (value: number | null) => {
const bool = !boxChange.value
boxChange.value = false
console.log('boxChange', bool, value)
const item = podBoxList.value?.find((item) => item.box === value)
currentItem.value = item?.data as OrderData
console.log('currentItem.value', currentItem.value)
renderItemBox(bool)
}
})
......@@ -431,6 +445,7 @@ watch(
if (value) {
const item = value.find((item) => item.box === podBoxIndex.value)
console.log('podBoxList', value, podBoxIndex.value, item)
currentItem.value = item?.data as OrderData
if (item?.data) {
renderItemBox(true)
} else {
......@@ -838,19 +853,23 @@ const handleOpened = () => {
productionOrderRef.value.focus()
}
const handleClose = (done: () => void) => {
console.log(999, currentItem.value)
nextStep(() => {
done()
})
}, currentItem.value)
}
const onClose = () => {
// orderStore.clearPodBox()
emit('refresh')
}
// 下一步
const nextStep = async (callback: () => void) => {
const nextStep = async (callback: () => void, data?: OrderData) => {
const everyPicked = podOrderDetailsData.value?.productList?.every(
(item) => item.count === item.purchaseNumber,
)
console.log(854, data)
if (
everyPicked &&
(podOrderDetailsData.value?.printResult === 'printSuccess' ||
......@@ -858,7 +877,9 @@ const nextStep = async (callback: () => void) => {
) {
try {
await ElMessageBox.alert(
'当前订单验货完成并打印面单成功,是否转至已完成',
`当前订单验货完成并打印面单成功,是否转至${
data?.replaceShipment == 1 ? '待称重' : '已完成'
}`,
'提示',
{
confirmButtonText: '确定',
......@@ -876,8 +897,12 @@ const nextStep = async (callback: () => void) => {
callback && callback()
}
}
const currentItem = ref<OrderData>({})
const handleBoxClick = (item: PodMakeOrderData) => {
const { box, data } = item
console.log('data11111111', data)
currentItem.value = item as OrderData
isBillLading.value = !data?.filePath
nextStep(() => {
if (!data) {
......@@ -887,7 +912,7 @@ const handleBoxClick = (item: PodMakeOrderData) => {
boxIndex.value = box || null
boxChange.value = true
productionOrderRef.value.focus()
})
}, currentItem.value)
}
const handleClearBox = async () => {
try {
......
......@@ -97,11 +97,11 @@
@row-click="handleRowClick"
>
<template #image="{ row }">
<div
style="display: flex; flex-wrap: nowrap"
>
<div style="display: flex; flex-wrap: nowrap">
<div
v-for="img in row.productMark!=='normal'?row.previewImgs:[{url:row.variantImage}]"
v-for="img in row.productMark !== 'normal'
? row.previewImgs
: [{ url: row.variantImage }]"
:key="img"
@click.stop="handleCurrentChange(img.url)"
style="cursor: pointer; margin-right: 5px; flex: 1"
......@@ -156,12 +156,7 @@
@click="podOrderDetailsData && print(podOrderDetailsData, true)"
>手动打印</ElButton
>
<ElButton
type="primary"
@click="printNormal"
>普货拣货
</ElButton
>
<ElButton type="primary" @click="printNormal">普货拣货 </ElButton>
<ElButton type="success" @click="handlePrintFinish"
>打单完成</ElButton
>
......@@ -328,6 +323,7 @@ watch(visible, async (value: boolean) => {
if (value) {
podOrderDetailsData.value = {}
currentCode = ''
currentItem.value = {}
if (userStore.user?.factory.id) {
try {
await socket.init(
......@@ -350,6 +346,7 @@ watch(visible, async (value: boolean) => {
initPrintDevice()
const locaclPrinter = localStorage.getItem('sheetPrinter')
if (locaclPrinter) sheetPrinter.value = JSON.parse(locaclPrinter)
emit('set-printer', sheetPrinter.value)
} else {
if (userStore.user?.factory.id) {
socket.send({
......@@ -365,6 +362,9 @@ watch(boxIndex, (value: number | null) => {
const bool = !boxChange.value
boxChange.value = false
console.log('boxChange', bool, value)
const item = podBoxList.value?.find((item) => item.box === value)
currentItem.value = item?.data as OrderData
renderItemBox(bool)
}
})
......@@ -374,6 +374,8 @@ watch(
if (value) {
const item = value.find((item) => item.box === podBoxIndex.value)
console.log('podBoxList', value, podBoxIndex.value, item)
currentItem.value = item?.data as OrderData
if (item?.data) {
renderItemBox(true)
} else {
......@@ -486,8 +488,8 @@ const messageChange = (data: WebSocketMessage) => {
}
const printNormal = async () => {
const arr: (number | undefined)[] = [];
(podBoxList.value || []).forEach((item: PodMakeOrderData) => {
const arr: (number | undefined)[] = []
;(podBoxList.value || []).forEach((item: PodMakeOrderData) => {
if (item.data) {
if (item.data.productList && item.data.productList.length > 0) {
const flag = item.data.productList.some((item1) => {
......@@ -659,8 +661,8 @@ const initOrderDetailBox = async () => {
ElMessage.warning(res.message)
return
}
res.data.forEach(r=>{
r.data?.productList?.forEach(d=>{
res.data.forEach((r) => {
r.data?.productList?.forEach((d) => {
if (d.productMark === 'normal') {
d.previewImgs = [{ url: d.variantImage || '' }]
} else {
......@@ -764,14 +766,14 @@ const handleOpened = () => {
const handleClose = (done: () => void) => {
nextStep(() => {
done()
})
}, currentItem.value)
}
const onClose = () => {
// orderStore.clearPodBox()
emit('refresh')
}
// 下一步
const nextStep = async (callback: () => void) => {
const nextStep = async (callback: () => void, data?: OrderData) => {
const everyPicked = podOrderDetailsData.value?.productList?.every(
(item) => item.count === item.purchaseNumber,
)
......@@ -782,7 +784,9 @@ const nextStep = async (callback: () => void) => {
) {
try {
await ElMessageBox.alert(
'当前订单验货完成并打印面单成功,是否转至已完成',
`当前订单验货完成并打印面单成功,是否转至${
data?.replaceShipment == 0 ? '已完成' : '待称重'
}`,
'提示',
{
confirmButtonText: '确定',
......@@ -800,9 +804,13 @@ const nextStep = async (callback: () => void) => {
callback && callback()
}
}
const currentItem = ref<OrderData>({})
const handleBoxClick = (item: PodMakeOrderData) => {
const { box, data } = item
isBillLading.value = !data?.filePath
currentItem.value = data as OrderData
nextStep(() => {
if (!data) {
ElMessage.warning('暂无数据')
......@@ -811,7 +819,7 @@ const handleBoxClick = (item: PodMakeOrderData) => {
boxIndex.value = box || null
boxChange.value = true
productionOrderRef.value.focus()
})
}, currentItem.value)
}
const handleClearBox = async () => {
try {
......@@ -920,8 +928,9 @@ const clearAllBox = async () => {
}
const handleRowClick = (row: ProductList) => {
console.log(907, row)
const previewImages = row.productMark!=='normal'?row.previewImgs:[{url:row.variantImage}]
coverImage.value =previewImages?.[0]?.url || ''
const previewImages =
row.productMark !== 'normal' ? row.previewImgs : [{ url: row.variantImage }]
coverImage.value = previewImages?.[0]?.url || ''
productionOrderRef.value.focus()
}
const handleCurrentChange = (url: string) => {
......
<template>
<div>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
title="称重分拣"
:before-close="handleClose"
v-model="isweight"
width="1200px"
>
<div
style="
display: flex;
justify-content: space-between;
margin-bottom: 10px;
gap: 10px;
"
>
<el-select
style="width: 100px; height: 100%; margin-right: 5px"
v-model="selectType"
placeholder=""
>
<el-option label="跟踪号" value="trackingNumber"></el-option>
<el-option label="店铺单号" value="shopNumber"></el-option>
</el-select>
<input
class="inputWeight"
@keyup.enter="weightChange"
style="flex: 1; border: 3px solid blue"
ref="weighInput"
:placeholder="
weight.weightInput
? selectType === 'trackingNumber'
? '请输入跟踪号'
: '请输入店铺单号'
: '请输入重量'
"
v-model.lazy="weightText"
/>
<el-select
style="flex: 1"
v-model="logisticsCompanyCode"
placeholder="物流公司"
filterable
clearable
>
<el-option
v-for="(item, index) in allCodelist"
:key="index"
:label="item.basicsName"
:value="item.code"
></el-option>
</el-select>
<el-button type="primary" @click="weightChange"> 查询 </el-button>
</div>
<div>
<CustomizeTable
ref="tableRef"
border
:isShowCheckBox="false"
v-model="tableData"
height="400px"
:config="tableConfig"
align="center"
:row-config="{ isCurrent: true }"
></CustomizeTable>
</div>
<div class="statistics" style="display: flex; margin-top: 10px">
<div class="number">
<span>数量</span>
<span style="color: red">{{ tableData.length }}</span>
</div>
<div class="warehouse-weight" style="margin-left: 10px">
<span> 出库总重量:</span>
<span style="color: red">{{ warehouseWeightTotal() || 0 }}(g)</span>
</div>
</div>
<template #footer>
<div style="display: flex; justify-content: center">
<el-button @click="handleClose()"> 取消 </el-button>
<el-button type="primary" @click="weightGet"> 确定 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="tsx">
interface IpodCnWeighingParams {
id?: string
outWarehouseWeight?: string
weight?: string
trackingNumber?: string
shopNumber?: string
platform?: string
logisticsCompanyName?: string
logisticsCompanyId?: string
wgap?: number | string
}
interface ILogisticsList {
code: string
basicsName: string
apiData: unknown
}
import BigNumber from 'bignumber.js'
import weight from '../components/weigh.js'
import {
listByNoApi,
orderWeighingApi,
allErpCodeListApi,
} from '@/api/podCnOrder'
// import { logisticsCompanyAllCodelist } from '@/api/logistics.ts'
import CustomizeTable from '@/components/VxeTable.tsx'
import { TableColumn } from '@/components/VxeTable'
const tableData = ref<IpodCnWeighingParams[]>([])
const isweight = ref(false)
const selectType = ref('trackingNumber')
const logisticsCompanyCode = ref('')
const weighInput = ref()
const weightText = ref('')
const allCodelist = ref<ILogisticsList[]>([])
const tableRef = ref()
const tableConfig = ref<TableColumn[]>([
{
prop: 'trackingNumber',
label: '跟踪号',
attrs: {
minWidth: 120,
},
},
{
prop: 'shopNumber',
label: '店铺单号',
attrs: {
minWidth: 120,
},
},
{
prop: 'platform',
label: '销售平台',
attrs: {
width: 120,
},
},
{
prop: 'logisticsCompanyName',
label: '物流公司',
attrs: {
width: 120,
},
},
{
prop: 'weight',
label: '系统重量(g)',
attrs: {
width: 100,
},
},
{
prop: 'outWarehouseWeight',
label: '出库重量(g)',
attrs: {
width: 100,
},
},
{
prop: 'wgap',
label: '重量差(g)',
attrs: {
width: 100,
},
},
{
prop: 'opeare',
label: '操作',
attrs: {
align: 'center',
width: 80,
},
render: {
default: ({ row }: { row: IpodCnWeighingParams }) => (
<div>
<el-button type="danger" onclick={() => deleteFn(row)}>
删除
</el-button>
</div>
),
},
},
])
const emits = defineEmits<{
(e: 'updateList'): void
}>()
const getlogisticsCompanyAllCodelist = async () => {
try {
const res = await allErpCodeListApi()
if (res.code !== 200) return
allCodelist.value = res.data
} catch (e) {
console.error(e)
}
}
const warehouseWeightTotal = (): number => {
if (!tableData.value) return 0
return Number(
tableData.value
.reduce((prev: BigNumber, cur: IpodCnWeighingParams) => {
const weight = new BigNumber(cur.outWarehouseWeight || 0)
return prev.plus(weight)
}, new BigNumber(0))
.toFixed(2), // 保留两位小数
)
}
const handleClose = () => {
isweight.value = false
}
const weightChange = async () => {
const noValue = weightText.value
if (!noValue) return weight.playAudio('weight_search_error')
if (weight.weightInput) {
if (noValue.length < 7)
return weight.playAudio(
'weight_search_error',
`没有该${
selectType.value === 'trackingNumber' ? '跟踪号' : '店铺单号'
}相关的订单`,
)
}
weightText.value = ''
console.log(343, weight.weightInput)
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const params: {
type: string
no: string
logisticsCompanyCode?: string
} = {
type: selectType.value,
no: noValue,
}
if (logisticsCompanyCode.value)
params.logisticsCompanyCode = logisticsCompanyCode.value
weight.check(
noValue,
listByNoApi,
params,
(arr) => {
tableData.value = [...arr]
if (!(noValue.length < 7)) {
tableData.value.forEach((el) => {
if (el.trackingNumber === noValue || el.shopNumber === noValue) {
console.log(1111, el)
tableRef.value?.selectRowEvent(el)
}
})
}
console.log(284, arr)
},
selectType.value,
)
weighInput.value?.focus()
} catch (error) {
console.log(error)
} finally {
loading.close()
}
console.log('weightChange')
}
const weightGet = async () => {
if (tableData.value.length < 1) {
ElMessage.warning('当前没有可提交的数据!')
return
}
for (const iterator of tableData.value) {
if (!iterator.outWarehouseWeight) {
ElMessage.warning('跟踪号:' + iterator.trackingNumber + '的出库重量为空')
return
}
}
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
await orderWeighingApi({ podCnWeighingParams: tableData.value })
ElMessage.success('保存称重分拣成功')
handleClose()
emits('updateList')
} catch (error) {
console.log(error)
} finally {
loading.close()
}
}
const open = () => {
isweight.value = true
weight.clear()
tableData.value = []
selectType.value = 'trackingNumber'
logisticsCompanyCode.value = ''
weightText.value = ''
getlogisticsCompanyAllCodelist()
}
const deleteFn = (row: IpodCnWeighingParams) => {
tableData.value = tableData.value.filter((el) => el.id !== row.id)
weight.updatedList(tableData.value)
}
defineExpose({
open,
})
</script>
<style scoped></style>
// Lock 基类
class Lock {
public isLock: boolean
constructor() {
this.isLock = false
}
lock(): void {
this.isLock = true
}
unlock(): void {
this.isLock = false
}
}
import { ElMessage } from 'element-plus'
import BigNumber from 'bignumber.js'
// 定义数据类型
interface WeighItem {
id?: string
outWarehouseWeight?: string
status?: string
trackingNumber?: string
shopNumber?: string
platform?: string
logisticsCompanyName?: string
logisticsCompanyCode?: string
logisticsCompanyId?: string
wgap?: number | string
weight?: string
}
interface SearchItem {
type: string
no: string
logisticsCompanyCode?: string
}
interface Data {
data: WeighItem[]
code: number
}
type AudioKey =
| 'weight_warning'
| 'weight_success'
| 'weight_repeat'
| 'weight_search_error'
| 'weight_search_success'
type AudioFiles = Record<AudioKey, string>
// API 响应类型
class Weigh extends Lock {
public weightInput: boolean
public list: WeighItem[]
public selectType: string
private audios: AudioFiles
private audioElements: Map<AudioKey, HTMLAudioElement>
constructor() {
super()
this.weightInput = true
this.selectType = 'trackingNumber'
this.list = []
this.audios = {
weight_warning: new URL(
'@/assets/audio/weight_warning.mp3',
import.meta.url,
).href,
weight_success: new URL(
'@/assets/audio/weight_success.mp3',
import.meta.url,
).href,
weight_repeat: new URL(
'@/assets/audio/weight_repeat.mp3',
import.meta.url,
).href,
weight_search_error: new URL(
'@/assets/audio/weight_search_error.mp3',
import.meta.url,
).href,
weight_search_success: new URL(
'@/assets/audio/weight_search_success.mp3',
import.meta.url,
).href,
}
this.audioElements = new Map()
this.preloadAudios()
}
// 预加载音频文件
private preloadAudios(): void {
Object.entries(this.audios).forEach(([key, src]) => {
const audio = new Audio()
audio.src = src
audio.preload = 'auto'
this.audioElements.set(key as AudioKey, audio)
})
}
clear(): void {
this.list = []
this.weightInput = true
this.selectType = 'trackingNumber'
}
updatedList(data: WeighItem[]) {
this.weightInput = true
this.list = [...data]
}
// 去重逻辑优化
private deduplicate(
value: string,
callback?: (list: WeighItem[]) => void,
): boolean {
const existingIndex = this.list.findIndex(
(item) => item.trackingNumber === value,
)
if (existingIndex !== -1) {
const [existingItem] = this.list.splice(existingIndex, 1)
this.list.unshift(existingItem)
callback?.(this.list)
this.playAudio('weight_repeat')
return true
}
return false
}
check(
value: string,
apiCall: (params: SearchItem) => Promise<Data>,
params: SearchItem,
callback?: (list: WeighItem[]) => void,
type?: string,
): void {
this.selectType = type as string
// 空值检查
if (!value?.trim()) {
this.playAudio('weight_warning')
return
}
// 防止重复提交
if (this.isLock) return
this.lock()
try {
// 判断输入类型并处理
if (this.isWeightInput(value)) {
this.processWeightInput(value, callback)
} else {
this.processTrackingNumberInput(value, apiCall, params, callback)
}
} catch (error) {
console.error('称重处理错误:', error)
this.playAudio('weight_search_error')
} finally {
this.unlock()
}
}
private isWeightInput(value: string): boolean {
return value.length < 7
}
private processWeightInput(
value: string,
callback?: (list: WeighItem[]) => void,
): void {
// 验证数字格式
if (isNaN(Number(value)) || Number(value) <= 0) {
this.playAudio('weight_warning', '请录入正确的重量')
return
}
console.log('currentItem', this.list)
if (!this.list.length) {
this.playAudio(
'weight_warning',
`请录入${this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'}`,
)
return
}
// 检查是否已录入重量
if (!this.hasPendingWeights()) {
this.playAudio(
'weight_warning',
`请录入${this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'}`,
)
return
}
this.list = this.list.map((el) => {
if (!el.outWarehouseWeight) {
// 确保输入值也是BigNumber处理过的
const valueNum = Number(value) || 0
const weightNum = Number(el.weight) || 0
const wgap = new BigNumber(valueNum)
.minus(weightNum)
.abs()
.decimalPlaces(2, BigNumber.ROUND_HALF_UP) // 明确指定四舍五入规则
.toNumber()
return {
...el,
outWarehouseWeight: value,
wgap: wgap,
}
}
return el
})
// 更新重量信息
callback?.(this.list)
this.weightInput = true
this.playAudio('weight_success')
}
private async processTrackingNumberInput(
value: string,
apiCall: (params: SearchItem) => Promise<Data>,
params: SearchItem,
callback?: (list: WeighItem[]) => void,
): Promise<void> {
// 检查重量录入状态
if (!this.weightInput) {
this.playAudio('weight_warning', '请录入重量')
return
}
// 去重检查
if (this.deduplicate(value, callback)) {
return
}
try {
const response = await apiCall(params)
console.log(211, response)
const { data } = response
if (!data.length) {
this.playAudio('weight_search_error', '查询失败')
return
}
// const waitWeighingList = data.filter(
// (el) => el.status === 'WAIT_WEIGHING',
// )
// if (waitWeighingList.length === 0) {
// this.playAudio(
// 'weight_search_error',
// `必须是待称重状态的订单下的${
// this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'
// }才能使用`,
// )
// return
// }
if (this.list?.length) {
const firstLogisticsCode = data[0]?.logisticsCompanyCode
const hasDifferentLogistics = this.list.some(
(el) =>
// 如果两个代码不都是undefined,并且它们不相等
!(
el.logisticsCompanyCode === undefined &&
firstLogisticsCode === undefined
) && el.logisticsCompanyCode !== firstLogisticsCode,
)
if (hasDifferentLogistics) {
this.playAudio(
'weight_search_error',
'当前查询的订单不属于所选择的物流公司,请核实后再试',
)
return
}
}
// 最终去重检查
if (
this.deduplicate(
(data[0] as WeighItem).trackingNumber as string,
callback,
)
) {
return
}
// 添加新项目
this.addNewItem(data, callback)
} catch (error) {
console.error('跟踪号查询错误:', error)
// this.playAudio('weight_search_error')
}
}
private addNewItem(
data: WeighItem[],
callback?: (list: WeighItem[]) => void,
): void {
const newArr = data.map((el) => {
return {
...el,
outWarehouseWeight: '',
wgap: '',
}
})
this.list = [...newArr, ...this.list]
this.weightInput = false
callback?.(this.list)
this.playAudio('weight_search_success')
}
playAudio(key: AudioKey, message?: string): void {
const messageMap: Record<AudioKey, string> = {
weight_warning: `请录入${
this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'
}或重量`,
weight_success: '',
weight_repeat: '重复录入',
weight_search_error: `请录入${
this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'
}或重量`,
weight_search_success: '',
}
const displayMessage = message || messageMap[key]
if (displayMessage) {
console.log(displayMessage)
ElMessage.warning(displayMessage)
}
// 使用预加载的音频元素
const audio = this.audioElements.get(key)
if (audio) {
audio.currentTime = 0 // 重置播放位置
audio.play().catch((error) => {
console.warn(`音频播放失败: ${key}`, error)
})
}
}
hasPendingWeights(): boolean {
return this.list.some((item) => !item.outWarehouseWeight)
}
getTotalWeight(): number {
return this.list.reduce((total, item) => {
return (
total + (item.outWarehouseWeight ? Number(item.outWarehouseWeight) : 0)
)
}, 0)
}
}
export default new Weigh()
......@@ -49,7 +49,9 @@
<div style="text-align: center">
<ElButton @click="cancelFn">取消</ElButton>
<ElButton type="primary" @click="saveSupplierFn">保存</ElButton>
<ElButton type="primary" :loading="saveLoading" @click="saveSupplierFn"
>保存</ElButton
>
</div>
</template>
</Dialog>
......@@ -58,17 +60,17 @@
title="管理供应价格"
v-model="priceDialogVisible"
width="65%"
v-loading="priceLoading"
@close="cancelPiceFn"
teleported
>
<div style="display: flex; margin-bottom: 10px">
<div style="display: flex; align-items: center; gap: 20px">
<div><span style="color: red">*</span> 结算币种</div>
<el-select
style="width: 300px; flex: 1"
v-model="currentGoods.currencyCode"
@change="
<div v-loading="priceLoading">
<div style="display: flex; margin-bottom: 10px">
<div style="display: flex; align-items: center; gap: 20px">
<div><span style="color: red">*</span> 结算币种</div>
<el-select
style="width: 300px; flex: 1"
v-model="currentGoods.currencyCode"
@change="
(v:string) => {
currentGoods.currencyName =
......@@ -78,95 +80,96 @@
}
"
>
<el-option
v-for="(item, index) in currencyOptions"
:key="index"
:label="`${item.currencyName}(${item.currencyCode})`"
:value="item.currencyCode"
>
</el-option>
</el-select>
</div>
</div>
<div
style="border-top: 1px solid #eee; margin-bottom: 10px"
v-if="showColorList.length"
>
<div style="font-size: 20px; font-weight: bold; margin: 10px 0">
颜色(Color)
</div>
<div class="flex" style="flex-wrap: wrap; gap: 5px">
<el-tag
:title="`${item.cnname}(${item.enname})`"
:color="item.bgColor"
v-for="(item, index) in showColorList"
:key="index"
:style="{ color: item.fontColor }"
class="tabBox"
:class="{ active: colorIndex === item.code }"
@click="optionSelection(item, 'color')"
>{{ item.cnname }}({{ item.enname }})</el-tag
>
<el-option
v-for="(item, index) in currencyOptions"
:key="index"
:label="`${item.currencyName}(${item.currencyCode})`"
:value="item.currencyCode"
>
</el-option>
</el-select>
</div>
</div>
</div>
<div
style="border-top: 1px solid #eee; margin-bottom: 10px"
v-if="showSizeList.length"
>
<div style="font-size: 20px; font-weight: bold; margin: 10px 0">
尺码(Size)
<div
style="border-top: 1px solid #eee; margin-bottom: 10px"
v-if="showColorList.length"
>
<div style="font-size: 20px; font-weight: bold; margin: 10px 0">
颜色(Color)
</div>
<div class="flex" style="flex-wrap: wrap; gap: 5px">
<el-tag
:title="`${item.cnname}(${item.enname})`"
:color="item.bgColor"
v-for="(item, index) in showColorList"
:key="index"
:style="{ color: item.fontColor }"
class="tabBox"
:class="{ active: colorIndex === item.code }"
@click="optionSelection(item, 'color')"
>{{ item.cnname }}({{ item.enname }})</el-tag
>
</div>
</div>
<div class="flex" style="flex-wrap: wrap; gap: 5px">
<el-tag
class="tabBox"
color="#ffff87"
style="color: #333"
effect="dark"
type="info"
v-for="(item, index) in showSizeList"
:key="index"
:class="{ active: sizeIndex === item.code }"
@click="optionSelection(item, 'size')"
>
{{ item.cnname }}</el-tag
>
<div
style="border-top: 1px solid #eee; margin-bottom: 10px"
v-if="showSizeList.length"
>
<div style="font-size: 20px; font-weight: bold; margin: 10px 0">
尺码(Size)
</div>
<div class="flex" style="flex-wrap: wrap; gap: 5px">
<el-tag
class="tabBox"
color="#ffff87"
style="color: #333"
effect="dark"
type="info"
v-for="(item, index) in showSizeList"
:key="index"
:class="{ active: sizeIndex === item.code }"
@click="optionSelection(item, 'size')"
>
{{ item.cnname }}</el-tag
>
</div>
</div>
</div>
<div
style="
display: flex;
justify-content: end;
border-top: 1px solid #eee;
"
>
<div style="margin: 10px 0">
<el-input
placeholder="请输入供应价格"
style="width: 200px; margin-right: 10px"
size="small"
v-model="supplierPirce"
clearable
></el-input
><ElButton type="primary" @click="updatePrices" size="small"
>批量更新供应价格</ElButton
>
<span style="color: #f56c6c; vertical-align: middle"
>(请注意!该操作会覆盖已有供应价格)</span
>
<div
style="
display: flex;
justify-content: end;
border-top: 1px solid #eee;
"
>
<div style="margin: 10px 0">
<el-input
placeholder="请输入供应价格"
style="width: 200px; margin-right: 10px"
size="small"
v-model="supplierPirce"
clearable
></el-input
><ElButton type="primary" @click="updatePrices" size="small"
>批量更新供应价格</ElButton
>
<span style="color: #f56c6c; vertical-align: middle"
>(请注意!该操作会覆盖已有供应价格)</span
>
</div>
</div>
<CustomizeTable
ref="tableRef"
border
style="margin-bottom: 20px"
v-model="pricetableData"
:config="priceTableConfig"
align="center"
height="500px"
@getCheckboxRecords="selectPirce"
></CustomizeTable>
</div>
<CustomizeTable
ref="tableRef"
border
style="margin-bottom: 20px"
v-model="pricetableData"
:config="priceTableConfig"
align="center"
height="500px"
@getCheckboxRecords="selectPirce"
></CustomizeTable>
<template #footer>
<div style="text-align: center">
<ElButton @click="cancelPiceFn">取消</ElButton>
......@@ -212,6 +215,7 @@ import {
IsizeType,
IPropertyResponseItem,
Iprice,
IPropertyItem,
} from './types/index.ts'
const [editForm, resetEditForm] = useValue<IsupplierType>({})
......@@ -658,6 +662,7 @@ function savePiceFn() {
/**
* @description: 检查数据
*/
const saveLoading = ref(false)
async function checkData(): Promise<{
isValid: boolean
postData: IsupplierType
......@@ -722,9 +727,10 @@ async function checkData(): Promise<{
* @description: 保存按钮
*/
const saveSupplierFn = debounce(async () => {
const { isValid, postData } = await checkData()
if (isValid) {
try {
saveLoading.value = true
try {
const { isValid, postData } = await checkData()
if (isValid) {
if (!postData.id) {
await addSupplierApi({
...postData,
......@@ -741,9 +747,11 @@ const saveSupplierFn = debounce(async () => {
cancelFn()
search()
} catch (e) {
return
}
} catch (e) {
return
} finally {
saveLoading.value = false
}
}, 400)
......@@ -945,7 +953,7 @@ async function addPice(product: IgoodsType) {
}
// 辅助函数:创建属性映射
function createPropertyMap(propertyList: any[]): Map<number, number[]> {
function createPropertyMap(propertyList: IPropertyItem[]): Map<number, number[]> {
const map = new Map<number, number[]>()
propertyList.forEach((item) => {
......
......@@ -20,7 +20,7 @@ export interface IgoodsType {
customProductItemList?: Iprice[]
supplierPriceItemList?: Iprice[]
customProductInfo?: IgoodsType
propertyList?: []
propertyList?: IPropertyItem[]
supplyPriceRange?: string
}
export interface IsupplierType {
......@@ -57,6 +57,11 @@ export interface IPropertyResponseItem {
valueList: IcolorType[] | IsizeType[]
}
export interface IPropertyItem {
propertyId?: number
valueId?: number
}
export interface Iprice {
productItemSku?: string
productItemImage?: string
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment