Commit a18364fe by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !204
parents 12e1e603 01701ca7
......@@ -57,6 +57,7 @@ declare module 'vue' {
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default']
......
......@@ -47,7 +47,8 @@
"unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.1.4"
"vite": "^5.1.4",
"vite-plugin-compression": "^0.5.1"
}
},
"node_modules/@ampproject/remapping": {
......@@ -4320,6 +4321,20 @@
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
......@@ -4509,6 +4524,12 @@
"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": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
......@@ -4842,6 +4863,18 @@
"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": {
"version": "4.5.4",
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
......@@ -6201,6 +6234,15 @@
"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": {
"version": "1.16.1",
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz",
......@@ -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": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",
......
......@@ -49,6 +49,7 @@
"unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5",
"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 {
LogListData,
operateOrderListData,
PickCompleteData,
PickFailData,
ProductListData,
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'
import type { LogisticsData } from '@/types/api/podCnOrder'
import type { PrintData } from '@/types/api/podOrder'
function normalizePodOrderQueryPayload(
data: Record<string, unknown>,
......@@ -44,6 +44,7 @@ export function getFactoryOrderNewListApi(
status?: string,
subStatus?: number,
pauseReason?: number,
cancelSubType?: number,
) {
const body = normalizePodOrderQueryPayload({ ...data } as Record<
string,
......@@ -58,6 +59,7 @@ export function getFactoryOrderNewListApi(
status,
acceptedStatus: subStatus,
pauseReason,
cancelSubType,
},
)
}
......@@ -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: {
orderIds?: 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(
data: Record<string, unknown>,
currentPage: number,
......@@ -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) {
return axios.get<never, BaseRespData<ProductListData[]>>(
'factory/podOrderPauseControl/get',
......@@ -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) {
return axios.post<never, BaseRespData<never>>(
'factory/podOrder/updateReceiverAddress',
......@@ -590,3 +630,19 @@ export function exportFactoryOrderInfo(data: ExportParams) {
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(
},
)
}
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[]) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/processExceptionOrders',
......
......@@ -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: '取消后订单处理',
// },
],
},
{
......
export interface CancelOrderProcessStateGroupMap {
alreadyInStock: number
NotInStock: number
}
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>>
......@@ -92,6 +92,8 @@ export interface FactoryOrderNewListData {
source?: string
logisticsCompanyName?: string
logisticsCompanyCode?: string
logisticsWayId?: number | string
logisticsWayName?: string
selfOwned?: boolean
createTime?: 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 {
} 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<{
......
<template>
<el-dialog
v-model="dialogVisible"
:title="fastKey === 'detail' ? '查看详情' : '快捷生产'"
:title="title"
top="140px"
:fullscreen="true"
:close-on-click-modal="false"
......@@ -44,7 +44,10 @@
</div>
</div>
<div class="right">
<div v-if="fastKey === 'fastProduction'" class="input">
<div
v-if="['fastProduction', 'fastReceipt'].includes(fastKey)"
class="input"
>
<el-input
ref="trackingNumberRef"
v-model="TrackingNumber"
......@@ -60,20 +63,35 @@
<div class="div-text">
<div class="div-content">
<div :title="String(detail?.userMark)" class="div-item">
<span style="font-size: 18px">客户</span>
<p style="color: red; font-size: 30px">
<span class="div-item-label" style="font-size: 18px"
>客户:</span
>
<div class="div-item-value" style="color: red; font-size: 30px">
{{ detail?.userMark }}
</p>
</div>
</div>
<div
:title="String(detail?.factoryOrderNumber)"
class="div-item"
style="margin-top: 14px"
>
<span style="font-size: 18px">订单号</span>
<p style="color: red; font-size: 22px">
<span class="div-item-label" style="font-size: 18px"
>订单号:</span
>
<div class="div-item-value" style="color: red; font-size: 22px">
{{ 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>
......@@ -86,77 +104,132 @@
:title="String(operationNoDisplay)"
class="div-item"
>
<span>操作单号</span>
<p>{{ operationNoDisplay }}</p>
<span class="div-item-label">操作单号:</span>
<div class="div-item-value">{{ operationNoDisplay }}</div>
</div>
<div
v-else
:title="detail?.factorySubOrderNumber"
class="div-item"
>
<span>生产单号</span>
<p>
<span class="div-item-label">生产单号:</span>
<div class="div-item-value">
{{ 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
v-if="!showOperationNoRow"
:title="detail?.thirdSubOrderNumber || ''"
class="div-item"
>
<span>第三方生产单号</span>
<p>
<span class="div-item-label">第三方生产单号:</span>
<div class="div-item-value">
{{ detail?.thirdSubOrderNumber }}
</p>
</div>
</div>
<div :title="String(detail?.craftName)" class="div-item">
<span>生产工艺</span>
<p>
<span class="div-item-label">生产工艺:</span>
<div class="div-item-value">
{{ detail?.craftName }}
</p>
</div>
</div>
<div :title="detail?.baseSku" class="div-item">
<span>基版</span>
<p>{{ detail?.baseSku }}</p>
<div
v-if="fastKey !== 'fastReceipt'"
:title="detail?.baseSku"
class="div-item"
>
<span class="div-item-label">基版:</span>
<div class="div-item-value">{{ detail?.baseSku }}</div>
</div>
<div :title="detail?.variantSku" class="div-item">
<span>变体SKU</span>
<p>{{ detail?.variantSku }}</p>
<span class="div-item-label">变体SKU:</span>
<div class="div-item-value">{{ detail?.variantSku }}</div>
</div>
<div
v-if="fastKey !== 'fastReceipt'"
:title="
String(showOperationNoRow ? detail?.quantity : detail?.num)
"
class="div-item"
>
<span>数量</span>
<p>{{ showOperationNoRow ? detail?.quantity : detail?.num }}</p>
<span class="div-item-label">数量:</span>
<div class="div-item-value">
{{ showOperationNoRow ? detail?.quantity : detail?.num }}
</div>
</div>
<div :title="String(detail?.size)" class="div-item">
<span>尺寸</span>
<p>{{ detail?.size }}</p>
<div
v-if="fastKey !== 'fastReceipt'"
:title="String(detail?.size)"
class="div-item"
>
<span class="div-item-label">尺寸:</span>
<div class="div-item-value">{{ detail?.size }}</div>
</div>
<div
v-if="detail?.sizeType"
v-if="detail?.sizeType && fastKey !== 'fastReceipt'"
:title="String(detail?.sizeType)"
class="div-item"
>
<span>尺码类型</span>
<p>
<span class="div-item-label">尺码类型:</span>
<div class="div-item-value">
{{ sizeList.find((i) => i.value === detail.sizeType)?.name }}
</p>
</div>
</div>
<div :title="detail?.shopNumber ?? ''" class="div-item">
<span>店铺单号</span>
<p>{{ detail?.shopNumber ?? '' }}</p>
<div
v-if="fastKey !== 'fastReceipt'"
:title="detail?.shopNumber ?? ''"
class="div-item"
>
<span class="div-item-label">店铺单号:</span>
<div class="div-item-value">{{ detail?.shopNumber ?? '' }}</div>
</div>
<div :title="detail?.createTime" class="div-item">
<span>创建时间</span>
<p>{{ detail?.createTime }}</p>
<span class="div-item-label">创建时间:</span>
<div class="div-item-value">{{ detail?.createTime }}</div>
</div>
</div>
</div>
......@@ -164,7 +237,10 @@
<div class="btn">
<div
:style="{
visibility: fastKey === 'fastProduction' ? 'visible' : 'hidden',
visibility:
fastKey === 'fastProduction' || fastKey === 'fastReceipt'
? 'visible'
: 'hidden',
}"
class="btn-sure"
>
......@@ -174,13 +250,13 @@
type="success"
@click="changeStatus"
>
生产完成
{{ fastKey === 'fastProduction' ? '生产完成' : '快捷入库' }}
</el-button>
<div class="check">
<el-checkbox v-model="isAutoSure"> 自动完成上一单 </el-checkbox>
</div>
</div>
<div class="btn-down">
<div v-if="fastKey !== 'fastReceipt'" class="btn-down">
<div class="check">
<el-checkbox v-model="isDownloadImage" size="large">
扫码下载素材
......@@ -243,12 +319,21 @@ interface HistoryDataItem {
orderNumber: string
finished: boolean
}
const title = computed(() => {
if (props.fastKey === 'fastProduction') {
return '快捷生产'
} else if (props.fastKey === 'fastReceipt') {
return '快捷入库'
} else {
return '查看详情'
}
})
export type FastProductionDetail = PodOrderRes & {
operationNo?: string | null
quantity?: number | null
podOrderNo?: string | null
podOrderProductId?: number | null
statusName?: string | null
}
const trackingNumberRef = ref()
const historyData = ref<HistoryDataItem[]>([])
......@@ -422,7 +507,10 @@ watch(
historyData.value = history ? JSON.parse(history) : []
const len = historyData.value
if (len.length > 0 && props.fastKey === 'fastProduction') {
if (
len.length > 0 &&
(props.fastKey === 'fastProduction' || props.fastKey === 'fastReceipt')
) {
confirmQuery(len, 0)
}
placeholderText.value = props.trackingPlaceholder
......@@ -452,7 +540,9 @@ watch(
const confirmQuery = (len: HistoryDataItem[], i: number) => {
const el = len[i]
showConfirm(
`${props.pendingOrderLabel} ${el.orderNumber} 未生产完成,取消则不提醒?`,
`${props.pendingOrderLabel} ${el.orderNumber} ${
props.fastKey === 'fastProduction' ? '未生产' : '未入库'
}完成,取消则不提醒?`,
{
confirmButtonText: '确定',
cancelButtonText: '取消',
......@@ -562,7 +652,7 @@ const download = async () => {
if (detail.value && detail.value?.id != -1) {
try {
const id = props.isNewOrder
? detail.value.podOrderProductId as number
? (detail.value.podOrderProductId as number)
: detail.value.id
if (id !== undefined) {
try {
......@@ -668,6 +758,12 @@ const trackCodeInput = async () => {
const onOpened = () => {
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>
<style lang="scss" scoped>
.sure-btn {
......@@ -784,27 +880,27 @@ const onOpened = () => {
align-items: flex-start;
padding: 15px 10px;
box-sizing: border-box;
position: relative;
border: 1px solid #ececec;
.div-item {
width: 50%;
margin-bottom: 10px;
display: flex;
align-items: center;
height: 36px;
line-height: 36px;
p {
font-weight: 400;
color: black;
}
span {
.div-item-label {
display: inline-block;
text-align: right;
width: 120px;
}
span::after {
content: ':';
margin: 0 3px;
.div-item-value {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
......@@ -819,11 +915,6 @@ const onOpened = () => {
color: black;
z-index: 3;
}
.div-content {
position: relative;
border: 1px solid #ececec;
}
}
.input {
......
......@@ -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 @@ const formRef = ref<FormInstance>()
const orderIds = ref<(number | string)[]>([])
const suspendReasons = [
{ label: '客户拦截', value: 1 },
// { label: '客户拦截', value: 1 },
{ label: '地址异常', value: 2 },
{ label: '素材异常', value: 3 },
{ label: '其他', value: 4 },
......
......@@ -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'
......
......@@ -50,8 +50,11 @@ export function useOrderBatchActions(options: UseOrderBatchActionsOptions) {
const res = await action.api(ids)
if (res.code !== 200) return
if (action.successText) ElMessage.success(action.successText)
if (action.onSuccess) await action.onSuccess(res)
await refreshCurrentView({ isRefreshTree: !!action.refreshTree })
if (action.onSuccess) {
await action.onSuccess(res)
} else {
await refreshCurrentView({ isRefreshTree: !!action.refreshTree })
}
if (action.onAfter) await action.onAfter()
} catch (e) {
console.error(e)
......
......@@ -141,8 +141,6 @@ export function useOrderDictionaries() {
getUserMark(),
getCustomTagList(),
getLogisticsCompanyAllCodelist(),
getReceiverCountryList(),
loadWarehouseList(),
])
}
......
......@@ -5,8 +5,9 @@ import {
getFactoryOrderNewDetailApi,
getFactoryOrderNewListApi,
getFactoryOrderNewLogApi,
getOperationOrderByIdApi,
} from '@/api/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew'
import type {
FactoryOrderNewListData,
LogListData,
......@@ -23,6 +24,7 @@ interface UseOrderListAndDetailOptions {
getQueryPayload: () => Record<string, unknown>
getListPageAcceptedSubStatus: () => number | undefined
suspendedSubTab: Ref<number>
cancelledSubTab: Ref<number>
}
export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
......@@ -34,11 +36,13 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
getQueryPayload,
getListPageAcceptedSubStatus,
suspendedSubTab,
cancelledSubTab,
} = options
const subLoading = ref(false)
const activeTab = ref<'product' | 'log'>('product')
const activeTab = ref<'product' | 'operation' | 'log'>('product')
const productList = ref<ProductListData[]>([])
const operationOrderList = ref<operateOrderListData[]>([])
const logList = ref<LogListData[]>([])
const tableRef = ref()
const currentRow = ref<FactoryOrderNewListData | null>(null)
......@@ -73,6 +77,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
} = usePageList<FactoryOrderNewListData>({
query: (page, size) => {
const isSuspendTree = treeStatus.value === 'SUSPEND'
const isCancelled = status.value === 'CANCELLED'
return getFactoryOrderNewListApi(
buildListQueryBody(),
page,
......@@ -80,6 +85,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
status.value === 'ALL' ? undefined : status.value,
getListPageAcceptedSubStatus(),
isSuspendTree ? suspendedSubTab.value : undefined,
isCancelled ? cancelledSubTab.value : undefined,
).then(async (res) => {
const records = res.data.records || []
await nextTick(() => {
......@@ -101,6 +107,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
selectedRows.value = []
cardSelectList.value = []
productList.value = []
operationOrderList.value = []
logList.value = []
currentRow.value = null
listSortProp.value = null
......@@ -160,9 +167,12 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
cardSelectList.value = items
}
const getOrderDetailsById = async (tabName?: 'product' | 'log') => {
const getOrderDetailsById = async (
tabName?: 'product' | 'operation' | 'log',
) => {
if (!currentRow.value) {
productList.value = []
operationOrderList.value = []
logList.value = []
return
}
......@@ -181,6 +191,11 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
productList.value = Array.isArray(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 {
logList.value = []
// const logRes = isSuspend
......@@ -200,6 +215,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
watch(currentRow, (row) => {
if (!row) {
productList.value = []
operationOrderList.value = []
logList.value = []
return
}
......@@ -217,7 +233,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
const handleTabClick = (tab: TabsPaneContext) => {
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)
}
......@@ -225,6 +241,7 @@ export function useOrderListAndDetail(options: UseOrderListAndDetailOptions) {
subLoading,
activeTab,
productList,
operationOrderList,
logList,
tableRef,
currentRow,
......
import { computed, ref, nextTick } from 'vue'
import {
getCancelledOrderStatisticsApi,
getPodOrderAcceptedStatisticsApi,
getPodOrderStateGroupListApi,
getSuspendStatisticsApi,
} from '@/api/factoryOrderNew'
import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew'
import type { SearchForm } from '@/types/api/factoryOrderNew'
interface UseOrderStatusTreeOptions {
getQueryPayload: () => Record<string, unknown>
currentPage: { value: number }
pageSize: { value: number }
onClearTableState: () => void
onRefreshCurrentView: () => void
}
export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
......@@ -19,8 +19,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
getQueryPayload,
currentPage,
pageSize,
onClearTableState,
onRefreshCurrentView,
} = options
const cardLayoutStatuses = [
......@@ -46,12 +44,19 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
acceptedOutOfStockCount: 0,
})
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: 'materialExceptionCount', value: 3, count: 0 },
{ label: '其他', key: 'otherReasonCount', value: 4, count: 0 },
])
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 isCardLayout = computed(() => cardLayoutStatuses.includes(status.value))
......@@ -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 () => {
try {
const res = await getPodOrderStateGroupListApi()
......@@ -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 = (
tab: 'PENDING_RECEIVE' | 'ACCEPT_FAIL_OUT_OF_STOCK',
......@@ -160,6 +168,8 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
pendingAcceptCounts,
suspendedTabs,
suspendedSubTab,
cancelledTabs,
cancelledSubTab,
treeRef,
isCardLayout,
isSpecialLayout,
......@@ -167,9 +177,9 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
getListPageAcceptedSubStatus,
getPendingReceiveCounts,
getSuspendCounts,
getCancelledCounts,
loadStatusTreeCounts,
handleStatusNodeClick,
handlePendingAcceptTabClick,
toggleExpand,
toggleExpand
}
}
......@@ -404,14 +404,27 @@
</ElButton>
</span>
<span
v-if="status === 'PENDING_RECEIVE' || status === 'SUSPEND'"
v-if="
status === 'PENDING_RECEIVE' ||
(status === 'SUSPEND' && suspendedSubTab !== 1)
"
class="item"
>
<ElButton type="danger" @click="handleCancelOrder">取消订单</ElButton>
</span>
<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"
@command="handleLogisticsCommand"
>
......@@ -584,11 +597,21 @@
</template>
<!-- 挂起 -->
<template v-if="status === 'SUSPEND'">
<span class="item">
<span v-if="suspendedSubTab !== 1" class="item">
<ElButton type="warning" @click="() => handleCancelSuspend()"
>取消挂起</ElButton
>
</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">
<ElButton type="primary" @click="handleSyncAddress"
>同步收货地址</ElButton
......@@ -659,6 +682,18 @@
</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
class="status-subtab"
......@@ -735,7 +770,10 @@
}}
</ElButton>
<ElButton
v-if="showPendingLogisticsUpdateAddress(row)"
v-if="
showPendingLogisticsUpdateAddress() &&
row.shipmentType === 1
"
type="primary"
link
size="small"
......@@ -744,16 +782,28 @@
修改地址
</ElButton>
<ElButton
v-if="status === 'PENDING_CREATE_LOGISTICS'"
type="primary"
v-if="showChangeLogistics(row)"
type="warning"
link
size="small"
@click.stop="handleChangeLogistics(row)"
>
更换物流方式
</ElButton>
<ElButton
v-if="showPendingLogisticsUpdateAddress()"
type="success"
link
size="small"
@click.stop="handleRefreshReceiverAddress(row)"
>
刷新地址
{{
row.shipmentType === 0 ? '同步物流/地址' : '同步地址'
}}
</ElButton>
<ElButton
v-if="status === 'SUSPEND'"
v-if="status === 'SUSPEND' && suspendedSubTab !== 1"
type="primary"
link
size="small"
......@@ -761,6 +811,24 @@
>
取消挂起
</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>
</template>
</TableView>
......@@ -803,6 +871,19 @@
/>
</div>
</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="操作日志">
<div
v-loading="subLoading"
......@@ -934,6 +1015,60 @@
:submit-address-api="submitFactoryOrderReceiverAddress"
@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
v-model="exportVisible"
title="导出选项"
......@@ -996,6 +1131,8 @@ import {
composingNewPodOrderDesignImages,
printNewPodOrderProductionOrderApi,
syncReceiverAddress,
getLogisticsCalculation,
changeLogisticsApi,
updateReceiverAddressApi,
updateRemarkApi,
completeDeliveryApi,
......@@ -1008,6 +1145,8 @@ import {
statusPushApi,
arrangeFinishApi,
exportFactoryOrderInfo,
interceptUpdateApi,
interceptSuccessApi,
} from '@/api/factoryOrderNew'
import { getLogisticsWayApi } from '@/api/podUsOrder'
import BigNumber from 'bignumber.js'
......@@ -1034,6 +1173,7 @@ import PodMakeOrder from '@/views/order/podUs/PodMakeOrder.vue'
import PodDistributionOrder from '@/views/order/podCN/PodDistributionOrder.vue'
import PrintWarehouseSkuTag from '@/views/order/components/printWarehouseSkuTag.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 type { FactoryOrderSearchQueryVisibilityContext } from '@/types/api/factoryOrderNew/searchQueryVisibility'
import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew'
......@@ -1055,6 +1195,17 @@ const exportForm = ref({
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 {
userMarkList,
receiverCountryList,
......@@ -1063,6 +1214,8 @@ const {
craftList,
warehouseList,
productTypeGroups,
getReceiverCountryList,
loadWarehouseList,
loadAllDictionaries,
} = useOrderDictionaries()
......@@ -1096,6 +1249,8 @@ const {
pendingAcceptCounts,
suspendedTabs,
suspendedSubTab,
// cancelledTabs,
cancelledSubTab,
treeRef,
isCardLayout,
isSpecialLayout,
......@@ -1103,6 +1258,7 @@ const {
getListPageAcceptedSubStatus,
getPendingReceiveCounts,
getSuspendCounts,
// getCancelledCounts,
loadStatusTreeCounts,
handlePendingAcceptTabClick: handlePendingAcceptTabClickRaw,
toggleExpand,
......@@ -1110,8 +1266,6 @@ const {
getQueryPayload,
currentPage: statusCurrentPageRef,
pageSize: statusPageSizeRef,
onClearTableState: () => {},
onRefreshCurrentView: () => {},
})
// 查询状态树
const statusTreeChildren = computed((): StatusTreeNode[] => {
......@@ -1167,6 +1321,7 @@ const {
subLoading,
activeTab,
productList,
operationOrderList,
logList,
tableRef,
selectedRows,
......@@ -1195,6 +1350,7 @@ const {
getQueryPayload: getListQueryPayload,
getListPageAcceptedSubStatus,
suspendedSubTab,
cancelledSubTab,
})
statusCurrentPageRef.value = currentPage.value
statusPageSizeRef.value = pageSize.value
......@@ -1269,6 +1425,9 @@ const refreshCurrentView = (options?: { isRefreshTree?: boolean }) => {
if (status.value === 'SUSPEND') {
void getSuspendCounts()
}
// if (status.value === 'CANCELLED') {
// void getCancelledCounts()
// }
refreshTableList()
}
refreshCurrentViewProxy.value = () => refreshCurrentView()
......@@ -1287,12 +1446,29 @@ const receiverAddressForm = ref<AddressInfo>({
receiverPostCode: '',
})
const showPendingLogisticsUpdateAddress = (row: FactoryOrderNewListData) =>
status.value === 'PENDING_CREATE_LOGISTICS' && row.shipmentType === 1
const showPendingLogisticsUpdateAddress = () =>
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) =>
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) => {
receiverAddressForm.value = {
rfcNumber: '',
......@@ -1312,25 +1488,33 @@ const openUpdateReceiverAddress = (row: FactoryOrderNewListData) => {
const handleRefreshReceiverAddress = async (row: FactoryOrderNewListData) => {
try {
await ElMessageBox.confirm('确定刷新地址吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
await ElMessageBox.confirm(
row.shipmentType === 0 ? '确定同步物流/地址吗?' : '确定同步地址吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
} catch {
return
}
await syncReceiverAddress([row.id])
await ElMessageBox.alert(
'请修改/刷新地址后取消物流或者更换物流再重新创建物流订单、获取跟踪号、获取打印面单',
'提示',
{
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消',
showCancelButton: true,
},
)
if (row.shipmentType === 1) {
await ElMessageBox.alert(
'请修改/刷新地址后取消物流或者更换物流再重新创建物流订单、获取跟踪号、获取打印面单',
'提示',
{
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消',
showCancelButton: true,
},
)
} else {
ElMessage.success('操作成功')
}
refreshCurrentView({ isRefreshTree: true })
}
......@@ -1342,6 +1526,9 @@ const handleStatusNodeClick = (node: { status: string }) => {
if (node.status !== 'SUSPEND') {
suspendedSubTab.value = 1
}
if (node.status !== 'CANCELLED') {
cancelledSubTab.value = 1
}
clearTableState()
if (!isSpecialLayout.value) {
refreshCurrentView()
......@@ -1358,6 +1545,13 @@ const handleSuspendTabClick = (value: number) => {
statusCurrentPageRef.value = 1
refreshTableList()
}
// const handleCancelledTabClick = (value: number) => {
// if (cancelledSubTab.value === value) return
// cancelledSubTab.value = value
// currentPage.value = 1
// statusCurrentPageRef.value = 1
// refreshTableList()
// }
const mainColumns = computed(() => [
{
prop: 'factoryOrderNumber',
......@@ -1484,6 +1678,8 @@ const mainColumns = computed(() => [
<span>{row?.receiverProvince || ''}</span>
<span>{row?.receiverCity || ''}</span>
<span>{row?.receiverDistrict || ''}</span>
<span>{row?.receiverAddress1 || ''}</span>
<span>{row?.receiverAddress2 || ''}</span>
</div>
)
},
......@@ -1512,7 +1708,7 @@ const mainColumns = computed(() => [
{
prop: 'operation',
label: '操作',
minWidth: 200,
minWidth: 300,
align: 'center',
slot: 'operation',
fixed: 'right',
......@@ -1524,6 +1720,143 @@ const showProductInventoryColumns = computed(
status.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(() => [
{
......@@ -2064,7 +2397,6 @@ const handlePrinterChange = (value: string) => {
const handleWarehouseIdChange = (value: string) => {
localStorage.setItem('localNewWarehouseId', JSON.stringify(value))
}
// 复用 podCN 的单件打单组件:为 factoryOrderNew 注入差异部分
const mapOrderParamListToSubmitItems = (
orderParamList: { id: number; dataVersion?: number }[],
) => orderParamList.map((item) => ({ id: item.id }))
......@@ -2424,19 +2756,11 @@ const handleProductionComplete = async () => {
getIds: getSelectedIds,
api: completeDeliveryApi,
confirmText: '确定生产完成吗?',
refreshTree: true,
onSuccess: (res) => {
const data = Array.isArray(res.data)
? (res.data as ResultInfoDataItem[])
: []
const hasFailed = data.some((item) => !item.status)
if (hasFailed) {
resultInfo.value = data.filter((item) => !item.status)
resultRefs.value?.showDialog()
} else {
ElMessage.success('操作成功')
refreshCurrentView({ isRefreshTree: true })
}
openResultInfoDialog(data)
},
})
}
......@@ -2575,11 +2899,82 @@ const getLogisticsWay = async () => {
const { data } = await getLogisticsWayApi()
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(() => {
loadStatusTreeCounts()
loadAllDictionaries()
getLogisticsWay()
// 仅用于消除 TS 插件的 unused-vars 警告:template refs 会绑定这两个变量
getReceiverCountryList()
loadWarehouseList()
void treeRef.value
void tableRef.value
if (status.value === 'PENDING_RECEIVE') {
......
......@@ -13,7 +13,7 @@ import {
import {
getListCraftApi,
getProductionClientApi,
getOrderList,
getOrderTrackingList,
getOperationLogApi,
getOrderTabData,
getfaceSimplexFileApi,
......@@ -44,7 +44,7 @@ import { convertToChinaTime } from '@/utils/index'
import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts'
import { useValue } from '@/utils/hooks/useValue'
const tabName = ref<string>('1')
const [searchForm, resetSearchForm] = useValue<SearchForm>({
timeType: 1,
shopNumber: '',
......@@ -539,7 +539,7 @@ const getOrderListFn = async () => {
})
const { currentPage, pageSize } = pagination.value
try {
const { data } = await getOrderList(
const { data } = await getOrderTrackingList(
{
...searchForm.value,
startTime:
......@@ -711,6 +711,12 @@ function changeChinaTime(zone: string) {
})
}
const tabChange = () => {
if(tabName.value==='2'){
operationLog()
}
}
const handleRadioGroupClick = (event: Event) => {
const target = event.target as HTMLElement
const radioButton = target.closest('.el-radio-button')
......@@ -790,14 +796,6 @@ useEnterKeyTrigger({
},
})
watch(
() => currentRowId.value,
(newValue, oldValue) => {
if (newValue !== oldValue) {
operationLog()
}
},
)
loadTabData()
loadProductionClient()
getUserMark()
......@@ -1198,8 +1196,8 @@ onMounted(() => {
</template>
<template #other>
<div class="bottom-table">
<el-tabs>
<el-tab-pane label="商品明细">
<el-tabs v-model="tabName" @tab-change="tabChange">
<el-tab-pane name="1" label="商品明细">
<TableView
ref="goodsRef"
v-loading="goodsLoading"
......@@ -1212,7 +1210,7 @@ onMounted(() => {
</template>
</TableView>
</el-tab-pane>
<el-tab-pane label="操作日志" @tab-click="operationLog">
<el-tab-pane name="2" label="操作日志" >
<div>
<el-button
:type="zoneType === 'Asia/Shanghai' ? 'primary' : ''"
......
......@@ -65,7 +65,14 @@
</template>
<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 isChangeWayLoading = ref(false)
const changeWayRow = ref<LogisticsData>({} as LogisticsData)
......@@ -76,13 +83,13 @@ const wayDialogTitle = ref('')
const props = withDefaults(
defineProps<{
rowData: PodCnOrderListData | null
rowData: ChangeWayRowData | null
getTableFn?: (...args: any[]) => any
submitFn?: (...args: any[]) => any
changeLogisticsApi?: (...args: any[]) => any
}>(),
{
rowData: () => ({} as PodCnOrderListData),
rowData: () => ({} as ChangeWayRowData),
getTableFn: () => {},
submitFn: () => {},
changeLogisticsApi: () => {},
......
......@@ -4,13 +4,27 @@ import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
import compression from 'vite-plugin-compression'
export default defineConfig({
base: '/',
server:{
proxy: {
'/api/': {
target: 'http://172.16.17.244:8060',
},
},
},
plugins: [
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(),
AutoImport({
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