Commit a96bb2d7 by qinjianhui

feat: 工厂订单取消后处理功能开发

parent 72632338
import axios from './axios'
import type { BasePaginationData } from '@/types/api'
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 getCancelOrderProcessListApi(
data: Record<string, unknown>,
currentPage: number,
pageSize: number,
) {
const body = normalizePodOrderQueryPayload({ ...data })
return axios.post<never, BasePaginationData<operateOrderListData>>(
'factory/podOrderOperation/list_page',
{ ...body, status: 'PENDING_SCHEDULE', currentPage, pageSize },
)
}
......@@ -11,7 +11,7 @@ import type {
RestockData,
SearchForm,
StatusTreeNode,
} from '@/types/api/factoryOrderNew/factoryOrderNew'
} from '@/types/api/factoryOrderNew'
import { ResultInfoDataItem } from '@/types/api/order/common'
import { ExportParams } from '@/types/api/order/factoryOrderNew'
import type { OrderData } from '@/types/api/podMakeOrder'
......
......@@ -107,6 +107,14 @@ const router = createRouter({
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',
meta: {
title: 'POD(CN)订单跟踪',
......
......@@ -151,6 +151,11 @@ const menu: MenuItem[] = [
id: 12,
label: '工厂订单(NEW)',
},
{
index: '/order/cancel-order-process',
id: 13,
label: '取消后订单处理',
},
],
},
{
......
import type { PaginationData } from '@/types/api'
export interface StatusTreeNode {
status: string
statusName: string
......@@ -271,3 +272,9 @@ export interface PickFailData {
afterOutOccupied?: number
afterOutAvailable?: number
}
export type CardLayoutListFetcher = (
payload: Record<string, unknown>,
currentPage: number,
pageSize: number,
) => Promise<PaginationData<operateOrderListData>>
<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 === 'processed' }"
@click="handleOrderStatusClick('processed')"
>
<span>已处理</span>
<span>({{ orderStatus.processed }})</span>
</div>
<div
class="order-status-item"
:class="{ 'order-status-item-active': currentStatus === 'unprocessed' }"
@click="handleOrderStatusClick('unprocessed')"
>
<span>未处理</span>
<span>({{ orderStatus.unprocessed }})</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"></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="historyFactoryOrderNewData"
tracking-placeholder="扫描枪输入操作单号,录入下一单本单自动生产完成,最后一单扫两次完成生产"
:query-api="getOperationByNo"
:complete-api="completeOperationById"
:download-api="downloadOperationById"
@on-success="handleFastProductionSuccess"
@close="fastClose"
/>
</template>
<script setup lang="ts">
import { 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 {
completeDeliveryApi,
downloadOperationMaterialApi,
getByOperationNoLogApi,
getFactoryOrderNewOperateDetailApi,
} from '@/api/factoryOrderNew'
import { getCancelOrderProcessListApi } 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'
const cardLayoutRef = ref()
const fetchCardList: CardLayoutListFetcher = async (payload, current, size) => {
const res = await getCancelOrderProcessListApi(payload, current, size)
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 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) =>
getByOperationNoLogApi(operationNo) as Promise<{ data?: unknown }>
const completeOperationById = (ids: number[]) => completeDeliveryApi(ids)
const downloadOperationById = (ids: number[]) =>
downloadOperationMaterialApi(ids)
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<{
processed: number
unprocessed: number
}>({
processed: 0,
unprocessed: 0,
})
const currentStatus = ref<'processed' | 'unprocessed'>('processed')
const handleOrderStatusClick = (status: 'processed' | 'unprocessed') => {
currentStatus.value = status
search()
}
onMounted(() => {
void loadAllDictionaries()
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 {
} from '@/api/logistics'
import { createLogisticsOrdersApi } from '@/api/podCnOrder'
import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew/factoryOrderNew'
import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew'
const createLogisticDialogVisible = ref(false)
const props = defineProps<{
......
......@@ -57,7 +57,7 @@
</template>
<script setup lang="ts">
import { updateCustomDeclarationInfoApi } from '@/api/podCnOrder'
import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew/factoryOrderNew'
import { FactoryOrderNewListData } from '@/types/api/factoryOrderNew'
import {
CustomDeclarationInfoForm,
PodCnOrderListData,
......
......@@ -196,7 +196,7 @@ import {
printPickPdfByBatchNumberApi,
printProductionPdfByBatchNumberApi,
} 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 TableView from '@/components/TableView.vue'
import ArrangeDialog from './ArrangeDialog.vue'
......
......@@ -293,19 +293,32 @@ import {
getFactoryOrderNewLogApi,
} from '@/api/factoryOrderNew'
import type { PaginationData } from '@/types/api'
import type { CardLayoutListFetcher } from '@/types/api/factoryOrderNew'
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 LogList from '@/components/LogList.vue'
import RightClickMenu from '@/components/RightClickMenu.vue'
import platformJson from '../../../../json/platform.json'
const props = defineProps<{
status: string
queryPayload: Record<string, unknown>
status?: string
queryPayload?: Record<string, unknown>
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<{
'selection-change': [items: operateOrderListData[]]
'view-detail': [item: operateOrderListData]
......@@ -324,9 +337,9 @@ const {
initPageSize: props.initPageSize || 100,
initLoad: false,
query: async (current, size) => {
const payload = {
...props.queryPayload,
status: props.status === 'ALL' ? undefined : props.status,
const payload = buildListPayload()
if (props.fetchList) {
return props.fetchList(payload, current, size)
}
const res = await getCardLayoutListApi(payload, current, size)
return res.data as unknown as PaginationData<operateOrderListData>
......@@ -411,13 +424,20 @@ const rightChange = (code: string) => {
const openLogDialog = async (item: operateOrderListData) => {
if (!item.podOrderId) return
const loading = ElLoading.service({
fullscreen: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const res = await getFactoryOrderNewLogApi(item.podOrderId as number)
logList.value = (res.data || []) as unknown as LogListData[]
logVisible.value = true
} catch (e) {
// ignore
}
logList.value = (res.data || []) as unknown as LogListData[]
logVisible.value = true
} catch (e) {
// ignore
} finally {
loading.close()
}
}
const clearSelection = () => {
......
......@@ -257,7 +257,7 @@ import platformJson from '../../../../json/platform.json'
import type {
operateOrderListData,
PickCompleteData,
} from '@/types/api/factoryOrderNew/factoryOrderNew'
} from '@/types/api/factoryOrderNew'
const emit = defineEmits(['adjustPickOrderSuccess'])
const props = defineProps<{
......
......@@ -123,7 +123,7 @@ import {
replenishmentCompleteApi,
markStockOutOfApi,
} 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 {
InterProductList,
......
......@@ -71,7 +71,7 @@ import { ref } from 'vue'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import BigNumber from 'bignumber.js'
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 _ from 'lodash'
import CreateOutboundDialog from './CreateOutboundDialog.vue'
......
......@@ -63,7 +63,7 @@
import { reactive } from 'vue'
import { ElMessage } from 'element-plus'
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 usePageList from '@/utils/hooks/usePageList'
......
......@@ -141,8 +141,6 @@ export function useOrderDictionaries() {
getUserMark(),
getCustomTagList(),
getLogisticsCompanyAllCodelist(),
getReceiverCountryList(),
loadWarehouseList(),
])
}
......
......@@ -7,7 +7,7 @@ import {
getFactoryOrderNewLogApi,
getOperationOrderByIdApi,
} from '@/api/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew'
import type {
FactoryOrderNewListData,
LogListData,
......
......@@ -6,7 +6,7 @@ import {
getSuspendStatisticsApi,
} from '@/api/factoryOrderNew'
import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew'
interface UseOrderStatusTreeOptions {
getQueryPayload: () => Record<string, unknown>
......
......@@ -1189,6 +1189,8 @@ const {
craftList,
warehouseList,
productTypeGroups,
getReceiverCountryList,
loadWarehouseList,
loadAllDictionaries,
} = useOrderDictionaries()
......@@ -2925,6 +2927,8 @@ onMounted(() => {
loadStatusTreeCounts()
loadAllDictionaries()
getLogisticsWay()
getReceiverCountryList()
loadWarehouseList()
// 仅用于消除 TS 插件的 unused-vars 警告:template refs 会绑定这两个变量
void treeRef.value
void tableRef.value
......
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