Commit a18364fe by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !204
parents 12e1e603 01701ca7
...@@ -57,6 +57,7 @@ declare module 'vue' { ...@@ -57,6 +57,7 @@ declare module 'vue' {
ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/Icon.vue')['default'] Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default'] ImageView: typeof import('./src/components/ImageView.vue')['default']
......
...@@ -47,7 +47,8 @@ ...@@ -47,7 +47,8 @@
"unplugin-auto-import": "^0.17.5", "unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5", "unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.1.4" "vite": "^5.1.4",
"vite-plugin-compression": "^0.5.1"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
...@@ -4320,6 +4321,20 @@ ...@@ -4320,6 +4321,20 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
...@@ -4509,6 +4524,12 @@ ...@@ -4509,6 +4524,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
"node_modules/graphemer": { "node_modules/graphemer": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
...@@ -4842,6 +4863,18 @@ ...@@ -4842,6 +4863,18 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/jsonfile": {
"version": "6.2.1",
"resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz",
"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
...@@ -6201,6 +6234,15 @@ ...@@ -6201,6 +6234,15 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/unplugin": { "node_modules/unplugin": {
"version": "1.16.1", "version": "1.16.1",
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz", "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz",
...@@ -6516,6 +6558,20 @@ ...@@ -6516,6 +6558,20 @@
} }
} }
}, },
"node_modules/vite-plugin-compression": {
"version": "0.5.1",
"resolved": "https://registry.npmmirror.com/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz",
"integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==",
"dev": true,
"dependencies": {
"chalk": "^4.1.2",
"debug": "^4.3.3",
"fs-extra": "^10.0.0"
},
"peerDependencies": {
"vite": ">=2.0.0"
}
},
"node_modules/vscode-uri": { "node_modules/vscode-uri": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
"unplugin-auto-import": "^0.17.5", "unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5", "unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.1.4" "vite": "^5.1.4",
"vite-plugin-compression": "^0.5.1"
} }
} }
import axios from './axios'
import type { BasePaginationData, BaseRespData } from '@/types/api'
import type { CancelOrderProcessStateGroupMap } from '@/types/api/cancelOrderProcess'
import type { operateOrderListData } from '@/types/api/factoryOrderNew'
function normalizePodOrderQueryPayload(
data: Record<string, unknown>,
): Record<string, unknown> {
const { receiverCountry, ...rest } = data
if (!Array.isArray(receiverCountry)) {
return data
}
return {
...rest,
...(receiverCountry.length
? { receiverCountry: receiverCountry.join(',') }
: {}),
}
}
export function getCancelOrderProcessStateGroupMapApi() {
return axios.get<never, BaseRespData<CancelOrderProcessStateGroupMap>>(
'factory/podOrderOperationForReclaim/findStateGroupMap',
)
}
export function getCancelOrderProcessListApi(
data: Record<string, unknown>,
currentPage: number,
pageSize: number,
alreadyInStock: boolean,
) {
const body = normalizePodOrderQueryPayload({ ...data })
return axios.post<never, BasePaginationData<operateOrderListData>>(
'factory/podOrderOperationForReclaim/list_page',
{ ...body, currentPage, pageSize, alreadyInStock },
)
}
export function getOperationInfoByNoApi(operationNo: string) {
return axios.get<never, BaseRespData<operateOrderListData>>(
`factory/podOrderOperationForReclaim/getByOperationNo?operationNo=${operationNo}`,
)
}
export function completeDeliveryByCancelOrderProcessApi(data: {
operationNo: string
inboundType: number
warehouseId: number
}) {
return axios.post<never, BaseRespData<never>>(
'factory/podOrderOperationForReclaim/storeAfterScan',
data,
)
}
...@@ -6,15 +6,15 @@ import type { ...@@ -6,15 +6,15 @@ import type {
LogListData, LogListData,
operateOrderListData, operateOrderListData,
PickCompleteData, PickCompleteData,
PickFailData,
ProductListData, ProductListData,
RestockData, RestockData,
SearchForm, SearchForm,
StatusTreeNode, StatusTreeNode,
} from '@/types/api/factoryOrderNew/factoryOrderNew' } from '@/types/api/factoryOrderNew'
import { ResultInfoDataItem } from '@/types/api/order/common' import { ResultInfoDataItem } from '@/types/api/order/common'
import { ExportParams } from '@/types/api/order/factoryOrderNew' import { ExportParams } from '@/types/api/order/factoryOrderNew'
import type { OrderData } from '@/types/api/podMakeOrder' import type { OrderData } from '@/types/api/podMakeOrder'
import type { LogisticsData } from '@/types/api/podCnOrder'
import type { PrintData } from '@/types/api/podOrder' import type { PrintData } from '@/types/api/podOrder'
function normalizePodOrderQueryPayload( function normalizePodOrderQueryPayload(
data: Record<string, unknown>, data: Record<string, unknown>,
...@@ -44,6 +44,7 @@ export function getFactoryOrderNewListApi( ...@@ -44,6 +44,7 @@ export function getFactoryOrderNewListApi(
status?: string, status?: string,
subStatus?: number, subStatus?: number,
pauseReason?: number, pauseReason?: number,
cancelSubType?: number,
) { ) {
const body = normalizePodOrderQueryPayload({ ...data } as Record< const body = normalizePodOrderQueryPayload({ ...data } as Record<
string, string,
...@@ -58,6 +59,7 @@ export function getFactoryOrderNewListApi( ...@@ -58,6 +59,7 @@ export function getFactoryOrderNewListApi(
status, status,
acceptedStatus: subStatus, acceptedStatus: subStatus,
pauseReason, pauseReason,
cancelSubType,
}, },
) )
} }
...@@ -112,6 +114,14 @@ export function getFactoryOrderNewLogApi(id: number | string) { ...@@ -112,6 +114,14 @@ export function getFactoryOrderNewLogApi(id: number | string) {
}, },
) )
} }
export function getOperationOrderByIdApi(id: number | string) {
return axios.get<never, BaseRespData<operateOrderListData[]>>(
'factory/podOrderOperation/getByPodOrderId',
{
params: { podOrderId: id },
},
)
}
export function refreshProductInfoApi(data: { export function refreshProductInfoApi(data: {
orderIds?: number | string orderIds?: number | string
...@@ -257,13 +267,6 @@ export function replenishmentCompleteApi(ids: (number | string)[]) { ...@@ -257,13 +267,6 @@ export function replenishmentCompleteApi(ids: (number | string)[]) {
) )
} }
export function pickFailApi(ids: (number | string)[]) {
return axios.post<never, BaseRespData<PickFailData[]>>(
'factory/podOrderOperation/listByIds',
{ ids },
)
}
export function getCardLayoutListApi( export function getCardLayoutListApi(
data: Record<string, unknown>, data: Record<string, unknown>,
currentPage: number, currentPage: number,
...@@ -308,6 +311,23 @@ export function getSuspendStatisticsApi( ...@@ -308,6 +311,23 @@ export function getSuspendStatisticsApi(
) )
} }
/** 已取消列表子 tab 数量统计(与挂起 suspendStatistics 用法一致) */
export function getCancelledOrderStatisticsApi(
data: SearchForm,
currentPage: number,
pageSize: number,
cancelSubType?: number,
) {
const body = normalizePodOrderQueryPayload({ ...data } as Record<
string,
unknown
>)
return axios.post<never, BaseRespData<Record<string, number>>>(
'factory/podOrder/cancelledStatistics',
{ ...body, currentPage, pageSize, cancelSubType },
)
}
export function getSuspendDetailApi(id: number | string) { export function getSuspendDetailApi(id: number | string) {
return axios.get<never, BaseRespData<ProductListData[]>>( return axios.get<never, BaseRespData<ProductListData[]>>(
'factory/podOrderPauseControl/get', 'factory/podOrderPauseControl/get',
...@@ -467,6 +487,26 @@ export function syncReceiverAddress(data: number[]) { ...@@ -467,6 +487,26 @@ export function syncReceiverAddress(data: number[]) {
) )
} }
export function getLogisticsCalculation(id: number) {
return axios.get<never, BaseRespData<never>>(
'factory/podOrder/getLogisticsCalculation',
{ params: { id } },
)
}
export function changeLogisticsApi(params: {
updateByIdParam: {
id: string | number
dataVersion: number
}
logisticsTrialCalculation: LogisticsData
}) {
return axios.post<never, BaseRespData<never>>(
'factory/podOrder/changeLogistics',
params,
)
}
export function updateReceiverAddressApi(data: object) { export function updateReceiverAddressApi(data: object) {
return axios.post<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'factory/podOrder/updateReceiverAddress', 'factory/podOrder/updateReceiverAddress',
...@@ -590,3 +630,19 @@ export function exportFactoryOrderInfo(data: ExportParams) { ...@@ -590,3 +630,19 @@ export function exportFactoryOrderInfo(data: ExportParams) {
data, data,
) )
} }
export function interceptUpdateApi(ids: (string | number)[]) {
return axios.post<never, BaseRespData<ResultInfoDataItem[]>>(
'factory/podOrder/factoryOrderUnblock',
ids,
)
}
export function interceptSuccessApi(
data: { factoryOrderNumber: string; suspendSussessType: number }[],
) {
return axios.post<never, BaseRespData<ResultInfoDataItem[]>>(
'factory/podOrder/factoryOrderBlockSuccess',
data,
)
}
...@@ -66,6 +66,24 @@ export function getOrderList( ...@@ -66,6 +66,24 @@ export function getOrderList(
}, },
) )
} }
export function getOrderTrackingList(
params: SearchForm,
currentPage: number,
pageSize: number,
orderTracking?: boolean,
) {
return axios.post<never, BasePaginationData<PodUsOrderListData>>(
'/factory/podJomallOrderUs/get-order-tracking-list',
{
...params,
currentPage,
pageSize,
orderTracking,
},
)
}
export function handleExceptionOrderApi(orderIds: number[]) { export function handleExceptionOrderApi(orderIds: number[]) {
return axios.post<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/processExceptionOrders', 'factory/podJomallOrderUs/processExceptionOrders',
......
...@@ -107,6 +107,14 @@ const router = createRouter({ ...@@ -107,6 +107,14 @@ const router = createRouter({
import('@/views/order/factoryOrderNew/index.vue'), import('@/views/order/factoryOrderNew/index.vue'),
}, },
{ {
path:'/order/cancel-order-process',
meta: {
title: '取消后订单处理',
},
component: () =>
import('@/views/order/cancelOrderProcess/index.vue'),
},
{
path: '/pod-cn-order/orderTracking', path: '/pod-cn-order/orderTracking',
meta: { meta: {
title: 'POD(CN)订单跟踪', title: 'POD(CN)订单跟踪',
......
...@@ -151,6 +151,11 @@ const menu: MenuItem[] = [ ...@@ -151,6 +151,11 @@ const menu: MenuItem[] = [
id: 12, id: 12,
label: '工厂订单(NEW)', label: '工厂订单(NEW)',
}, },
// {
// index: '/order/cancel-order-process',
// id: 13,
// label: '取消后订单处理',
// },
], ],
}, },
{ {
......
export interface CancelOrderProcessStateGroupMap {
alreadyInStock: number
NotInStock: number
}
import type { PaginationData } from '@/types/api'
export interface StatusTreeNode { export interface StatusTreeNode {
status: string status: string
statusName: string statusName: string
...@@ -271,3 +272,9 @@ export interface PickFailData { ...@@ -271,3 +272,9 @@ export interface PickFailData {
afterOutOccupied?: number afterOutOccupied?: number
afterOutAvailable?: number afterOutAvailable?: number
} }
export type CardLayoutListFetcher = (
payload: Record<string, unknown>,
currentPage: number,
pageSize: number,
) => Promise<PaginationData<operateOrderListData>>
...@@ -92,6 +92,8 @@ export interface FactoryOrderNewListData { ...@@ -92,6 +92,8 @@ export interface FactoryOrderNewListData {
source?: string source?: string
logisticsCompanyName?: string logisticsCompanyName?: string
logisticsCompanyCode?: string logisticsCompanyCode?: string
logisticsWayId?: number | string
logisticsWayName?: string
selfOwned?: boolean selfOwned?: boolean
createTime?: string createTime?: string
updateTime?: string updateTime?: string
......
<template>
<div class="page card h-100 flex-gap-10 overflow-hidden flex">
<div class="order-status">
<div
class="order-status-item"
:class="{ 'order-status-item-active': currentStatus === 'unprocessed' }"
@click="handleOrderStatusClick('unprocessed')"
>
<span>待处理</span>
<span>({{ orderStatus.NotInStock }})</span>
</div>
<div
class="order-status-item"
:class="{ 'order-status-item-active': currentStatus === 'processed' }"
@click="handleOrderStatusClick('processed')"
>
<span>已处理</span>
<span>({{ orderStatus.alreadyInStock }})</span>
</div>
</div>
<div class="order-content flex-1 flex-column overflow-hidden">
<div class="header">
<div class="header-filter-form">
<ElForm
class="search-form"
:model="searchForm"
:inline="true"
label-width="70px"
>
<ElFormItem label="平台">
<ElSelect
v-model="searchForm.platform"
placeholder="请选择"
clearable
filterable
popper-class="customize-select-style"
style="width: 140px"
>
<ElOption
v-for="(item, index) in platformJson"
:key="index"
:label="item.type"
:value="item.type"
style="width: 160px"
>
<img
:src="`/images/icon/${item.icon.split('/').pop()}`"
style="height: 20px; margin: 5px 10px 0 0"
/>
<span :title="item.type">{{ item.type }}</span>
</ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="工艺">
<LogisticsWaySelect
v-model="searchForm.craftCode"
:company-list="craftList"
:start-width="'150px'"
search-placeholder="搜索工艺名称"
start-placeholder="请选择工艺名称"
></LogisticsWaySelect>
</ElFormItem>
<ElFormItem label="库存SKU">
<ElInput
v-model.trim="searchForm.thirdSkuCode"
placeholder="库存SKU"
clearable
style="width: 140px"
/>
</ElFormItem>
<ElFormItem label="店铺单号">
<ElInput
v-model.trim="searchForm.shopNumber"
placeholder="店铺单号"
clearable
style="width: 150px"
/>
</ElFormItem>
<ElFormItem label="批次号">
<ElInput
v-model.trim="searchForm.batchArrangeNumber"
placeholder="批次号"
clearable
style="width: 140px"
/>
</ElFormItem>
<ElFormItem label="操作单号">
<ElInput
v-model.trim="searchForm.operationNo"
placeholder="操作单号"
clearable
style="width: 140px"
/>
</ElFormItem>
<ElFormItem label="商品类型">
<ProductTypeFilter
v-model="searchForm.productMarkList"
:options="productTypeGroups"
:multiple="true"
placeholder="请选择商品类型"
width="140px"
/>
</ElFormItem>
<ElFormItem label="数量">
<el-radio-group v-model="searchForm.multi">
<el-radio-button :value="false" @click="toggleMulti(false)"
>单件</el-radio-button
>
<el-radio-button :value="true" @click="toggleMulti(true)"
>多件</el-radio-button
>
</el-radio-group>
</ElFormItem>
<ElFormItem>
<ElPopover placement="bottom" width="600" trigger="click">
<ElForm
ref="searchFormPopoverRef"
:model="searchForm"
size="default"
inline
label-width="100px"
>
<ElFormItem style="width: 100%; padding-right: 40px">
<div style="width: 100%; display: flex; flex-wrap: nowrap">
<el-select
v-model="searchForm.timeType"
clearable
:teleported="false"
placeholder="时间类型"
style="flex: 1; margin-right: 10px"
>
<el-option :value="1" label="创建时间"></el-option>
<el-option :value="2" label="确认时间"></el-option>
<el-option :value="3" label="完成时间"></el-option>
</el-select>
<el-date-picker
v-model="dateRange"
:teleported="false"
:default-time="[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
style="width: 280px"
:shortcuts="pickerOptions.shortcuts"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
/>
</div>
</ElFormItem>
<ElFormItem label="订单号">
<ElInput
v-model.trim="searchForm.factoryOrderNumber"
placeholder="订单号"
clearable
style="width: 150px"
/>
</ElFormItem>
<ElFormItem label="客户">
<el-select
v-model="searchForm.userMark"
clearable
filterable
:teleported="false"
style="width: 150px"
placeholder="客户"
>
<el-option
v-for="item in userMarkList"
:key="item"
:value="item"
:label="item"
></el-option>
</el-select>
</ElFormItem>
<ElFormItem label="Variant SKU">
<ElInput
v-model.trim="searchForm.sku"
placeholder="Variant SKU"
clearable
style="width: 150px"
/>
</ElFormItem>
<ElFormItem label="款号">
<ElInput
v-model.trim="searchForm.supplierProductNo"
placeholder="款号"
clearable
style="width: 150px"
/>
</ElFormItem>
<ElFormItem label="物流跟踪号">
<ElInput
v-model.trim="searchForm.trackingNumber"
placeholder="物流跟踪号"
clearable
style="width: 150px"
/>
</ElFormItem>
<ElFormItem label="物流类型">
<ElSelect
v-model="searchForm.shipmentType"
placeholder="物流类型"
clearable
:teleported="false"
style="width: 150px"
>
<ElOption
v-for="(item, index) in [
'自有物流',
'九猫统筹物流',
'自提',
]"
:key="index"
:value="index"
:label="item"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="客户标签">
<ElSelect
v-model="searchForm.tagsIdArr"
placeholder="请选择客户标签"
clearable
filterable
multiple
collapse-tags
collapse-tags-tooltip
:teleported="false"
style="width: 150px"
>
<ElOption
v-for="item in customTagList"
:key="item.id"
:value="item.id"
:label="item.name"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="自有物流公司">
<ElSelect
v-model="searchForm.logisticsCompanyCode"
placeholder="请选择"
clearable
filterable
:teleported="false"
style="width: 150px"
>
<ElOption
v-for="(item, index) in allCodelist"
:key="index"
:value="item.code"
:label="item.basicsName"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="规范素材">
<ElSelect
v-model="searchForm.newStandard"
placeholder="请选择"
clearable
filterable
style="width: 150px"
>
<ElOption
v-for="(_, index) in ['否', '是']"
:key="index"
:value="index"
:label="index === 0 ? '否' : '是'"
></ElOption>
</ElSelect>
</ElFormItem>
</ElForm>
<template #reference>
<el-button
type="warning"
@click="searchVisible = !searchVisible"
>
<el-icon v-if="searchVisible">
<CaretTop />
</el-icon>
<el-icon v-else>
<CaretBottom />
</el-icon>
</el-button>
</template>
</ElPopover>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search"> 查询 </ElButton>
<ElButton @click="reset"> 重置 </ElButton>
</ElFormItem>
</ElForm>
</div>
</div>
<div class="operation-list">
<span v-if="currentStatus === 'unprocessed'" class="item">
<ElButton type="primary" @click="handleEmbryoInStock"
>胚衣入库</ElButton
>
</span>
<span v-if="currentStatus === 'unprocessed'" class="item">
<ElButton type="success" @click="handleProductionCompleteInStock"
>生产完成入库</ElButton
>
</span>
</div>
<div class="card-content">
<CardLayout
ref="cardLayoutRef"
:query-payload="getQueryPayload()"
:fetch-list="fetchCardList"
@selection-change="handleCardSelectionChange"
@view-detail="handleViewDetail"
/>
</div>
</div>
</div>
<FastProduction
v-model:detail-visible="detailVisible"
:is-new-order="true"
:detail-data="detailData"
:fast-key="fastKey"
:default-auto-sure="true"
:show-operation-no-row="true"
not-found-message="操作单不存在"
pending-order-label="操作单号"
please-scan-tip="请扫码操作单号"
search-input-audio-tip="请录入操作单号"
:history-storage-key="fastHistoryStorageKey"
tracking-placeholder="扫描枪输入操作单号"
:query-api="getOperationByNo"
:complete-api="completeOperationById"
@on-success="handleFastProductionSuccess"
@close="fastClose"
/>
<ReceiptProductDialog
ref="receiptProductDialogRef"
v-model:visible="receiptDialogVisible"
v-model:user-mark="receiptUserMark"
v-model:select-sku="receiptSelectSku"
title="创建入库单"
:disable-warehouse="true"
:show-user-mark-filter="false"
:show-query-button="false"
:show-batch-add-button="false"
:show-import-button="false"
:edit-form="receiptEditForm"
:rules="receiptRules"
:warehouse-list="warehouseList"
:other-purchase-data="otherPurchaseData"
:location-list="locationList"
:user-mark-list="receiptUserMarkList"
:sku-data="skuData"
:filter-sku-data="filterSkuData"
@warehouse-change="handleReceiptWarehouseChange"
@selection-change="handleReceiptProductSelectionChange"
@set-cost-price="setCostPrice"
@location-change="handleReceiptLocationChange"
@update:remark="updateReceiptRemark"
@update:warehouse-id="updateReceiptWarehouseId"
@delete="deleteReceiptProducts"
@save="handleReceiptSave"
/>
<!-- 快捷入库 -->
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted, ref } from 'vue'
import CardLayout from '../factoryOrderNew/component/CardLayout.vue'
import platformJson from '../../../json/platform.json'
import { useOrderDictionaries } from '../factoryOrderNew/hooks/useOrderDictionaries'
import { operateOrderListData } from '@/types/api/factoryOrderNew'
import { getFactoryOrderNewOperateDetailApi } from '@/api/factoryOrderNew'
import {
getCancelOrderProcessListApi,
getCancelOrderProcessStateGroupMapApi,
getOperationInfoByNoApi,
completeDeliveryByCancelOrderProcessApi,
} from '@/api/cancelOrderProcess'
import type { CardLayoutListFetcher } from '@/types/api/factoryOrderNew'
import LogisticsWaySelect from '@/views/logistics/components/LogisticsWaySelect'
import { SearchForm } from '@/types/api/order/factoryOrderNew'
import ProductTypeFilter from '../factoryOrderNew/component/ProductTypeFilter.vue'
import { CaretBottom, CaretTop } from '@element-plus/icons-vue'
import { normalizeProductMarkListForQuery } from '../factoryOrderNew/utils/productMarkQuery'
import FastProduction from '../components/FastProduction.vue'
import { ResultInfoDataItem } from '@/types/api/order/common'
import { ElButton } from 'element-plus'
import ReceiptProductDialog from '@/views/warehouse/components/ReceiptProductDialog.vue'
import { useValue } from '@/utils/hooks/useValue'
import {
ILocation,
InterProductList,
InterskuList,
InterWarehouseDetail,
} from '@/types/api/warehouse'
import { useReceiptProductDialog } from '@/views/warehouse/hooks/useReceiptProductDialog'
import {
addInRecordApi,
getBySkuAndUserMarkApi,
getByWareHouseIdAndCodeApi,
warehouseInfo,
warehouseInfoGetAll,
} from '@/api/warehouse'
import type { CancelOrderProcessStateGroupMap } from '@/types/api/cancelOrderProcess'
const cardLayoutRef = ref()
const fetchCardList: CardLayoutListFetcher = async (payload, current, size) => {
const res = await getCancelOrderProcessListApi(
payload,
current,
size,
currentStatus.value === 'processed',
)
return res.data
}
const searchForm = ref<SearchForm>({})
const dateRange = ref<[string, string]>(['', ''])
const searchVisible = ref(false)
const pickerOptions = {
shortcuts: [
{
text: '今日',
value: () => {
const start = new Date()
start.setHours(0, 0, 0, 0)
const end = new Date()
end.setHours(23, 59, 59, 999)
return [start, end]
},
},
{
text: '最近7天',
value: () => {
const end = new Date()
end.setHours(23, 59, 59, 999)
const start = new Date()
start.setDate(start.getDate() - 6)
start.setHours(0, 0, 0, 0)
return [start, end]
},
},
{
text: '最近30天',
value: () => {
const end = new Date()
end.setHours(23, 59, 59, 999)
const start = new Date()
start.setDate(start.getDate() - 29)
start.setHours(0, 0, 0, 0)
return [start, end]
},
},
],
}
const toggleMulti = (value: boolean) => {
searchForm.value.multi = value
}
const getQueryPayload = () => {
const { productMarkList, tagsIdArr, ...rest } = searchForm.value
return {
...rest,
...normalizeProductMarkListForQuery(productMarkList),
...(Array.isArray(tagsIdArr) && tagsIdArr.length
? { tagsId: tagsIdArr.join(',') }
: {}),
startTime: dateRange.value?.[0] || null,
endTime: dateRange.value?.[1] || null,
}
}
const reset = () => {
searchForm.value = {}
dateRange.value = ['', '']
searchVisible.value = false
nextTick(() => {
search()
})
}
const loadOrderStatusCounts = async () => {
try {
const res = await getCancelOrderProcessStateGroupMapApi()
if (res.code !== 200) return
orderStatus.value = {
alreadyInStock: res.data.alreadyInStock ?? 0,
NotInStock: res.data.NotInStock ?? 0,
}
} catch (e) {
console.error(e)
}
}
const search = () => {
cardLayoutRef.value?.clearSelection()
nextTick(() => {
cardLayoutRef.value?.refresh()
})
}
const cardSelectList = ref<operateOrderListData[]>([])
const handleCardSelectionChange = (items: operateOrderListData[]) => {
cardSelectList.value = items
}
const detailVisible = ref(false)
const fastKey = ref('')
const detailData = ref<Record<string, unknown>>({})
const handleViewDetail = async (item: operateOrderListData) => {
const loading = ElLoading.service({
fullscreen: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const res = await getFactoryOrderNewOperateDetailApi(item.id)
if (res.code !== 200) return
detailData.value = res.data
fastKey.value = 'detail'
detailVisible.value = true
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const getOperationByNo = (operationNo: string) =>
getOperationInfoByNoApi(operationNo) as Promise<{ data?: unknown }>
const fastInboundType = ref(0)
const fastHistoryStorageKey = computed(() =>
fastInboundType.value === 1
? 'historyCancelOrderProcessProductionCompleteInStock'
: 'historyCancelOrderProcessEmbryoInStock',
)
const completeOperationById = (_: number[], detail: Record<string, unknown>) =>
completeDeliveryByCancelOrderProcessApi({
operationNo: detail.operationNo as string,
warehouseId: detail.warehouseId as number,
inboundType: fastInboundType.value,
})
const handleFastProductionSuccess = (data: ResultInfoDataItem[]) => {
const result = data[0]
if (!result.status) {
ElMessageBox.alert(
`操作单号 ${result.factoryOrderNumber} ${result.message}`,
'提示',
{
type: 'error',
},
)
return
}
}
const fastClose = () => {
detailVisible.value = false
if (fastKey.value === 'detail') return
search()
}
const {
userMarkList,
customTagList,
allCodelist,
craftList,
productTypeGroups,
loadAllDictionaries,
} = useOrderDictionaries()
const orderStatus = ref<CancelOrderProcessStateGroupMap>({
alreadyInStock: 0,
NotInStock: 0,
})
const currentStatus = ref<'processed' | 'unprocessed'>('unprocessed')
const handleOrderStatusClick = (status: 'processed' | 'unprocessed') => {
currentStatus.value = status
search()
}
const receiptDialogVisible = ref(false)
const receiptUserMark = ref(0)
const receiptSelectSku = ref('')
const otherPurchaseData = ref<InterProductList[]>([])
const [receiptEditForm, resetReceiptEditForm] = useValue<InterWarehouseDetail>({
inNo: '',
warehouseId: '',
warehouseName: '',
remark: '',
factoryCode: '',
factoryId: 0,
productList: [],
})
const receiptRules = {
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
}
const batchUserMark = ref(0)
const importUserMark = ref(0)
const receiptUserMarkList = ref<
{ userId: number; userMark: string; userName: string }[]
>([{ userId: 0, userMark: '', userName: '' }])
const warehouseList = ref<warehouseInfo[]>([])
const locationList = ref<ILocation[]>([])
const otherReceiptSelection = ref<InterProductList[]>([])
const receiptProductDialogRef = ref()
const { skuData, setCostPrice, filterSkuData } = useReceiptProductDialog({
editForm: receiptEditForm,
otherPurchaseData,
userMark: receiptUserMark,
batchUserMark,
importUserMark,
selectSku: receiptSelectSku,
userMarkList: receiptUserMarkList,
})
function mapInterskuToProduct(val: InterskuList): InterProductList {
const warehouseSku = val.warehouseSku || val.sku || ''
const skuNameVal = val.skuName || val.productName || ''
let customerId: number | null = null
if (val.customerId != null && val.customerId !== '') {
const n = Number(val.customerId)
if (!Number.isNaN(n)) customerId = n
}
return {
skuImage: val.skuImage || val.image || '',
warehouseSku,
skuName: skuNameVal,
productNo: val.productNo ?? null,
locationCode: val.locationCode ?? '',
locationId: val.locationId ?? null,
costPrice: val.costPrice ?? null,
buyStored: null,
totalPrice: null,
currencyName: val.currencyName ?? null,
currencyCode: val.currencyCode ?? null,
customerId,
customerName: val.customerName ?? null,
userMark: val.userMark ?? null,
}
}
const handleEmbryoInStock = async () => {
if (cardSelectList.value.length !== 0) {
handleEmbryoInStockByCard()
} else {
handleEmbryoInFastStock(0)
}
}
const handleEmbryoInFastStock = (inboundType: number) => {
fastInboundType.value = inboundType
fastKey.value = 'fastReceipt'
detailVisible.value = true
detailData.value = {}
}
const handleEmbryoInStockByCard = async () => {
const firstWid = cardSelectList.value[0].warehouseId
if (cardSelectList.value.some((r) => r.warehouseId !== firstWid)) {
return ElMessage.error('请选择相同仓库的库存SKU!')
}
resetReceiptEditForm()
otherPurchaseData.value = []
receiptUserMark.value = 0
receiptSelectSku.value = ''
skuData.value = []
otherReceiptSelection.value = []
const userJson = localStorage.getItem('user')
if (userJson) {
try {
const userData = JSON.parse(userJson)
receiptEditForm.value.factoryCode = userData.factoryCode || ''
receiptEditForm.value.factoryId = userData.factoryId || 0
} catch {
// ignore
}
}
receiptEditForm.value.warehouseId = firstWid
receiptEditForm.value.warehouseName = cardSelectList.value[0]
.warehouseName as string
const wh = warehouseList.value.find((w) => w.id == firstWid)
if (wh) {
receiptEditForm.value.warehouseName = wh.name
}
const loading = ElLoading.service({
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const skus = cardSelectList.value.map((r) => r.thirdSkuCode as string)
const res = await getBySkuAndUserMarkApi(firstWid, skus.join(','), null)
if (res.code !== 200) return
otherPurchaseData.value = res.data.map((item: InterskuList) =>
mapInterskuToProduct(item),
)
await fetchReceiptLocationList('')
const listRes = await warehouseInfoGetAll()
warehouseList.value = listRes.data || []
receiptDialogVisible.value = true
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const fetchReceiptLocationList = async (query: string) => {
if (!receiptEditForm.value.warehouseId) return
try {
const res = await getByWareHouseIdAndCodeApi(
receiptEditForm.value.warehouseId,
query,
)
const result = res.data || []
locationList.value = result.map((item: ILocation) => ({
locationId: item.id,
locationCode: item.locationCode,
}))
} catch (e) {
console.error(e)
}
}
const handleReceiptWarehouseChange = (val: number | string | undefined) => {
const found = warehouseList.value.find(
(item: warehouseInfo) => item.id === val,
)
receiptEditForm.value.warehouseName = found ? found.name : ''
}
const handleReceiptProductSelectionChange = (v: InterProductList[]) => {
otherReceiptSelection.value = v
}
const handleReceiptLocationChange = (
val: number | null | undefined,
row: InterProductList,
) => {
const found = locationList.value.find(
(item: ILocation) => item.locationId === val,
)
row.locationCode = found ? found.locationCode : ''
}
const updateReceiptRemark = (value: string) => {
receiptEditForm.value.remark = value
}
const updateReceiptWarehouseId = (value: number | string | undefined) => {
receiptEditForm.value.warehouseId = value
}
const deleteReceiptProducts = () => {
const arr = otherReceiptSelection.value
if (arr.length === 0) return
const idList = arr.map((v: InterProductList) => v.warehouseSku)
otherPurchaseData.value = otherPurchaseData.value.filter(
(item: InterProductList) => !idList.includes(item.warehouseSku),
)
}
const handleReceiptSave = async () => {
try {
await receiptProductDialogRef.value?.validateForm()
} catch {
return
}
const arr = otherPurchaseData.value
if (arr.length === 0) {
ElMessage.error('请至少选择一条数据')
return
}
for (let i = 0; i < arr.length; i++) {
if (!arr[i].buyStored) {
ElMessage.error('请输入入库数量')
return
}
if (!arr[i].locationId) {
ElMessage.error('请选择库位')
return
}
const found = locationList.value.find(
(item: ILocation) => item.locationId === arr[i].locationId,
)
if (!arr[i].locationCode) {
arr[i].locationCode = found ? found?.locationCode : ''
}
}
const params = {
...receiptEditForm.value,
productList: otherPurchaseData.value,
}
const loading = ElLoading.service({
text: '保存中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
await addInRecordApi(params)
ElMessage.success('保存成功')
receiptDialogVisible.value = false
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const handleProductionCompleteInStock = () => {
// 客户定制的单子不能操作生产完成入库,提示:客户定制商品,不能入库!
if (cardSelectList.value.length !== 0) {
if (cardSelectList.value.some((r) => r.inboundType === 2)) {
ElMessage.error('客户定制商品,不能入库!')
return
}
handleEmbryoInStockByCard()
} else {
handleEmbryoInFastStock(1)
}
}
onMounted(() => {
void loadAllDictionaries()
void loadOrderStatusCounts()
search()
})
</script>
<style scoped lang="scss">
.order-status {
width: 180px;
}
.order-status-item {
font-size: 14px;
cursor: pointer;
padding-left: 10px;
height: 30px;
line-height: 30px;
color: #333;
font-weight: 500;
&:hover {
background-color: #f5f7fa;
}
&.order-status-item-active {
background-color: #f5f7fa;
color: #409eff;
}
}
.card-content {
flex: 1;
margin-top: 10px;
overflow: hidden;
}
.header-filter-form {
width: 100%;
min-width: 0;
}
.search-form {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0;
min-width: 0;
:deep(.el-form-item) {
margin-right: 10px;
margin-bottom: 10px;
}
:deep(.el-form-item__content) {
min-width: 0;
}
}
</style>
...@@ -85,7 +85,7 @@ import { ...@@ -85,7 +85,7 @@ import {
} from '@/api/logistics' } from '@/api/logistics'
import { createLogisticsOrdersApi } from '@/api/podCnOrder' import { createLogisticsOrdersApi } from '@/api/podCnOrder'
import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew/factoryOrderNew' import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew'
const createLogisticDialogVisible = ref(false) const createLogisticDialogVisible = ref(false)
const props = defineProps<{ const props = defineProps<{
......
<template> <template>
<el-dialog <el-dialog
v-model="dialogVisible" v-model="dialogVisible"
:title="fastKey === 'detail' ? '查看详情' : '快捷生产'" :title="title"
top="140px" top="140px"
:fullscreen="true" :fullscreen="true"
:close-on-click-modal="false" :close-on-click-modal="false"
...@@ -44,7 +44,10 @@ ...@@ -44,7 +44,10 @@
</div> </div>
</div> </div>
<div class="right"> <div class="right">
<div v-if="fastKey === 'fastProduction'" class="input"> <div
v-if="['fastProduction', 'fastReceipt'].includes(fastKey)"
class="input"
>
<el-input <el-input
ref="trackingNumberRef" ref="trackingNumberRef"
v-model="TrackingNumber" v-model="TrackingNumber"
...@@ -60,20 +63,35 @@ ...@@ -60,20 +63,35 @@
<div class="div-text"> <div class="div-text">
<div class="div-content"> <div class="div-content">
<div :title="String(detail?.userMark)" class="div-item"> <div :title="String(detail?.userMark)" class="div-item">
<span style="font-size: 18px">客户</span> <span class="div-item-label" style="font-size: 18px"
<p style="color: red; font-size: 30px"> >客户:</span
>
<div class="div-item-value" style="color: red; font-size: 30px">
{{ detail?.userMark }} {{ detail?.userMark }}
</p> </div>
</div> </div>
<div <div
:title="String(detail?.factoryOrderNumber)" :title="String(detail?.factoryOrderNumber)"
class="div-item" class="div-item"
style="margin-top: 14px"
> >
<span style="font-size: 18px">订单号</span> <span class="div-item-label" style="font-size: 18px"
<p style="color: red; font-size: 22px"> >订单号:</span
>
<div class="div-item-value" style="color: red; font-size: 22px">
{{ detail?.factoryOrderNumber }} {{ detail?.factoryOrderNumber }}
</p> </div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail?.shopNumber)"
class="div-item"
>
<span class="div-item-label" style="font-size: 18px"
>店铺单号:</span
>
<div class="div-item-value" style="color: red; font-size: 22px">
{{ detail?.shopNumber }}
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -86,77 +104,132 @@ ...@@ -86,77 +104,132 @@
:title="String(operationNoDisplay)" :title="String(operationNoDisplay)"
class="div-item" class="div-item"
> >
<span>操作单号</span> <span class="div-item-label">操作单号:</span>
<p>{{ operationNoDisplay }}</p> <div class="div-item-value">{{ operationNoDisplay }}</div>
</div> </div>
<div <div
v-else v-else
:title="detail?.factorySubOrderNumber" :title="detail?.factorySubOrderNumber"
class="div-item" class="div-item"
> >
<span>生产单号</span> <span class="div-item-label">生产单号:</span>
<p> <div class="div-item-value">
{{ detail?.factorySubOrderNumber }} {{ detail?.factorySubOrderNumber }}
</p> </div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="setProductMark(String(detail.productMark))"
class="div-item"
>
<span class="div-item-label">类型:</span>
<div
class="div-item-value"
style="font-weight: bold; color: red"
>
{{ setProductMark(String(detail.productMark)) }}
</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail.productName) || ''"
class="div-item"
>
<span class="div-item-label">商品名称:</span>
<div class="div-item-value">{{ detail.productName || '' }}</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail.statusName || '')"
class="div-item"
>
<span class="div-item-label">挂起前状态:</span>
<div class="div-item-value">
<ElTag v-if="detail.statusName" type="primary">{{ detail.statusName || '' }}</ElTag
>
</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail.thirdSkuCode)"
class="div-item"
>
<span class="div-item-label">库存SKU:</span>
<div class="div-item-value">{{ detail.thirdSkuCode }}</div>
</div> </div>
<div <div
v-if="!showOperationNoRow" v-if="!showOperationNoRow"
:title="detail?.thirdSubOrderNumber || ''" :title="detail?.thirdSubOrderNumber || ''"
class="div-item" class="div-item"
> >
<span>第三方生产单号</span> <span class="div-item-label">第三方生产单号:</span>
<p> <div class="div-item-value">
{{ detail?.thirdSubOrderNumber }} {{ detail?.thirdSubOrderNumber }}
</p> </div>
</div> </div>
<div :title="String(detail?.craftName)" class="div-item"> <div :title="String(detail?.craftName)" class="div-item">
<span>生产工艺</span> <span class="div-item-label">生产工艺:</span>
<p> <div class="div-item-value">
{{ detail?.craftName }} {{ detail?.craftName }}
</p> </div>
</div> </div>
<div :title="detail?.baseSku" class="div-item"> <div
<span>基版</span> v-if="fastKey !== 'fastReceipt'"
<p>{{ detail?.baseSku }}</p> :title="detail?.baseSku"
class="div-item"
>
<span class="div-item-label">基版:</span>
<div class="div-item-value">{{ detail?.baseSku }}</div>
</div> </div>
<div :title="detail?.variantSku" class="div-item"> <div :title="detail?.variantSku" class="div-item">
<span>变体SKU</span> <span class="div-item-label">变体SKU:</span>
<p>{{ detail?.variantSku }}</p> <div class="div-item-value">{{ detail?.variantSku }}</div>
</div> </div>
<div <div
v-if="fastKey !== 'fastReceipt'"
:title=" :title="
String(showOperationNoRow ? detail?.quantity : detail?.num) String(showOperationNoRow ? detail?.quantity : detail?.num)
" "
class="div-item" class="div-item"
> >
<span>数量</span> <span class="div-item-label">数量:</span>
<p>{{ showOperationNoRow ? detail?.quantity : detail?.num }}</p> <div class="div-item-value">
{{ showOperationNoRow ? detail?.quantity : detail?.num }}
</div>
</div> </div>
<div :title="String(detail?.size)" class="div-item"> <div
<span>尺寸</span> v-if="fastKey !== 'fastReceipt'"
<p>{{ detail?.size }}</p> :title="String(detail?.size)"
class="div-item"
>
<span class="div-item-label">尺寸:</span>
<div class="div-item-value">{{ detail?.size }}</div>
</div> </div>
<div <div
v-if="detail?.sizeType" v-if="detail?.sizeType && fastKey !== 'fastReceipt'"
:title="String(detail?.sizeType)" :title="String(detail?.sizeType)"
class="div-item" class="div-item"
> >
<span>尺码类型</span> <span class="div-item-label">尺码类型:</span>
<p> <div class="div-item-value">
{{ sizeList.find((i) => i.value === detail.sizeType)?.name }} {{ sizeList.find((i) => i.value === detail.sizeType)?.name }}
</p> </div>
</div> </div>
<div :title="detail?.shopNumber ?? ''" class="div-item"> <div
<span>店铺单号</span> v-if="fastKey !== 'fastReceipt'"
<p>{{ detail?.shopNumber ?? '' }}</p> :title="detail?.shopNumber ?? ''"
class="div-item"
>
<span class="div-item-label">店铺单号:</span>
<div class="div-item-value">{{ detail?.shopNumber ?? '' }}</div>
</div> </div>
<div :title="detail?.createTime" class="div-item"> <div :title="detail?.createTime" class="div-item">
<span>创建时间</span> <span class="div-item-label">创建时间:</span>
<p>{{ detail?.createTime }}</p> <div class="div-item-value">{{ detail?.createTime }}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -164,7 +237,10 @@ ...@@ -164,7 +237,10 @@
<div class="btn"> <div class="btn">
<div <div
:style="{ :style="{
visibility: fastKey === 'fastProduction' ? 'visible' : 'hidden', visibility:
fastKey === 'fastProduction' || fastKey === 'fastReceipt'
? 'visible'
: 'hidden',
}" }"
class="btn-sure" class="btn-sure"
> >
...@@ -174,13 +250,13 @@ ...@@ -174,13 +250,13 @@
type="success" type="success"
@click="changeStatus" @click="changeStatus"
> >
生产完成 {{ fastKey === 'fastProduction' ? '生产完成' : '快捷入库' }}
</el-button> </el-button>
<div class="check"> <div class="check">
<el-checkbox v-model="isAutoSure"> 自动完成上一单 </el-checkbox> <el-checkbox v-model="isAutoSure"> 自动完成上一单 </el-checkbox>
</div> </div>
</div> </div>
<div class="btn-down"> <div v-if="fastKey !== 'fastReceipt'" class="btn-down">
<div class="check"> <div class="check">
<el-checkbox v-model="isDownloadImage" size="large"> <el-checkbox v-model="isDownloadImage" size="large">
扫码下载素材 扫码下载素材
...@@ -243,12 +319,21 @@ interface HistoryDataItem { ...@@ -243,12 +319,21 @@ interface HistoryDataItem {
orderNumber: string orderNumber: string
finished: boolean finished: boolean
} }
const title = computed(() => {
if (props.fastKey === 'fastProduction') {
return '快捷生产'
} else if (props.fastKey === 'fastReceipt') {
return '快捷入库'
} else {
return '查看详情'
}
})
export type FastProductionDetail = PodOrderRes & { export type FastProductionDetail = PodOrderRes & {
operationNo?: string | null operationNo?: string | null
quantity?: number | null quantity?: number | null
podOrderNo?: string | null podOrderNo?: string | null
podOrderProductId?: number | null podOrderProductId?: number | null
statusName?: string | null
} }
const trackingNumberRef = ref() const trackingNumberRef = ref()
const historyData = ref<HistoryDataItem[]>([]) const historyData = ref<HistoryDataItem[]>([])
...@@ -422,7 +507,10 @@ watch( ...@@ -422,7 +507,10 @@ watch(
historyData.value = history ? JSON.parse(history) : [] historyData.value = history ? JSON.parse(history) : []
const len = historyData.value const len = historyData.value
if (len.length > 0 && props.fastKey === 'fastProduction') { if (
len.length > 0 &&
(props.fastKey === 'fastProduction' || props.fastKey === 'fastReceipt')
) {
confirmQuery(len, 0) confirmQuery(len, 0)
} }
placeholderText.value = props.trackingPlaceholder placeholderText.value = props.trackingPlaceholder
...@@ -452,7 +540,9 @@ watch( ...@@ -452,7 +540,9 @@ watch(
const confirmQuery = (len: HistoryDataItem[], i: number) => { const confirmQuery = (len: HistoryDataItem[], i: number) => {
const el = len[i] const el = len[i]
showConfirm( showConfirm(
`${props.pendingOrderLabel} ${el.orderNumber} 未生产完成,取消则不提醒?`, `${props.pendingOrderLabel} ${el.orderNumber} ${
props.fastKey === 'fastProduction' ? '未生产' : '未入库'
}完成,取消则不提醒?`,
{ {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
...@@ -562,7 +652,7 @@ const download = async () => { ...@@ -562,7 +652,7 @@ const download = async () => {
if (detail.value && detail.value?.id != -1) { if (detail.value && detail.value?.id != -1) {
try { try {
const id = props.isNewOrder const id = props.isNewOrder
? detail.value.podOrderProductId as number ? (detail.value.podOrderProductId as number)
: detail.value.id : detail.value.id
if (id !== undefined) { if (id !== undefined) {
try { try {
...@@ -668,6 +758,12 @@ const trackCodeInput = async () => { ...@@ -668,6 +758,12 @@ const trackCodeInput = async () => {
const onOpened = () => { const onOpened = () => {
trackingNumberRef.value && trackingNumberRef.value.focus() trackingNumberRef.value && trackingNumberRef.value.focus()
} }
function setProductMark(productMark: string) {
if (!productMark) return ''
if (productMark === 'custom_normal') return 'CB'
if (productMark === 'normal') return 'G'
return ''
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.sure-btn { .sure-btn {
...@@ -784,27 +880,27 @@ const onOpened = () => { ...@@ -784,27 +880,27 @@ const onOpened = () => {
align-items: flex-start; align-items: flex-start;
padding: 15px 10px; padding: 15px 10px;
box-sizing: border-box; box-sizing: border-box;
position: relative;
border: 1px solid #ececec;
.div-item { .div-item {
width: 50%; width: 50%;
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
height: 36px;
line-height: 36px;
p { .div-item-label {
font-weight: 400;
color: black;
}
span {
display: inline-block; display: inline-block;
text-align: right; text-align: right;
width: 120px; width: 120px;
} }
.div-item-value {
span::after { flex: 1;
content: ':'; text-overflow: ellipsis;
margin: 0 3px; overflow: hidden;
white-space: nowrap;
} }
} }
} }
...@@ -819,11 +915,6 @@ const onOpened = () => { ...@@ -819,11 +915,6 @@ const onOpened = () => {
color: black; color: black;
z-index: 3; z-index: 3;
} }
.div-content {
position: relative;
border: 1px solid #ececec;
}
} }
.input { .input {
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { updateCustomDeclarationInfoApi } from '@/api/podCnOrder' import { updateCustomDeclarationInfoApi } from '@/api/podCnOrder'
import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew/factoryOrderNew' import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew'
import { import {
CustomDeclarationInfoForm, CustomDeclarationInfoForm,
PodCnOrderListData, PodCnOrderListData,
......
...@@ -196,7 +196,7 @@ import { ...@@ -196,7 +196,7 @@ import {
printPickPdfByBatchNumberApi, printPickPdfByBatchNumberApi,
printProductionPdfByBatchNumberApi, printProductionPdfByBatchNumberApi,
} from '@/api/factoryOrderNew' } from '@/api/factoryOrderNew'
import type { BatchManageData } from '@/types/api/factoryOrderNew/factoryOrderNew' import type { BatchManageData } from '@/types/api/factoryOrderNew'
import type { PaginationData } from '@/types/api' import type { PaginationData } from '@/types/api'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import ArrangeDialog from './ArrangeDialog.vue' import ArrangeDialog from './ArrangeDialog.vue'
......
...@@ -293,19 +293,32 @@ import { ...@@ -293,19 +293,32 @@ import {
getFactoryOrderNewLogApi, getFactoryOrderNewLogApi,
} from '@/api/factoryOrderNew' } from '@/api/factoryOrderNew'
import type { PaginationData } from '@/types/api' import type { PaginationData } from '@/types/api'
import type { CardLayoutListFetcher } from '@/types/api/factoryOrderNew'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { operateOrderListData } from '@/types/api/factoryOrderNew/factoryOrderNew' import { operateOrderListData } from '@/types/api/factoryOrderNew'
import type { LogListData } from '@/types/api/order' import type { LogListData } from '@/types/api/order'
import LogList from '@/components/LogList.vue' import LogList from '@/components/LogList.vue'
import RightClickMenu from '@/components/RightClickMenu.vue' import RightClickMenu from '@/components/RightClickMenu.vue'
import platformJson from '../../../../json/platform.json' import platformJson from '../../../../json/platform.json'
const props = defineProps<{ const props = defineProps<{
status: string status?: string
queryPayload: Record<string, unknown> queryPayload?: Record<string, unknown>
initPageSize?: number initPageSize?: number
fetchList?: CardLayoutListFetcher
}>() }>()
const buildListPayload = () => {
const payload = { ...props.queryPayload }
if (props.fetchList || props.status === undefined) {
return payload
}
return {
...payload,
status: props.status === 'ALL' ? undefined : props.status,
}
}
const emit = defineEmits<{ const emit = defineEmits<{
'selection-change': [items: operateOrderListData[]] 'selection-change': [items: operateOrderListData[]]
'view-detail': [item: operateOrderListData] 'view-detail': [item: operateOrderListData]
...@@ -324,9 +337,9 @@ const { ...@@ -324,9 +337,9 @@ const {
initPageSize: props.initPageSize || 100, initPageSize: props.initPageSize || 100,
initLoad: false, initLoad: false,
query: async (current, size) => { query: async (current, size) => {
const payload = { const payload = buildListPayload()
...props.queryPayload, if (props.fetchList) {
status: props.status === 'ALL' ? undefined : props.status, return props.fetchList(payload, current, size)
} }
const res = await getCardLayoutListApi(payload, current, size) const res = await getCardLayoutListApi(payload, current, size)
return res.data as unknown as PaginationData<operateOrderListData> return res.data as unknown as PaginationData<operateOrderListData>
...@@ -411,13 +424,20 @@ const rightChange = (code: string) => { ...@@ -411,13 +424,20 @@ const rightChange = (code: string) => {
const openLogDialog = async (item: operateOrderListData) => { const openLogDialog = async (item: operateOrderListData) => {
if (!item.podOrderId) return if (!item.podOrderId) return
const loading = ElLoading.service({
fullscreen: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try { try {
const res = await getFactoryOrderNewLogApi(item.podOrderId as number) const res = await getFactoryOrderNewLogApi(item.podOrderId as number)
logList.value = (res.data || []) as unknown as LogListData[] logList.value = (res.data || []) as unknown as LogListData[]
logVisible.value = true logVisible.value = true
} catch (e) { } catch (e) {
// ignore // ignore
} } finally {
loading.close()
}
} }
const clearSelection = () => { const clearSelection = () => {
......
...@@ -257,7 +257,7 @@ import platformJson from '../../../../json/platform.json' ...@@ -257,7 +257,7 @@ import platformJson from '../../../../json/platform.json'
import type { import type {
operateOrderListData, operateOrderListData,
PickCompleteData, PickCompleteData,
} from '@/types/api/factoryOrderNew/factoryOrderNew' } from '@/types/api/factoryOrderNew'
const emit = defineEmits(['adjustPickOrderSuccess']) const emit = defineEmits(['adjustPickOrderSuccess'])
const props = defineProps<{ const props = defineProps<{
......
...@@ -123,7 +123,7 @@ import { ...@@ -123,7 +123,7 @@ import {
replenishmentCompleteApi, replenishmentCompleteApi,
markStockOutOfApi, markStockOutOfApi,
} from '@/api/factoryOrderNew' } from '@/api/factoryOrderNew'
import type { PickCompleteData } from '@/types/api/factoryOrderNew/factoryOrderNew' import type { PickCompleteData } from '@/types/api/factoryOrderNew'
import type { BaseRespData } from '@/types/api' import type { BaseRespData } from '@/types/api'
import type { import type {
InterProductList, InterProductList,
......
...@@ -71,7 +71,7 @@ import { ref } from 'vue' ...@@ -71,7 +71,7 @@ import { ref } from 'vue'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus' import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { pickCompleteByIdsDataApi } from '@/api/factoryOrderNew' import { pickCompleteByIdsDataApi } from '@/api/factoryOrderNew'
import type { PickCompleteData } from '@/types/api/factoryOrderNew/factoryOrderNew' import type { PickCompleteData } from '@/types/api/factoryOrderNew'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import _ from 'lodash' import _ from 'lodash'
import CreateOutboundDialog from './CreateOutboundDialog.vue' import CreateOutboundDialog from './CreateOutboundDialog.vue'
......
...@@ -63,7 +63,7 @@ const formRef = ref<FormInstance>() ...@@ -63,7 +63,7 @@ const formRef = ref<FormInstance>()
const orderIds = ref<(number | string)[]>([]) const orderIds = ref<(number | string)[]>([])
const suspendReasons = [ const suspendReasons = [
{ label: '客户拦截', value: 1 }, // { label: '客户拦截', value: 1 },
{ label: '地址异常', value: 2 }, { label: '地址异常', value: 2 },
{ label: '素材异常', value: 3 }, { label: '素材异常', value: 3 },
{ label: '其他', value: 4 }, { label: '其他', value: 4 },
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
import { reactive } from 'vue' import { reactive } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getRestockListApi, restockCheckApi } from '@/api/factoryOrderNew' import { getRestockListApi, restockCheckApi } from '@/api/factoryOrderNew'
import type { RestockData } from '@/types/api/factoryOrderNew/factoryOrderNew' import type { RestockData } from '@/types/api/factoryOrderNew'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
......
...@@ -50,8 +50,11 @@ export function useOrderBatchActions(options: UseOrderBatchActionsOptions) { ...@@ -50,8 +50,11 @@ export function useOrderBatchActions(options: UseOrderBatchActionsOptions) {
const res = await action.api(ids) const res = await action.api(ids)
if (res.code !== 200) return if (res.code !== 200) return
if (action.successText) ElMessage.success(action.successText) if (action.successText) ElMessage.success(action.successText)
if (action.onSuccess) await action.onSuccess(res) if (action.onSuccess) {
await refreshCurrentView({ isRefreshTree: !!action.refreshTree }) await action.onSuccess(res)
} else {
await refreshCurrentView({ isRefreshTree: !!action.refreshTree })
}
if (action.onAfter) await action.onAfter() if (action.onAfter) await action.onAfter()
} catch (e) { } catch (e) {
console.error(e) console.error(e)
......
...@@ -141,8 +141,6 @@ export function useOrderDictionaries() { ...@@ -141,8 +141,6 @@ export function useOrderDictionaries() {
getUserMark(), getUserMark(),
getCustomTagList(), getCustomTagList(),
getLogisticsCompanyAllCodelist(), getLogisticsCompanyAllCodelist(),
getReceiverCountryList(),
loadWarehouseList(),
]) ])
} }
......
...@@ -5,8 +5,9 @@ import { ...@@ -5,8 +5,9 @@ import {
getFactoryOrderNewDetailApi, getFactoryOrderNewDetailApi,
getFactoryOrderNewListApi, getFactoryOrderNewListApi,
getFactoryOrderNewLogApi, getFactoryOrderNewLogApi,
getOperationOrderByIdApi,
} from '@/api/factoryOrderNew' } from '@/api/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew/factoryOrderNew' import type { SearchForm } from '@/types/api/factoryOrderNew'
import type { import type {
FactoryOrderNewListData, FactoryOrderNewListData,
LogListData, LogListData,
...@@ -23,6 +24,7 @@ interface UseOrderListAndDetailOptions { ...@@ -23,6 +24,7 @@ interface UseOrderListAndDetailOptions {
getQueryPayload: () => Record<string, unknown> getQueryPayload: () => Record<string, unknown>
getListPageAcceptedSubStatus: () => number | undefined getListPageAcceptedSubStatus: () => number | undefined
suspendedSubTab: Ref<number> suspendedSubTab: Ref<number>
cancelledSubTab: Ref<number>
} }
export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
...@@ -34,11 +36,13 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -34,11 +36,13 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
getQueryPayload, getQueryPayload,
getListPageAcceptedSubStatus, getListPageAcceptedSubStatus,
suspendedSubTab, suspendedSubTab,
cancelledSubTab,
} = options } = options
const subLoading = ref(false) const subLoading = ref(false)
const activeTab = ref<'product' | 'log'>('product') const activeTab = ref<'product' | 'operation' | 'log'>('product')
const productList = ref<ProductListData[]>([]) const productList = ref<ProductListData[]>([])
const operationOrderList = ref<operateOrderListData[]>([])
const logList = ref<LogListData[]>([]) const logList = ref<LogListData[]>([])
const tableRef = ref() const tableRef = ref()
const currentRow = ref<FactoryOrderNewListData | null>(null) const currentRow = ref<FactoryOrderNewListData | null>(null)
...@@ -73,6 +77,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -73,6 +77,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
} = usePageList<FactoryOrderNewListData>({ } = usePageList<FactoryOrderNewListData>({
query: (page, size) => { query: (page, size) => {
const isSuspendTree = treeStatus.value === 'SUSPEND' const isSuspendTree = treeStatus.value === 'SUSPEND'
const isCancelled = status.value === 'CANCELLED'
return getFactoryOrderNewListApi( return getFactoryOrderNewListApi(
buildListQueryBody(), buildListQueryBody(),
page, page,
...@@ -80,6 +85,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -80,6 +85,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
status.value === 'ALL' ? undefined : status.value, status.value === 'ALL' ? undefined : status.value,
getListPageAcceptedSubStatus(), getListPageAcceptedSubStatus(),
isSuspendTree ? suspendedSubTab.value : undefined, isSuspendTree ? suspendedSubTab.value : undefined,
isCancelled ? cancelledSubTab.value : undefined,
).then(async (res) => { ).then(async (res) => {
const records = res.data.records || [] const records = res.data.records || []
await nextTick(() => { await nextTick(() => {
...@@ -101,6 +107,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -101,6 +107,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
selectedRows.value = [] selectedRows.value = []
cardSelectList.value = [] cardSelectList.value = []
productList.value = [] productList.value = []
operationOrderList.value = []
logList.value = [] logList.value = []
currentRow.value = null currentRow.value = null
listSortProp.value = null listSortProp.value = null
...@@ -160,9 +167,12 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -160,9 +167,12 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
cardSelectList.value = items cardSelectList.value = items
} }
const getOrderDetailsById = async (tabName?: 'product' | 'log') => { const getOrderDetailsById = async (
tabName?: 'product' | 'operation' | 'log',
) => {
if (!currentRow.value) { if (!currentRow.value) {
productList.value = [] productList.value = []
operationOrderList.value = []
logList.value = [] logList.value = []
return return
} }
...@@ -181,6 +191,11 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -181,6 +191,11 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
productList.value = Array.isArray(productRes.data) productList.value = Array.isArray(productRes.data)
? productRes.data ? productRes.data
: [] : []
} else if (effectiveTab === 'operation') {
operationOrderList.value = []
const opRes = await getOperationOrderByIdApi(id)
if (opRes.code !== 200) return
operationOrderList.value = opRes.data
} else { } else {
logList.value = [] logList.value = []
// const logRes = isSuspend // const logRes = isSuspend
...@@ -200,6 +215,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -200,6 +215,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
watch(currentRow, (row) => { watch(currentRow, (row) => {
if (!row) { if (!row) {
productList.value = [] productList.value = []
operationOrderList.value = []
logList.value = [] logList.value = []
return return
} }
...@@ -217,7 +233,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -217,7 +233,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
const handleTabClick = (tab: TabsPaneContext) => { const handleTabClick = (tab: TabsPaneContext) => {
if (!currentRow.value) return if (!currentRow.value) return
const name = tab?.props?.name as 'product' | 'log' | undefined const name = tab?.props?.name as 'product' | 'operation' | 'log' | undefined
void getOrderDetailsById(name) void getOrderDetailsById(name)
} }
...@@ -225,6 +241,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) { ...@@ -225,6 +241,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
subLoading, subLoading,
activeTab, activeTab,
productList, productList,
operationOrderList,
logList, logList,
tableRef, tableRef,
currentRow, currentRow,
......
import { computed, ref, nextTick } from 'vue' import { computed, ref, nextTick } from 'vue'
import { import {
getCancelledOrderStatisticsApi,
getPodOrderAcceptedStatisticsApi, getPodOrderAcceptedStatisticsApi,
getPodOrderStateGroupListApi, getPodOrderStateGroupListApi,
getSuspendStatisticsApi, getSuspendStatisticsApi,
} from '@/api/factoryOrderNew' } from '@/api/factoryOrderNew'
import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew' import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew'
interface UseOrderStatusTreeOptions { interface UseOrderStatusTreeOptions {
getQueryPayload: () => Record<string, unknown> getQueryPayload: () => Record<string, unknown>
currentPage: { value: number } currentPage: { value: number }
pageSize: { value: number } pageSize: { value: number }
onClearTableState: () => void
onRefreshCurrentView: () => void
} }
export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
...@@ -19,8 +19,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -19,8 +19,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
getQueryPayload, getQueryPayload,
currentPage, currentPage,
pageSize, pageSize,
onClearTableState,
onRefreshCurrentView,
} = options } = options
const cardLayoutStatuses = [ const cardLayoutStatuses = [
...@@ -46,12 +44,19 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -46,12 +44,19 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
acceptedOutOfStockCount: 0, acceptedOutOfStockCount: 0,
}) })
const suspendedTabs = ref([ const suspendedTabs = ref([
{ label: '客户拦截', key: 'customerInterceptCount', value: 1, count: 0 }, { label: '客户拦截-取消订单', key: 'customerInterceptCount', value: 1, count: 0 },
{ label: '地址异常', key: 'addressExceptionCount', value: 2, count: 0 }, { label: '地址异常', key: 'addressExceptionCount', value: 2, count: 0 },
{ label: '素材异常', key: 'materialExceptionCount', value: 3, count: 0 }, { label: '素材异常', key: 'materialExceptionCount', value: 3, count: 0 },
{ label: '其他', key: 'otherReasonCount', value: 4, count: 0 }, { label: '其他', key: 'otherReasonCount', value: 4, count: 0 },
]) ])
const suspendedSubTab = ref(1) const suspendedSubTab = ref(1)
/** 已取消:子 tab 与 list_page 的 cancelSubType 取值 1/2/3 对应 */
const cancelledTabs = ref([
{ label: '客户拦截', key: 'customerInterceptCount', value: 1, count: 0 },
{ label: '协商取消', key: 'mutualAgreeCancelCount', value: 2, count: 0 },
{ label: '其他', key: 'otherReasonCount', value: 3, count: 0 },
])
const cancelledSubTab = ref(1)
const treeRef = ref() const treeRef = ref()
const isCardLayout = computed(() => cardLayoutStatuses.includes(status.value)) const isCardLayout = computed(() => cardLayoutStatuses.includes(status.value))
...@@ -107,6 +112,25 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -107,6 +112,25 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
} }
} }
const getCancelledCounts = async () => {
try {
const res = await getCancelledOrderStatisticsApi(
getQueryPayload() as SearchForm,
currentPage.value,
pageSize.value,
cancelledSubTab.value,
)
if (res.code !== 200 || !res.data) return
const data = res.data || {}
cancelledTabs.value = cancelledTabs.value.map((tab) => ({
...tab,
count: data[String(tab.key)] ?? 0,
}))
} catch (e) {
console.error(e)
}
}
const loadStatusTreeCounts = async () => { const loadStatusTreeCounts = async () => {
try { try {
const res = await getPodOrderStateGroupListApi() const res = await getPodOrderStateGroupListApi()
...@@ -121,22 +145,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -121,22 +145,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
} }
} }
const handleStatusNodeClick = (node: StatusTreeNode) => {
status.value = node.status
if (node.status !== 'PENDING_RECEIVE') {
pendingAcceptSubTab.value = 'PENDING_RECEIVE'
}
if (node.status !== 'SUSPEND') {
suspendedSubTab.value = 1
}
onClearTableState()
if (!isSpecialLayout.value) {
onRefreshCurrentView()
}
if (node.status === 'SUSPEND') {
void getSuspendCounts()
}
}
const handlePendingAcceptTabClick = ( const handlePendingAcceptTabClick = (
tab: 'PENDING_RECEIVE' | 'ACCEPT_FAIL_OUT_OF_STOCK', tab: 'PENDING_RECEIVE' | 'ACCEPT_FAIL_OUT_OF_STOCK',
...@@ -160,6 +168,8 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -160,6 +168,8 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
pendingAcceptCounts, pendingAcceptCounts,
suspendedTabs, suspendedTabs,
suspendedSubTab, suspendedSubTab,
cancelledTabs,
cancelledSubTab,
treeRef, treeRef,
isCardLayout, isCardLayout,
isSpecialLayout, isSpecialLayout,
...@@ -167,9 +177,9 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -167,9 +177,9 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
getListPageAcceptedSubStatus, getListPageAcceptedSubStatus,
getPendingReceiveCounts, getPendingReceiveCounts,
getSuspendCounts, getSuspendCounts,
getCancelledCounts,
loadStatusTreeCounts, loadStatusTreeCounts,
handleStatusNodeClick,
handlePendingAcceptTabClick, handlePendingAcceptTabClick,
toggleExpand, toggleExpand
} }
} }
...@@ -404,14 +404,27 @@ ...@@ -404,14 +404,27 @@
</ElButton> </ElButton>
</span> </span>
<span <span
v-if="status === 'PENDING_RECEIVE' || status === 'SUSPEND'" v-if="
status === 'PENDING_RECEIVE' ||
(status === 'SUSPEND' && suspendedSubTab !== 1)
"
class="item" class="item"
> >
<ElButton type="danger" @click="handleCancelOrder">取消订单</ElButton> <ElButton type="danger" @click="handleCancelOrder">取消订单</ElButton>
</span> </span>
<ElDropdown <ElDropdown
v-if="status === 'PENDING_CREATE_LOGISTICS'" v-if="
[
'ALL',
'PENDING_RECEIVE',
'PENDING_CREATE_LOGISTICS',
'PENDING_SCHEDULE',
'PICKING',
'PENDING_DELIVERY',
'SUSPEND',
].includes(status)
"
trigger="click" trigger="click"
@command="handleLogisticsCommand" @command="handleLogisticsCommand"
> >
...@@ -584,11 +597,21 @@ ...@@ -584,11 +597,21 @@
</template> </template>
<!-- 挂起 --> <!-- 挂起 -->
<template v-if="status === 'SUSPEND'"> <template v-if="status === 'SUSPEND'">
<span class="item"> <span v-if="suspendedSubTab !== 1" class="item">
<ElButton type="warning" @click="() => handleCancelSuspend()" <ElButton type="warning" @click="() => handleCancelSuspend()"
>取消挂起</ElButton >取消挂起</ElButton
> >
</span> </span>
<span v-if="suspendedSubTab === 1" class="item">
<ElButton type="success" @click="() => handleInterceptionSuccess()"
>拦截成功</ElButton
>
</span>
<span v-if="suspendedSubTab === 1" class="item">
<ElButton type="danger" @click="() => handleInterceptionFail()"
>拦截失败</ElButton
>
</span>
<span class="item"> <span class="item">
<ElButton type="primary" @click="handleSyncAddress" <ElButton type="primary" @click="handleSyncAddress"
>同步收货地址</ElButton >同步收货地址</ElButton
...@@ -659,6 +682,18 @@ ...@@ -659,6 +682,18 @@
</div> </div>
</div> </div>
<!-- <div v-if="status === 'CANCELLED'" class="status-subtabs">
<div
v-for="tab in cancelledTabs"
:key="tab.value"
class="status-subtab"
:class="{ active: cancelledSubTab === tab.value }"
@click="handleCancelledTabClick(tab.value)"
>
{{ tab.label }}({{ tab.count || 0 }})
</div>
</div> -->
<div v-if="status === 'PENDING_RECEIVE'" class="status-subtabs"> <div v-if="status === 'PENDING_RECEIVE'" class="status-subtabs">
<div <div
class="status-subtab" class="status-subtab"
...@@ -735,7 +770,10 @@ ...@@ -735,7 +770,10 @@
}} }}
</ElButton> </ElButton>
<ElButton <ElButton
v-if="showPendingLogisticsUpdateAddress(row)" v-if="
showPendingLogisticsUpdateAddress() &&
row.shipmentType === 1
"
type="primary" type="primary"
link link
size="small" size="small"
...@@ -744,16 +782,28 @@ ...@@ -744,16 +782,28 @@
修改地址 修改地址
</ElButton> </ElButton>
<ElButton <ElButton
v-if="status === 'PENDING_CREATE_LOGISTICS'" v-if="showChangeLogistics(row)"
type="primary" type="warning"
link
size="small"
@click.stop="handleChangeLogistics(row)"
>
更换物流方式
</ElButton>
<ElButton
v-if="showPendingLogisticsUpdateAddress()"
type="success"
link link
size="small" size="small"
@click.stop="handleRefreshReceiverAddress(row)" @click.stop="handleRefreshReceiverAddress(row)"
> >
刷新地址 {{
row.shipmentType === 0 ? '同步物流/地址' : '同步地址'
}}
</ElButton> </ElButton>
<ElButton <ElButton
v-if="status === 'SUSPEND'" v-if="status === 'SUSPEND' && suspendedSubTab !== 1"
type="primary" type="primary"
link link
size="small" size="small"
...@@ -761,6 +811,24 @@ ...@@ -761,6 +811,24 @@
> >
取消挂起 取消挂起
</ElButton> </ElButton>
<ElButton
v-if="status === 'SUSPEND' && suspendedSubTab === 1"
type="primary"
link
size="small"
@click.stop="handleInterceptionSuccess(row)"
>
拦截成功
</ElButton>
<ElButton
v-if="status === 'SUSPEND' && suspendedSubTab === 1"
type="primary"
link
size="small"
@click.stop="handleInterceptionFail(row)"
>
拦截失败
</ElButton>
</div> </div>
</template> </template>
</TableView> </TableView>
...@@ -803,6 +871,19 @@ ...@@ -803,6 +871,19 @@
/> />
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane name="operation" label="操作单">
<div
v-loading="subLoading"
element-loading-text="加载中..."
class="sub-table-wrapper"
>
<TableView
:paginated-data="operationOrderList"
:columns="operationOrderColumns"
serial-numberable
/>
</div>
</el-tab-pane>
<el-tab-pane name="log" label="操作日志"> <el-tab-pane name="log" label="操作日志">
<div <div
v-loading="subLoading" v-loading="subLoading"
...@@ -934,6 +1015,60 @@ ...@@ -934,6 +1015,60 @@
:submit-address-api="submitFactoryOrderReceiverAddress" :submit-address-api="submitFactoryOrderReceiverAddress"
@success="() => refreshCurrentView({ isRefreshTree: true })" @success="() => refreshCurrentView({ isRefreshTree: true })"
/> />
<ChangeWayDialog
ref="changeWayDialogRef"
:row-data="changeWayCurrentItem"
:get-table-fn="getLogisticsCalculation"
:change-logistics-api="changeLogisticsApi"
@confirm="() => refreshCurrentView({ isRefreshTree: true })"
/>
<ElDialog
v-model="interceptSuccessDialogVisible"
title="拦截成功"
width="480px"
:close-on-click-modal="false"
>
<ElForm ref="interceptSuccessFormRef" :model="interceptSuccessForm">
<ElFormItem
label="拦截成功类型"
prop="suspendSussessType"
:rules="[
{
required: true,
message: '请选择拦截成功类型',
trigger: 'change',
},
]"
>
<ElSelect
v-model="interceptSuccessForm.suspendSussessType"
placeholder="请选择"
style="width: 100%"
clearable
>
<ElOption
v-for="opt in interceptSuccessTypeOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</ElSelect>
</ElFormItem>
</ElForm>
<p style="padding-left: 12px">
拦截成功,订单会流转到已取消,确认是否通过拦截申请?
</p>
<template #footer>
<div class="dialog-footer" style="text-align: center">
<ElButton @click="interceptSuccessDialogVisible = false"
>取消</ElButton
>
<ElButton type="primary" @click="submitInterceptSuccess"
>确定</ElButton
>
</div>
</template>
</ElDialog>
<ElDialog <ElDialog
v-model="exportVisible" v-model="exportVisible"
title="导出选项" title="导出选项"
...@@ -996,6 +1131,8 @@ import { ...@@ -996,6 +1131,8 @@ import {
composingNewPodOrderDesignImages, composingNewPodOrderDesignImages,
printNewPodOrderProductionOrderApi, printNewPodOrderProductionOrderApi,
syncReceiverAddress, syncReceiverAddress,
getLogisticsCalculation,
changeLogisticsApi,
updateReceiverAddressApi, updateReceiverAddressApi,
updateRemarkApi, updateRemarkApi,
completeDeliveryApi, completeDeliveryApi,
...@@ -1008,6 +1145,8 @@ import { ...@@ -1008,6 +1145,8 @@ import {
statusPushApi, statusPushApi,
arrangeFinishApi, arrangeFinishApi,
exportFactoryOrderInfo, exportFactoryOrderInfo,
interceptUpdateApi,
interceptSuccessApi,
} from '@/api/factoryOrderNew' } from '@/api/factoryOrderNew'
import { getLogisticsWayApi } from '@/api/podUsOrder' import { getLogisticsWayApi } from '@/api/podUsOrder'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
...@@ -1034,6 +1173,7 @@ import PodMakeOrder from '@/views/order/podUs/PodMakeOrder.vue' ...@@ -1034,6 +1173,7 @@ import PodMakeOrder from '@/views/order/podUs/PodMakeOrder.vue'
import PodDistributionOrder from '@/views/order/podCN/PodDistributionOrder.vue' import PodDistributionOrder from '@/views/order/podCN/PodDistributionOrder.vue'
import PrintWarehouseSkuTag from '@/views/order/components/printWarehouseSkuTag.vue' import PrintWarehouseSkuTag from '@/views/order/components/printWarehouseSkuTag.vue'
import UpdateAddress from '@/views/order/podCN/components/updateAddress.vue' import UpdateAddress from '@/views/order/podCN/components/updateAddress.vue'
import ChangeWayDialog from '@/views/order/podCN/components/ChangeWayDialog.vue'
import { ResultInfoDataItem } from '@/types/api/order/common' import { ResultInfoDataItem } from '@/types/api/order/common'
import type { FactoryOrderSearchQueryVisibilityContext } from '@/types/api/factoryOrderNew/searchQueryVisibility' import type { FactoryOrderSearchQueryVisibilityContext } from '@/types/api/factoryOrderNew/searchQueryVisibility'
import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew' import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew'
...@@ -1055,6 +1195,17 @@ const exportForm = ref({ ...@@ -1055,6 +1195,17 @@ const exportForm = ref({
resource: '', resource: '',
}) })
const interceptSuccessDialogVisible = ref(false)
const interceptSuccessFormRef = ref()
const interceptSuccessTypeOptions = [
{ label: '生产拦截成功', value: 1 },
{ label: '发货拦截成功', value: 2 },
]
const interceptSuccessForm = ref<{ suspendSussessType: number | undefined }>({
suspendSussessType: undefined,
})
const interceptSuccessRows = ref<FactoryOrderNewListData[]>([])
const { const {
userMarkList, userMarkList,
receiverCountryList, receiverCountryList,
...@@ -1063,6 +1214,8 @@ const { ...@@ -1063,6 +1214,8 @@ const {
craftList, craftList,
warehouseList, warehouseList,
productTypeGroups, productTypeGroups,
getReceiverCountryList,
loadWarehouseList,
loadAllDictionaries, loadAllDictionaries,
} = useOrderDictionaries() } = useOrderDictionaries()
...@@ -1096,6 +1249,8 @@ const { ...@@ -1096,6 +1249,8 @@ const {
pendingAcceptCounts, pendingAcceptCounts,
suspendedTabs, suspendedTabs,
suspendedSubTab, suspendedSubTab,
// cancelledTabs,
cancelledSubTab,
treeRef, treeRef,
isCardLayout, isCardLayout,
isSpecialLayout, isSpecialLayout,
...@@ -1103,6 +1258,7 @@ const { ...@@ -1103,6 +1258,7 @@ const {
getListPageAcceptedSubStatus, getListPageAcceptedSubStatus,
getPendingReceiveCounts, getPendingReceiveCounts,
getSuspendCounts, getSuspendCounts,
// getCancelledCounts,
loadStatusTreeCounts, loadStatusTreeCounts,
handlePendingAcceptTabClick: handlePendingAcceptTabClickRaw, handlePendingAcceptTabClick: handlePendingAcceptTabClickRaw,
toggleExpand, toggleExpand,
...@@ -1110,8 +1266,6 @@ const { ...@@ -1110,8 +1266,6 @@ const {
getQueryPayload, getQueryPayload,
currentPage: statusCurrentPageRef, currentPage: statusCurrentPageRef,
pageSize: statusPageSizeRef, pageSize: statusPageSizeRef,
onClearTableState: () => {},
onRefreshCurrentView: () => {},
}) })
// 查询状态树 // 查询状态树
const statusTreeChildren = computed((): StatusTreeNode[] => { const statusTreeChildren = computed((): StatusTreeNode[] => {
...@@ -1167,6 +1321,7 @@ const { ...@@ -1167,6 +1321,7 @@ const {
subLoading, subLoading,
activeTab, activeTab,
productList, productList,
operationOrderList,
logList, logList,
tableRef, tableRef,
selectedRows, selectedRows,
...@@ -1195,6 +1350,7 @@ const { ...@@ -1195,6 +1350,7 @@ const {
getQueryPayload: getListQueryPayload, getQueryPayload: getListQueryPayload,
getListPageAcceptedSubStatus, getListPageAcceptedSubStatus,
suspendedSubTab, suspendedSubTab,
cancelledSubTab,
}) })
statusCurrentPageRef.value = currentPage.value statusCurrentPageRef.value = currentPage.value
statusPageSizeRef.value = pageSize.value statusPageSizeRef.value = pageSize.value
...@@ -1269,6 +1425,9 @@ const refreshCurrentView = (options?: { isRefreshTree?: boolean }) => { ...@@ -1269,6 +1425,9 @@ const refreshCurrentView = (options?: { isRefreshTree?: boolean }) => {
if (status.value === 'SUSPEND') { if (status.value === 'SUSPEND') {
void getSuspendCounts() void getSuspendCounts()
} }
// if (status.value === 'CANCELLED') {
// void getCancelledCounts()
// }
refreshTableList() refreshTableList()
} }
refreshCurrentViewProxy.value = () => refreshCurrentView() refreshCurrentViewProxy.value = () => refreshCurrentView()
...@@ -1287,12 +1446,29 @@ const receiverAddressForm = ref<AddressInfo>({ ...@@ -1287,12 +1446,29 @@ const receiverAddressForm = ref<AddressInfo>({
receiverPostCode: '', receiverPostCode: '',
}) })
const showPendingLogisticsUpdateAddress = (row: FactoryOrderNewListData) => const showPendingLogisticsUpdateAddress = () =>
status.value === 'PENDING_CREATE_LOGISTICS' && row.shipmentType === 1 status.value === 'ALL' ||
status.value === 'PENDING_CREATE_LOGISTICS' ||
(status.value === 'SUSPEND' && suspendedSubTab.value === 2)
const showChangeLogistics = (row: FactoryOrderNewListData) =>
(status.value === 'ALL' ||
(status.value === 'SUSPEND' && suspendedSubTab.value === 2)) &&
row.shipmentType === 1
const submitFactoryOrderReceiverAddress = (data: AddressInfo) => const submitFactoryOrderReceiverAddress = (data: AddressInfo) =>
updateReceiverAddressApi(data) updateReceiverAddressApi(data)
const changeWayDialogRef = ref<InstanceType<typeof ChangeWayDialog>>()
const changeWayCurrentItem = ref<FactoryOrderNewListData | null>(null)
const handleChangeLogistics = (row: FactoryOrderNewListData) => {
changeWayCurrentItem.value = row
nextTick(() => {
changeWayDialogRef.value?.showDialog()
})
}
const openUpdateReceiverAddress = (row: FactoryOrderNewListData) => { const openUpdateReceiverAddress = (row: FactoryOrderNewListData) => {
receiverAddressForm.value = { receiverAddressForm.value = {
rfcNumber: '', rfcNumber: '',
...@@ -1312,25 +1488,33 @@ const openUpdateReceiverAddress = (row: FactoryOrderNewListData) => { ...@@ -1312,25 +1488,33 @@ const openUpdateReceiverAddress = (row: FactoryOrderNewListData) => {
const handleRefreshReceiverAddress = async (row: FactoryOrderNewListData) => { const handleRefreshReceiverAddress = async (row: FactoryOrderNewListData) => {
try { try {
await ElMessageBox.confirm('确定刷新地址吗?', '提示', { await ElMessageBox.confirm(
confirmButtonText: '确定', row.shipmentType === 0 ? '确定同步物流/地址吗?' : '确定同步地址吗?',
cancelButtonText: '取消', '提示',
type: 'warning', {
}) confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
} catch { } catch {
return return
} }
await syncReceiverAddress([row.id]) await syncReceiverAddress([row.id])
await ElMessageBox.alert( if (row.shipmentType === 1) {
'请修改/刷新地址后取消物流或者更换物流再重新创建物流订单、获取跟踪号、获取打印面单', await ElMessageBox.alert(
'提示', '请修改/刷新地址后取消物流或者更换物流再重新创建物流订单、获取跟踪号、获取打印面单',
{ '提示',
type: 'warning', {
confirmButtonText: '确定', type: 'warning',
cancelButtonText: '取消', confirmButtonText: '确定',
showCancelButton: true, cancelButtonText: '取消',
}, showCancelButton: true,
) },
)
} else {
ElMessage.success('操作成功')
}
refreshCurrentView({ isRefreshTree: true }) refreshCurrentView({ isRefreshTree: true })
} }
...@@ -1342,6 +1526,9 @@ const handleStatusNodeClick = (node: { status: string }) => { ...@@ -1342,6 +1526,9 @@ const handleStatusNodeClick = (node: { status: string }) => {
if (node.status !== 'SUSPEND') { if (node.status !== 'SUSPEND') {
suspendedSubTab.value = 1 suspendedSubTab.value = 1
} }
if (node.status !== 'CANCELLED') {
cancelledSubTab.value = 1
}
clearTableState() clearTableState()
if (!isSpecialLayout.value) { if (!isSpecialLayout.value) {
refreshCurrentView() refreshCurrentView()
...@@ -1358,6 +1545,13 @@ const handleSuspendTabClick = (value: number) => { ...@@ -1358,6 +1545,13 @@ const handleSuspendTabClick = (value: number) => {
statusCurrentPageRef.value = 1 statusCurrentPageRef.value = 1
refreshTableList() refreshTableList()
} }
// const handleCancelledTabClick = (value: number) => {
// if (cancelledSubTab.value === value) return
// cancelledSubTab.value = value
// currentPage.value = 1
// statusCurrentPageRef.value = 1
// refreshTableList()
// }
const mainColumns = computed(() => [ const mainColumns = computed(() => [
{ {
prop: 'factoryOrderNumber', prop: 'factoryOrderNumber',
...@@ -1484,6 +1678,8 @@ const mainColumns = computed(() => [ ...@@ -1484,6 +1678,8 @@ const mainColumns = computed(() => [
<span>{row?.receiverProvince || ''}</span> <span>{row?.receiverProvince || ''}</span>
<span>{row?.receiverCity || ''}</span> <span>{row?.receiverCity || ''}</span>
<span>{row?.receiverDistrict || ''}</span> <span>{row?.receiverDistrict || ''}</span>
<span>{row?.receiverAddress1 || ''}</span>
<span>{row?.receiverAddress2 || ''}</span>
</div> </div>
) )
}, },
...@@ -1512,7 +1708,7 @@ const mainColumns = computed(() => [ ...@@ -1512,7 +1708,7 @@ const mainColumns = computed(() => [
{ {
prop: 'operation', prop: 'operation',
label: '操作', label: '操作',
minWidth: 200, minWidth: 300,
align: 'center', align: 'center',
slot: 'operation', slot: 'operation',
fixed: 'right', fixed: 'right',
...@@ -1524,6 +1720,143 @@ const showProductInventoryColumns = computed( ...@@ -1524,6 +1720,143 @@ const showProductInventoryColumns = computed(
status.value == 'PENDING_RECEIVE' && status.value == 'PENDING_RECEIVE' &&
pendingAcceptSubTab.value !== 'PENDING_RECEIVE', pendingAcceptSubTab.value !== 'PENDING_RECEIVE',
) )
function parseOperationOrderImageAry(
imageAry?: string | null | unknown,
): { url: string; title?: string }[] {
if (typeof imageAry !== 'string' || !imageAry.trim()) return []
try {
const list = JSON.parse(imageAry) as { url?: string; title?: string }[]
if (!Array.isArray(list)) return []
return list
.filter((x) => x?.url)
.map((x) => ({ url: x.url as string, title: x.title }))
} catch {
return []
}
}
function getOperationOrderMarkLabel(row: operateOrderListData): string {
const mark = row.customizedQuantity
if (mark === 1) return '单面'
if (mark === 2) return '双面'
return ''
}
const operationOrderColumns = [
{
key: 'operationNo',
prop: 'operationNo',
label: '操作单号',
minWidth: 160,
align: 'center',
showOverflowTooltip: true,
},
{
key: 'imageAry',
prop: 'imageAry',
label: '商品图片',
minWidth: 100,
align: 'center',
render: (row: operateOrderListData) => {
const list = parseOperationOrderImageAry(row.imageAry)
if (!list.length) {
return (
<div>
<el-image
src={row.variantImage}
style="width:50px;height:50px"
previewSrcList={[row.variantImage ?? '']}
previewTeleported
/>
</div>
)
}
const urls = list.map((i) => i.url)
return (
<div style="display:flex;gap:4px;">
{list.map((img, idx) => (
<el-image
key={idx}
src={img.url}
previewSrcList={urls}
previewTeleported={true}
style="width:50px;height:50px"
title={img.title ?? ''}
/>
))}
</div>
)
},
},
{
key: 'productName',
prop: 'productName',
label: '商品名称',
minWidth: 180,
align: 'left',
showOverflowTooltip: true,
},
{
key: 'variantSku',
prop: 'variantSku',
label: '变体SKU',
minWidth: 140,
align: 'center',
showOverflowTooltip: true,
},
{
key: 'thirdSkuCode',
prop: 'thirdSkuCode',
label: '库存SKU',
minWidth: 140,
align: 'center',
showOverflowTooltip: true,
},
{
key: 'productMark',
prop: 'productMark',
label: '类型',
width: 72,
align: 'center',
render: (row: operateOrderListData) => {
const label = getOperationOrderMarkLabel(row)
if (!label) return <span />
return <ElTag type="primary">{label}</ElTag>
},
},
{
key: 'statusName',
prop: 'statusName',
label: '挂起前状态',
minWidth: 120,
align: 'center',
showOverflowTooltip: true,
},
{
key: 'craftName',
prop: 'craftName',
label: '工艺',
minWidth: 100,
align: 'center',
showOverflowTooltip: true,
},
{
key: 'supplierProductNo',
prop: 'supplierProductNo',
label: '款号',
minWidth: 120,
align: 'center',
showOverflowTooltip: true,
},
{
key: 'batchArrangeNumber',
prop: 'batchArrangeNumber',
label: '批次号',
minWidth: 120,
align: 'center',
showOverflowTooltip: true,
},
]
const baseProductColumns = computed(() => [ const baseProductColumns = computed(() => [
{ {
...@@ -2064,7 +2397,6 @@ const handlePrinterChange = (value: string) => { ...@@ -2064,7 +2397,6 @@ const handlePrinterChange = (value: string) => {
const handleWarehouseIdChange = (value: string) => { const handleWarehouseIdChange = (value: string) => {
localStorage.setItem('localNewWarehouseId', JSON.stringify(value)) localStorage.setItem('localNewWarehouseId', JSON.stringify(value))
} }
// 复用 podCN 的单件打单组件:为 factoryOrderNew 注入差异部分
const mapOrderParamListToSubmitItems = ( const mapOrderParamListToSubmitItems = (
orderParamList: { id: number; dataVersion?: number }[], orderParamList: { id: number; dataVersion?: number }[],
) => orderParamList.map((item) => ({ id: item.id })) ) => orderParamList.map((item) => ({ id: item.id }))
...@@ -2424,19 +2756,11 @@ const handleProductionComplete = async () => { ...@@ -2424,19 +2756,11 @@ const handleProductionComplete = async () => {
getIds: getSelectedIds, getIds: getSelectedIds,
api: completeDeliveryApi, api: completeDeliveryApi,
confirmText: '确定生产完成吗?', confirmText: '确定生产完成吗?',
refreshTree: true,
onSuccess: (res) => { onSuccess: (res) => {
const data = Array.isArray(res.data) const data = Array.isArray(res.data)
? (res.data as ResultInfoDataItem[]) ? (res.data as ResultInfoDataItem[])
: [] : []
const hasFailed = data.some((item) => !item.status) openResultInfoDialog(data)
if (hasFailed) {
resultInfo.value = data.filter((item) => !item.status)
resultRefs.value?.showDialog()
} else {
ElMessage.success('操作成功')
refreshCurrentView({ isRefreshTree: true })
}
}, },
}) })
} }
...@@ -2575,11 +2899,82 @@ const getLogisticsWay = async () => { ...@@ -2575,11 +2899,82 @@ const getLogisticsWay = async () => {
const { data } = await getLogisticsWayApi() const { data } = await getLogisticsWayApi()
logisticsWayList.value = data logisticsWayList.value = data
} }
const handleInterceptionSuccess = (row?: FactoryOrderNewListData) => {
if (row) {
interceptSuccessRows.value = [row]
} else {
if (!ensureSelection()) return
interceptSuccessRows.value = [...selectedRows.value]
}
interceptSuccessForm.value.suspendSussessType = undefined
interceptSuccessDialogVisible.value = true
nextTick(() => interceptSuccessFormRef.value?.clearValidate())
}
const submitInterceptSuccess = async () => {
const form = interceptSuccessFormRef.value
if (!form) return
try {
await form.validate()
} catch {
return
}
const suspendSussessType = interceptSuccessForm.value.suspendSussessType
if (suspendSussessType === undefined) return
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
const rows = interceptSuccessRows.value
const data = rows.map((item: FactoryOrderNewListData) => {
return {
factoryOrderNumber: item.factoryOrderNumber as string,
suspendSussessType,
}
}) as { factoryOrderNumber: string; suspendSussessType: number }[]
try {
const res = await interceptSuccessApi(data)
if (res.code !== 200) return
interceptSuccessDialogVisible.value = false
const resultData = Array.isArray(res.data)
? (res.data as ResultInfoDataItem[])
: []
openResultInfoDialog(resultData)
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const handleInterceptionFail = async (row?: FactoryOrderNewListData) => {
await executeBatchAction({
getIds: () =>
row
? [row.factoryOrderNumber as string]
: selectedRows.value.map(
(item: FactoryOrderNewListData) =>
item.factoryOrderNumber as string,
),
api: (ids) => interceptUpdateApi(ids as string[]),
confirmText: '拦截失败,订单会恢复到拦截前的状态,确认是否拒绝拦截申请?',
onSuccess: (res) => {
const data = Array.isArray(res.data)
? (res.data as ResultInfoDataItem[])
: []
openResultInfoDialog(data)
},
})
}
onMounted(() => { onMounted(() => {
loadStatusTreeCounts() loadStatusTreeCounts()
loadAllDictionaries() loadAllDictionaries()
getLogisticsWay() getLogisticsWay()
// 仅用于消除 TS 插件的 unused-vars 警告:template refs 会绑定这两个变量 getReceiverCountryList()
loadWarehouseList()
void treeRef.value void treeRef.value
void tableRef.value void tableRef.value
if (status.value === 'PENDING_RECEIVE') { if (status.value === 'PENDING_RECEIVE') {
......
...@@ -13,7 +13,7 @@ import { ...@@ -13,7 +13,7 @@ import {
import { import {
getListCraftApi, getListCraftApi,
getProductionClientApi, getProductionClientApi,
getOrderList, getOrderTrackingList,
getOperationLogApi, getOperationLogApi,
getOrderTabData, getOrderTabData,
getfaceSimplexFileApi, getfaceSimplexFileApi,
...@@ -44,7 +44,7 @@ import { convertToChinaTime } from '@/utils/index' ...@@ -44,7 +44,7 @@ import { convertToChinaTime } from '@/utils/index'
import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts' import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
const tabName = ref<string>('1')
const [searchForm, resetSearchForm] = useValue<SearchForm>({ const [searchForm, resetSearchForm] = useValue<SearchForm>({
timeType: 1, timeType: 1,
shopNumber: '', shopNumber: '',
...@@ -539,7 +539,7 @@ const getOrderListFn = async () => { ...@@ -539,7 +539,7 @@ const getOrderListFn = async () => {
}) })
const { currentPage, pageSize } = pagination.value const { currentPage, pageSize } = pagination.value
try { try {
const { data } = await getOrderList( const { data } = await getOrderTrackingList(
{ {
...searchForm.value, ...searchForm.value,
startTime: startTime:
...@@ -711,6 +711,12 @@ function changeChinaTime(zone: string) { ...@@ -711,6 +711,12 @@ function changeChinaTime(zone: string) {
}) })
} }
const tabChange = () => {
if(tabName.value==='2'){
operationLog()
}
}
const handleRadioGroupClick = (event: Event) => { const handleRadioGroupClick = (event: Event) => {
const target = event.target as HTMLElement const target = event.target as HTMLElement
const radioButton = target.closest('.el-radio-button') const radioButton = target.closest('.el-radio-button')
...@@ -790,14 +796,6 @@ useEnterKeyTrigger({ ...@@ -790,14 +796,6 @@ useEnterKeyTrigger({
}, },
}) })
watch(
() => currentRowId.value,
(newValue, oldValue) => {
if (newValue !== oldValue) {
operationLog()
}
},
)
loadTabData() loadTabData()
loadProductionClient() loadProductionClient()
getUserMark() getUserMark()
...@@ -1198,8 +1196,8 @@ onMounted(() => { ...@@ -1198,8 +1196,8 @@ onMounted(() => {
</template> </template>
<template #other> <template #other>
<div class="bottom-table"> <div class="bottom-table">
<el-tabs> <el-tabs v-model="tabName" @tab-change="tabChange">
<el-tab-pane label="商品明细"> <el-tab-pane name="1" label="商品明细">
<TableView <TableView
ref="goodsRef" ref="goodsRef"
v-loading="goodsLoading" v-loading="goodsLoading"
...@@ -1212,7 +1210,7 @@ onMounted(() => { ...@@ -1212,7 +1210,7 @@ onMounted(() => {
</template> </template>
</TableView> </TableView>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="操作日志" @tab-click="operationLog"> <el-tab-pane name="2" label="操作日志" >
<div> <div>
<el-button <el-button
:type="zoneType === 'Asia/Shanghai' ? 'primary' : ''" :type="zoneType === 'Asia/Shanghai' ? 'primary' : ''"
......
...@@ -65,7 +65,14 @@ ...@@ -65,7 +65,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PodCnOrderListData, LogisticsData } from '@/types/api/podCnOrder' import { LogisticsData } from '@/types/api/podCnOrder'
export interface ChangeWayRowData {
id?: number | string
version?: number
logisticsWayId?: number | string | null
logisticsWayName?: string
}
const isChangeWay = ref(false) const isChangeWay = ref(false)
const isChangeWayLoading = ref(false) const isChangeWayLoading = ref(false)
const changeWayRow = ref<LogisticsData>({} as LogisticsData) const changeWayRow = ref<LogisticsData>({} as LogisticsData)
...@@ -76,13 +83,13 @@ const wayDialogTitle = ref('') ...@@ -76,13 +83,13 @@ const wayDialogTitle = ref('')
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
rowData: PodCnOrderListData | null rowData: ChangeWayRowData | null
getTableFn?: (...args: any[]) => any getTableFn?: (...args: any[]) => any
submitFn?: (...args: any[]) => any submitFn?: (...args: any[]) => any
changeLogisticsApi?: (...args: any[]) => any changeLogisticsApi?: (...args: any[]) => any
}>(), }>(),
{ {
rowData: () => ({} as PodCnOrderListData), rowData: () => ({} as ChangeWayRowData),
getTableFn: () => {}, getTableFn: () => {},
submitFn: () => {}, submitFn: () => {},
changeLogisticsApi: () => {}, changeLogisticsApi: () => {},
......
...@@ -4,13 +4,27 @@ import Components from 'unplugin-vue-components/vite' ...@@ -4,13 +4,27 @@ import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx' import vueJsx from '@vitejs/plugin-vue-jsx'
import compression from 'vite-plugin-compression'
// https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: '/', base: '/',
server:{
proxy: {
'/api/': {
target: 'http://172.16.17.244:8060',
},
},
},
plugins: [ plugins: [
vue(), vue(),
compression({
ext: '.gz', // 生成的文件扩展名
compressionOptions: { level: 9 }, // 压缩级别,1-9,越高压缩率越大
algorithm: 'gzip', // 压缩算法,可选 'gzip' | 'brotli'
threshold: 51200, // 阈值:小于100kb的文件不压缩(压缩后反而可能变大)
filter:/\.(js|css|html|svg|json)$/i , // 排除已经压缩过的文件(图片、视频、压缩包本身)
deleteOriginFile: false, // 不要删除原始源文件,默认false,避免回退问题
}),
vueJsx(), vueJsx(),
AutoImport({ AutoImport({
imports: ['vue', 'vue-router', 'pinia'], imports: ['vue', 'vue-router', 'pinia'],
......
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