Commit 0a7f6649 by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !155
parents de4b50da a80a850e
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.7", "axios": "^1.6.7",
"bignumber": "^1.1.0",
"bignumber.js": "^9.3.0", "bignumber.js": "^9.3.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^6.0.0", "echarts": "^6.0.0",
...@@ -3171,6 +3172,14 @@ ...@@ -3171,6 +3172,14 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/bignumber": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/bignumber/-/bignumber-1.1.0.tgz",
"integrity": "sha512-EGqHCKkEAwVwufcEOCYhZQqdVH+7cNCyPZ9yxisYvSjHFB+d9YcGMvorsFpeN5IJpC+lC6K+FHhu8+S4MgJazw==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/bignumber.js": { "node_modules/bignumber.js": {
"version": "9.3.0", "version": "9.3.0",
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.0.tgz", "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.0.tgz",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.7", "axios": "^1.6.7",
"bignumber": "^1.1.0",
"bignumber.js": "^9.3.0", "bignumber.js": "^9.3.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^6.0.0", "echarts": "^6.0.0",
......
...@@ -5,6 +5,8 @@ import { LogisticBill } from '@/types/api/podMakeOrder' ...@@ -5,6 +5,8 @@ import { LogisticBill } from '@/types/api/podMakeOrder'
import { userData } from '@/types/api/user' import { userData } from '@/types/api/user'
import { VersionImageList } from '@/types/api/typesetting' import { VersionImageList } from '@/types/api/typesetting'
import { SupplierItem, WarehouseListData } from '@/types'
import { loactionData } from './warehouse'
// 获取物流公司 // 获取物流公司
export function getLogisticsCompanyList() { export function getLogisticsCompanyList() {
...@@ -49,3 +51,38 @@ export function getLogisticCNApi(content: string) { ...@@ -49,3 +51,38 @@ export function getLogisticCNApi(content: string) {
`factory/podJomallOrder/getOrderByThirdSubOrderNumber?thirdSubOrderNumber=${content}`, `factory/podJomallOrder/getOrderByThirdSubOrderNumber?thirdSubOrderNumber=${content}`,
) )
} }
// 获取仓库列表
export function loadWarehouseListApi() {
return axios.get<never, BaseRespData<WarehouseListData[]>>(
'factoryWarehouseInfo/getAll',
)
}
// 获取用户列表
export function getEmployeeListApi() {
return axios.get<never, BaseRespData<userData[]>>(`/factory/factoryUser/list`)
}
// 获取供应商列表
export function getSupplierListApi() {
return axios.get<never, BaseRespData<SupplierItem[]>>(
`/factory/supplier/list`,
)
}
// 获取币种接口
export function getBaseCurrencyInfoApi() {
return axios.get<never, BaseRespData<never>>(
'factory/supplier/getBaseCurrencyInfo',
)
}
// 获取库位List
export function LocationInfoGetAll(wareHouseId?: string | number) {
return axios.get<never, BaseRespData<loactionData[]>>(
'/factoryWarehouseLocation/getByWareHouse',
{
params: { wareHouseId },
},
)
}
import axios from './axios' import axios from './axios'
import { BasePaginationData, BaseRespData } from '@/types/api' import { BasePaginationData, BaseRespData } from '@/types/api'
import { SearchForm, OutOfStockItem } from '@/types/api/outOfStockStatistics' import { SearchForm, OutOfStockItem } from '@/types/api/supply/outOfStockStatistics'
export function getOutOfStockStatisticsListApi( export function getOutOfStockStatisticsListApi(
data: SearchForm, data: SearchForm,
currentPage: number, currentPage: number,
......
...@@ -5,7 +5,6 @@ import { ...@@ -5,7 +5,6 @@ import {
SearchForm, SearchForm,
Tab, Tab,
LogListData, LogListData,
WarehouseListData,
LogisticsData, LogisticsData,
ExportParams, ExportParams,
IconfirmSubmit, IconfirmSubmit,
...@@ -39,10 +38,6 @@ export function syncReceiverAddress(data: number[]) { ...@@ -39,10 +38,6 @@ export function syncReceiverAddress(data: number[]) {
) )
} }
export function getEmployeeListApi() {
return axios.get(`/factory/factoryUser/list`)
}
// 播种墙配货 扫码放入箱子 // 播种墙配货 扫码放入箱子
export function getPackingCnDataApi( export function getPackingCnDataApi(
code: string, code: string,
...@@ -419,11 +414,7 @@ export function getLogisticsCalculation(id: number) { ...@@ -419,11 +414,7 @@ export function getLogisticsCalculation(id: number) {
{ params: { id } }, { params: { id } },
) )
} }
export function loadWarehouseListApi() {
return axios.get<never, BaseRespData<WarehouseListData[]>>(
'factoryWarehouseInfo/getAll',
)
}
export function refreshMaterialApi(data: { export function refreshMaterialApi(data: {
orderIds?: string orderIds?: string
productIds?: string productIds?: string
......
...@@ -6,7 +6,6 @@ import { ...@@ -6,7 +6,6 @@ import {
Tab, Tab,
LogListData, LogListData,
ProductionClient, ProductionClient,
WarehouseListData,
LogisticsData, LogisticsData,
ExportParams, ExportParams,
InterceptStateGroupData, InterceptStateGroupData,
...@@ -286,10 +285,7 @@ export function clearBoxApi( ...@@ -286,10 +285,7 @@ export function clearBoxApi(
}, },
) )
} }
export function clearAllBoxApi( export function clearAllBoxApi(warehouseId: string | number, factoryNo: string | number | undefined) {
warehouseId: string | number,
factoryNo: string | number | undefined,
) {
return axios.get<never, BaseRespData<never>>( return axios.get<never, BaseRespData<never>>(
'factory/podJomallOrderUs/delPodBoxOrderDetails', 'factory/podJomallOrderUs/delPodBoxOrderDetails',
{ {
...@@ -310,11 +306,6 @@ export function getLogisticsCalculation(id: number) { ...@@ -310,11 +306,6 @@ export function getLogisticsCalculation(id: number) {
{ params: { id } }, { params: { id } },
) )
} }
export function loadWarehouseListApi() {
return axios.get<never, BaseRespData<WarehouseListData[]>>(
'factoryWarehouseInfo/getAll',
)
}
export function refreshMaterialApi(data: { export function refreshMaterialApi(data: {
orderIds?: string orderIds?: string
productIds?: string productIds?: string
...@@ -640,11 +631,6 @@ export function getAccountCodeByFactoryIdApi(params: { token: string }) { ...@@ -640,11 +631,6 @@ export function getAccountCodeByFactoryIdApi(params: { token: string }) {
export function getLogisticsWayApi() { export function getLogisticsWayApi() {
return axios.get(`logisticsWay/usableAllList`) return axios.get(`logisticsWay/usableAllList`)
} }
export function getEmployeeListApi() {
return axios.get(`/factory/factoryUser/list`)
}
// 打印拣货单item // 打印拣货单item
export function printPickPdfByBatchNumberApi(params: { export function printPickPdfByBatchNumberApi(params: {
batchArrangeNumber: string batchArrangeNumber: string
......
import axios from '../axios'
import { BasePaginationData, BaseRespData } from '@/types/api'
import {
SearchForm,
TableData,
TreeData,
AddStockingOrderForm,
StockingOrderProduct,
InternalMemoList,
RelatedDocumentList,
LogListData,
} from '@/types/api/supply/stockingOrder'
export function getStockingOrderListApi(
data: SearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<TableData>>(
'factory/supply/stockingUpManage/list_page',
{ ...data, currentPage, pageSize },
)
}
export function getStockingOrderStatusTreeApi() {
return axios.get<never, BaseRespData<TreeData[]>>(
'factory/supply/stockingUpManage/getStatusTree',
)
}
// 新增备货单
export function addStockingOrderApi(url: string, data: AddStockingOrderForm) {
return axios.post<never, BaseRespData<never>>(url, data)
}
// 根据父SKU查询商品信息
export function getProductBySkuApi(spu: string, supplierId: number) {
return axios.get<never, BaseRespData<StockingOrderProduct[]>>(
'factory/supply/stockingUpManage/getCustomProductItem',
{
params: { productItemSku: spu, supplierId },
},
)
}
export function getStockingOrderDetailByIdApi(id: number) {
return axios.get<never, BaseRespData<AddStockingOrderForm>>(
`factory/supply/stockingUpManage/get?id=${id}`,
)
}
export function getStockingOrderDetailListByIdApi(id: number) {
return axios.get<never, BaseRespData<StockingOrderProduct[]>>(
`factory/supply/stockingUpManage/getManageDetailsList?id=${id}`,
)
}
export function getStockingOrderRelatedDocumentListByIdApi(id: number) {
return axios.get<never, BaseRespData<RelatedDocumentList[]>>(
`factory/supply/stockingUpManage/getWarehouseApplyList?id=${id}`,
)
}
export function getStockingOrderInternalMemoListByIdApi(id: number) {
return axios.get<never, BaseRespData<InternalMemoList[]>>(
`factory/supply/stockingUpManage/getInternalMemoList?id=${id}`,
)
}
export function getStockingOrderLogListByIdApi(id: number) {
return axios.get<never, BaseRespData<LogListData[]>>(
`factory/supply/stockingUpManageLog/getStockingUpManageLog?manageId=${id}`,
)
}
export function submitStockingOrderAuditApi(ids: string) {
return axios.get<never, BaseRespData<void>>(
'factory/supply/stockingUpManage/submission',
{
params: { ids },
},
)
}
export function rejectedStockingOrderApi(params: {
id: number | string
turnDownReason: string
}) {
return axios.get<never, BaseRespData<void>>(
'factory/supply/stockingUpManage/rejected',
{
params,
},
)
}
export function supplierDispatchApi(data: {
manageId: number | string
manageNo: string
warehouseId: number | string
warehouseName: string
expectDeliveryTime: string
currencyCode: string
detailsList: StockingOrderProduct[]
}) {
return axios.post<never, BaseRespData<void>>(
'factory/supply/stockingUpWarehouseApply/add',
data,
)
}
export function stockingCompleteApi(id: number) {
return axios.get<never, BaseRespData<void>>(
`factory/supply/stockingUpManage/delivery?id=${id}`,
)
}
export function deleteStockingOrderApi(id: string | number) {
return axios.get<never, BaseRespData<void>>(
`factory/supply/stockingUpManage/delete`,
{
params: { id },
},
)
}
export function cancelStockingOrderApi(
id: string | number,
cancelReason: string,
) {
return axios.get<never, BaseRespData<void>>(
`factory/supply/stockingUpManage/cancel`,
{
params: { id, cancelReason },
},
)
}
export function addStockingOrderInternalTagApi(memo: string, idList: number[]) {
return axios.post<never, BaseRespData<void>>(
`factory/supply/stockingUpManage/batchAddInternalMemo`,
{
content: memo,
idList,
},
)
}
...@@ -45,12 +45,7 @@ export function getProductInfoBySpuApi(spu: string | number) { ...@@ -45,12 +45,7 @@ export function getProductInfoBySpuApi(spu: string | number) {
}, },
) )
} }
// 获取币种接口
export function getBaseCurrencyInfoApi() {
return axios.get<never, BaseRespData<never>>(
'factory/supplier/getBaseCurrencyInfo',
)
}
//新增 //新增
export function addSupplierApi(params: IsupplierType) { export function addSupplierApi(params: IsupplierType) {
return axios.post<never, BaseRespData<never>>('/factory/supplier/add', params) return axios.post<never, BaseRespData<never>>('/factory/supplier/add', params)
......
...@@ -284,14 +284,6 @@ export function warehouseInfoGetAll() { ...@@ -284,14 +284,6 @@ export function warehouseInfoGetAll() {
'/factoryWarehouseInfo/getAll', '/factoryWarehouseInfo/getAll',
) )
} }
export function LocationInfoGetAll(wareHouseId?: string | number) {
return axios.get<never, BaseRespData<loactionData[]>>(
'/factoryWarehouseLocation/getByWareHouse',
{
params: { wareHouseId },
},
)
}
export function createWarehouseInventoryApi(data: WarehouseWarningData) { export function createWarehouseInventoryApi(data: WarehouseWarningData) {
return axios.post<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
...@@ -447,6 +439,21 @@ export function getBySkuApi( ...@@ -447,6 +439,21 @@ export function getBySkuApi(
}, },
) )
} }
export function getBySkuAndUserMarkApi(
warehouseId: number | string | undefined,
sku: string | null,
userMark: string | null | undefined,
) {
return axios.post<never, BaseRespData<InterskuList[]>>(
'factory/warehouseInRecord/getProductBySku',
{
warehouseId,
userMark,
sku,
},
)
}
export function getByWareHouseIdAndCodeApi( export function getByWareHouseIdAndCodeApi(
wareHouseId: number | string | undefined, wareHouseId: number | string | undefined,
code: string | null, code: string | null,
...@@ -710,6 +717,14 @@ export function scanBoxCodeApi(boxCode: string) { ...@@ -710,6 +717,14 @@ export function scanBoxCodeApi(boxCode: string) {
}, },
) )
} }
// 备货计划 盘点
export function getLocalFactoryList() {
return axios.get<never, BaseRespData<never>>(
'dbDiyUser/getLocalFactoryList',
)
}
// 备货计划 打印箱贴 // 备货计划 打印箱贴
export function printBarcodeApi(data: { id: number, dataVersion: number }[]) { export function printBarcodeApi(data: { id: number, dataVersion: number }[]) {
return axios.post<never, BaseRespData<never[]>>( return axios.post<never, BaseRespData<never[]>>(
......
import axios from '@/api/axios'
import { BasePaginationData, BaseRespData } from '@/types/api'
import { LogListData } from '@/types/api/supply/stockingOrder'
import {
SearchForm,
TableData,
TreeData,
StockingApplyOrderDetailList,
RelatedDocumentList,
StockingApplyOrderDetailData,
LocationDataBySkuData,
} from '@/types/api/warehouse/stockingApplyOrder'
export function getStockingApplyOrderListApi(
data: SearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<TableData>>(
'factory/supply/stockingUpWarehouseApply/list_page',
{ ...data, currentPage, pageSize },
)
}
export function getStockingApplyOrderTreeApi() {
return axios.get<never, BaseRespData<TreeData[]>>(
'factory/supply/stockingUpWarehouseApply/getStatusTree',
)
}
export function getStockingApplyOrderDetailListByIdApi(id: number) {
return axios.get<never, BaseRespData<StockingApplyOrderDetailList[]>>(
`factory/supply/stockingUpWarehouseApply/getWarehouseApplyList?id=${id}`,
)
}
export function getStockingApplyOrderRelatedDocumentListByIdApi(
warehouseApplyNo: string,
) {
return axios.get<never, BaseRespData<RelatedDocumentList[]>>(
`factory/supply/stockingUpWarehouseApply/getWarehouseInList?warehouseApplyNo=${warehouseApplyNo}`,
)
}
export function getStockingApplyOrderLogListByIdApi(id: number) {
return axios.get<never, BaseRespData<LogListData[]>>(
`factory/supply/stockingUpWarehouseApplyLog/getWarehouseApplyLog?warehouseApplyId=${id}`,
)
}
export function warehouseReceiptApi(id: number) {
return axios.get<never, BaseRespData<void>>(
`factory/supply/stockingUpWarehouseApply/receiving?id=${id}`,
)
}
export function submitWarehousingApi(data: StockingApplyOrderDetailData) {
return axios.post<never, BaseRespData<void>>(
'factory/supply/stockingUpWarehouseApply/submitInventory',
data,
)
}
export function getStockingApplyOrderDetailById(id: number) {
return axios.get<never, BaseRespData<StockingApplyOrderDetailData>>(
`factory/supply/stockingUpWarehouseApply/get?id=${id}`,
)
}
export function getLocationListApi(sku: string, warehouseId: string | number) {
return axios.post<never, BaseRespData<LocationDataBySkuData[]>>(
'factoryWarehouseInventory/getBySkuAndWarehouseId',
{
sku,
warehouseId,
},
)
}
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<ElTable <ElTable
ref="tableRef" ref="tableRef"
:data="paginatedData" :data="paginatedData"
:show-overflow-tooltip="showOverflowTooltip"
border border
:stripe="stripe" :stripe="stripe"
header-align="center" header-align="center"
...@@ -114,6 +115,10 @@ const props = defineProps({ ...@@ -114,6 +115,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
showOverflowTooltip: {
type: Boolean,
default: true,
},
}) })
const attrs = useAttrs() const attrs = useAttrs()
......
...@@ -167,7 +167,14 @@ const router = createRouter({ ...@@ -167,7 +167,14 @@ const router = createRouter({
meta: { meta: {
title: '缺货统计', title: '缺货统计',
}, },
component: () => import('@/views/supply/OutOfStockStatistics.vue') component: () => import('@/views/supply/OutOfStockStatistics.vue'),
},
{
path: '/supply/stocking-order',
meta: {
title: '备货订单',
},
component: () => import('@/views/supply/stockingOrder/index.vue'),
}, },
{ {
path: '/system/delivery-note', path: '/system/delivery-note',
...@@ -296,6 +303,14 @@ const router = createRouter({ ...@@ -296,6 +303,14 @@ const router = createRouter({
component: stockingPlan, component: stockingPlan,
}, },
{ {
path: '/warehouse/stocking-application',
meta: {
title: '入库申请单',
},
component: () =>
import('@/views/warehouse/stockingApplicationOrder/index.vue'),
},
{
path: '/warehouse/warning', path: '/warehouse/warning',
meta: { meta: {
title: '仓库预警', title: '仓库预警',
......
...@@ -11,11 +11,6 @@ const menu: MenuItem[] = [ ...@@ -11,11 +11,6 @@ const menu: MenuItem[] = [
id: 1, id: 1,
label: '概览', label: '概览',
}, },
// {
// index: '/product',
// id: 11,
// label: '商品',
// },
{ {
index: '4', index: '4',
id: 7, id: 7,
...@@ -74,6 +69,11 @@ const menu: MenuItem[] = [ ...@@ -74,6 +69,11 @@ const menu: MenuItem[] = [
label: '库存', label: '库存',
children: [ children: [
{ {
index: '/warehouse/stocking-application',
id: 126,
label: '入库申请单',
},
{
index: '/warehouse/warning', index: '/warehouse/warning',
id: 125, id: 125,
label: '仓库预警', label: '仓库预警',
...@@ -159,8 +159,13 @@ const menu: MenuItem[] = [ ...@@ -159,8 +159,13 @@ const menu: MenuItem[] = [
id: 1, id: 1,
}, },
{ {
index: '/supply/supplierManagement', label: '备货订单',
index: '/supply/stocking-order',
id: 2, id: 2,
},
{
index: '/supply/supplierManagement',
id: 3,
label: '供应商管理', label: '供应商管理',
}, },
], ],
...@@ -238,17 +243,5 @@ const menu: MenuItem[] = [ ...@@ -238,17 +243,5 @@ const menu: MenuItem[] = [
}, },
], ],
}, },
// {
// index: '',
// id: 3,
// label: '生产管理',
// children: [
// {
// index: '/production/complete',
// id: 4,
// label: '生产完成',
// }
// ]
// },
] ]
export default menu export default menu
...@@ -116,4 +116,4 @@ img { ...@@ -116,4 +116,4 @@ img {
} }
.mb-10 { .mb-10 {
margin-bottom: 10px; margin-bottom: 10px;
} }
\ No newline at end of file
...@@ -32,6 +32,15 @@ ...@@ -32,6 +32,15 @@
color: #409eff !important; color: #409eff !important;
} }
} }
.el-form-item {
margin-right: 10px !important;
}
:root { :root {
--el-menu-icon-width: 0px; --el-menu-icon-width: 0px;
} }
.el-loading-mask.is-fullscreen {
z-index: 5000 !important;
}
.el-loading-mask {
z-index: 1020 !important;
}
\ No newline at end of file
...@@ -194,16 +194,7 @@ export interface PodOrderRes extends ProductList { ...@@ -194,16 +194,7 @@ export interface PodOrderRes extends ProductList {
note?: Array<{ prop: string | number; value: string | number }> note?: Array<{ prop: string | number; value: string | number }>
imgList: cardImages[] imgList: cardImages[]
} }
export interface WarehouseListData {
code?: string
defaulted?: number
factoryCode?: string
factoryId?: number
id: number
name?: string
remarks?: string
sort?: number
}
export interface LogisticsData { export interface LogisticsData {
logisticsWayName: string // 物流名称 logisticsWayName: string // 物流名称
warehouseName: string // 发货仓库 warehouseName: string // 发货仓库
......
export interface TreeData {
name: string
code: string
children?: TreeData[]
countQuantity?: number
id?: number
}
export interface SearchForm {
currentPage: number
endDate?: string
pageSize: number
shippingStatusList?: string[]
startDate?: string
status?: string
stockingUpManageNo?: string
stockingUpUserId?: string[]
supplierId?: string[]
warehouseId?: string | number[]
warehouseSku?: string | number[]
warehouseSkuName?: string
remark?: string
}
export interface TableData {
id: number
factoryId?: number
factoryCode?: string
stockingUpManageNo?: string
currencyCode?: string
skuTotal?: number
total?: number
totalPrice?: number
buyStored?: number
storedPrice?: number
buyUnstored?: number
unstoragePrice?: number
shipmentQuantity?: number
rejectsAmount?: number
shipmentFreight?: number
expectDeliveryTime?: string
status?: string
supplierId?: number
supplierName?: string
warehouseId?: number
warehouseName?: string
stockingUpUserId?: number
stockingUpUserName?: string
createUserId?: number
createUserName?: string
createTime?: string
updateTime?: string
remark?: string
dataVersion?: number
shippingStatus?: number
delayDays?: number
lastDeliveryTime?: string
completeTime?: string
auditName?: string
auditTime?: string
}
export interface StockProduct {
skuImage?: string
productName?: string
styleNumber?: string
warehouseSku?: string
stockQuantity?: number
stockUnitPrice?: number
stockAmount?: number
unshippedQuantity?: number
shippedQuantity?: number
inWarehouseQuantity?: number
defectiveQuantity?: number
}
export interface AddStockingOrderForm {
id?: number | string
stockingUpManageNo?: string
supplierId?: number
supplierName?: string
stockingUpUserId?: number
stockingUpUserName?: string
warehouseId?: number
warehouseName?: string
expectDeliveryTime?: string
totalPrice?: string
currencyCode?: string
currencyName?: string
remark?: string
status?: number
submission?: boolean
detailsList?: StockingOrderProduct[]
examineStatus?: number // 审核状态:1通过,0驳回
rejectReason?: string // 驳回原因
}
export interface StockingOrderProduct {
productNo?: string
warehouseSkuId?: number
warehouseSku?: string
warehouseSkuName?: string
warehouseSkuImage?: string
buyAmount?: number | string
price?: number | string
currencyCode?: string
currencyName?: string
totalPrice?: number | string
shipmentQuantity?: number | string
buyStored?: number
// 供应商发货相关字段
remainingQuantity?: number // 剩余待发货数量
currentShipQuantity?: number | string // 本次发货数量
}
export interface RelatedDocumentList {
id: number
factoryId?: number
factoryCode?: string
manageId?: number
manageNo?: string
warehouseApplyNo?: string
warehouseId?: number
warehouseName?: string
skuTotal?: number
total?: number
buyStored?: number
rejectsAmount?: number
storeStatus?: string
expectDeliveryTime?: string
createUserId?: number
createUserName?: string
createTime?: string
updateTime?: string
dataVersion?: number
}
export interface InternalMemoList {
id: number
outId?: number
operatorEmployeeName?: string
operatorTime?: string
content?: string
remark?: string
type?: number
}
export interface LogListData {
id: number
manageId?: number
userId?: number
userName?: string
description?: string
updateTime?: string
}
...@@ -61,6 +61,8 @@ export interface InterWarehouseBase { ...@@ -61,6 +61,8 @@ export interface InterWarehouseBase {
export interface InterWarehousePage extends InterWarehouseBase { } export interface InterWarehousePage extends InterWarehouseBase { }
// 子表列表ts // 子表列表ts
export interface InterWarehouseDetail extends InterWarehouseBase { export interface InterWarehouseDetail extends InterWarehouseBase {
currencyName?:string
currencyCode?:string
productList: InterProductList[] productList: InterProductList[]
} }
export interface ExportInWarehouseInfo extends warehouseSearchForm { export interface ExportInWarehouseInfo extends warehouseSearchForm {
...@@ -88,7 +90,7 @@ export interface InterProductList { ...@@ -88,7 +90,7 @@ export interface InterProductList {
remark?: string | null remark?: string | null
skuImage?: string skuImage?: string
skuName?: string skuName?: string
costPrice?: number | null costPrice?: number | null | undefined
totalPrice?: number | null totalPrice?: number | null
updateTime?: string updateTime?: string
warehouseSku?: string warehouseSku?: string
...@@ -102,7 +104,13 @@ export interface InterskuList { ...@@ -102,7 +104,13 @@ export interface InterskuList {
id?: number id?: number
productId?: number productId?: number
sku?: string sku?: string
userMark?: string
customerId?: string
customerName?: string
skuName?: string skuName?: string
productName?: string
skuImage?: string
costPrice?: undefined | null | number
image?: string image?: string
factoryPrice?: number | null factoryPrice?: number | null
productNo?: string | null //货号 productNo?: string | null //货号
......
export interface SearchForm {
warehouseApplyNo?: string
manageNo?: string
warehouseId?: string | number[]
warehouseSku?: string | number[]
warehouseSkuName?: string
currentPage: number
pageSize: number
storeStatus?: string
}
export interface TreeData {
id?: number
code?: string
name?: string
countQuantity?: number
children?: TreeData[]
leaf?: boolean
}
export interface TableData {
id: number
factoryId?: number
factoryCode?: string
manageId?: number
manageNo?: string
warehouseApplyNo: string
warehouseId?: number
warehouseName?: string
skuTotal?: number
total?: number
buyStored?: number
rejectsAmount?: number
storeStatus: string
expectDeliveryTime?: string
createUserId?: number
createUserName?: string
createTime?: string
updateTime?: string
dataVersion?: number
}
export interface StockingApplyOrderDetailList {
id: number
manageId?: number
warehouseApplyId?: number
warehouseApplyNo?: string
warehouseSku?: string
productNo?: string
warehouseSkuName?: string
warehouseSkuImage?: string
shipmentQuantity?: number
buyStored?: number | string
rejectsAmount?: number | string
price?: number
createTime?: string
updateTime?: string
locationId?: number
locationCode?: string | undefined
finallyShipmentQuantity?: number
storedPending?: number
}
export interface RelatedDocumentList {
id: number
dataVersion?: number
createTime?: string
updateTime?: string
factoryId?: number
factoryCode?: string
warehouseId?: number
warehouseName?: string
inNo?: string
skuAmount?: number
total?: number
totalPrice?: number
billStatus?: string
billStatusTxt?: string
makerName?: string
makerUserId?: number
makeTime?: string
checkerUserId?: number
checkerName?: string
source?: string
sourceOn?: string
}
export interface StockingApplyOrderDetailData {
id: number
warehouseApplyNo?: string
warehouseName?: string
warehouseId?: number
checkerUserId?: number
remark?: string
checkerName?: string
detailsList: StockingApplyOrderDetailList[]
}
export interface LocationDataBySkuData {
id: number
factoryId?: number
warehouseId?: number
warehouseName?: string
locationId?: number
locationCode?: string
warehouseSku?: string
customSku?: string
skuName?: string
createTime?: string
updateTime?: string
productItem?: unknown
usableInventory?: number
image?: string
price?: number
currencyCode?: string
currencyName?: string
sumOccupyInventory?: number
}
// 放系统中公共类型
export interface WarehouseListData {
code?: string
defaulted?: number
factoryCode?: string
factoryId?: number
id: number
name?: string
remarks?: string
sort?: number
}
export interface SupplierItem {
id: number
supplierName?: string
contacts?: string
contactsNumber?: string
address?: string
remark?: string
factoryId?: number
updateTime?: string
createTime?: string
supplierProductInfoList?: []
}
export interface CurrencyCodeData {
currencyName?: string
currencyCode?: string
id?: string
}
...@@ -86,10 +86,10 @@ import { ...@@ -86,10 +86,10 @@ import {
getTiktokCarrier, getTiktokCarrier,
} from '@/api/logistics' } from '@/api/logistics'
import type { FormItemRule } from 'element-plus' import type { FormItemRule } from 'element-plus'
import { WarehouseListData } from '@/types/api/podUsOrder' import { WarehouseListData } from '@/types'
import { ISeachFormConfig } from '@/types/searchType' import { ISeachFormConfig } from '@/types/searchType'
import { TableColumn } from '@/components/VxeTable' import { TableColumn } from '@/components/VxeTable'
import { loadWarehouseListApi } from '@/api/podUsOrder' import { loadWarehouseListApi } from '@/api/common'
import type { import type {
LogisticsMethod, LogisticsMethod,
platformObj, platformObj,
......
...@@ -935,6 +935,7 @@ onMounted(() => { ...@@ -935,6 +935,7 @@ onMounted(() => {
<el-radio-button value="single">单面</el-radio-button> <el-radio-button value="single">单面</el-radio-button>
<el-radio-button value="multiple">多面</el-radio-button> <el-radio-button value="multiple">多面</el-radio-button>
<el-radio-button value="normal">普品</el-radio-button> <el-radio-button value="normal">普品</el-radio-button>
<el-radio-button value="custom_normal">胚衣</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem label="数量"> <ElFormItem label="数量">
......
...@@ -974,6 +974,7 @@ onMounted(() => { ...@@ -974,6 +974,7 @@ onMounted(() => {
<el-radio-button value="single">单面</el-radio-button> <el-radio-button value="single">单面</el-radio-button>
<el-radio-button value="multiple">多面</el-radio-button> <el-radio-button value="multiple">多面</el-radio-button>
<el-radio-button value="normal">普品</el-radio-button> <el-radio-button value="normal">普品</el-radio-button>
<el-radio-button value="custom_normal">胚衣</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem label="数量"> <ElFormItem label="数量">
......
...@@ -282,7 +282,7 @@ import { ...@@ -282,7 +282,7 @@ import {
} from '@/api/podCnOrder' } from '@/api/podCnOrder'
const { getCLodop } = useLodop() const { getCLodop } = useLodop()
import useOrderStore from '@/store/cnOrder' import useOrderStore from '@/store/cnOrder'
import { WarehouseListData } from '@/types/api/podUsOrder' import { WarehouseListData } from '@/types/index'
const orderStore = useOrderStore() const orderStore = useOrderStore()
const boxList = computed(() => orderStore.podBoxList) const boxList = computed(() => orderStore.podBoxList)
const podOrderDetailsColumns = computed(() => [ const podOrderDetailsColumns = computed(() => [
......
...@@ -279,7 +279,7 @@ import { ...@@ -279,7 +279,7 @@ import {
import useUserStore from '@/store/user' import useUserStore from '@/store/user'
import { Check, Refresh } from '@element-plus/icons-vue' import { Check, Refresh } from '@element-plus/icons-vue'
import socket from '@/utils/cnWebsocket' import socket from '@/utils/cnWebsocket'
import { WarehouseListData } from '@/types/api/podUsOrder' import { WarehouseListData } from '@/types/index'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { filePath } from '@/api/axios.ts' import { filePath } from '@/api/axios.ts'
...@@ -554,7 +554,7 @@ const renderItemBox = (bool: boolean) => { ...@@ -554,7 +554,7 @@ const renderItemBox = (bool: boolean) => {
parts.length > 3 && parts[3].startsWith('CNPSC') parts.length > 3 && parts[3].startsWith('CNPSC')
? parts[3] ? parts[3]
: parts.length > 1 : parts.length > 1
? parts[1] ? currentCode
: parts[0] : parts[0]
} }
......
...@@ -237,6 +237,7 @@ ...@@ -237,6 +237,7 @@
<el-radio-button label="single">单面</el-radio-button> <el-radio-button label="single">单面</el-radio-button>
<el-radio-button label="multiple">多面</el-radio-button> <el-radio-button label="multiple">多面</el-radio-button>
<el-radio-button label="normal">普品</el-radio-button> <el-radio-button label="normal">普品</el-radio-button>
<el-radio-button value="custom_normal">胚衣</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="status !== 'BATCH_DOWNLOAD'" label="数量"> <ElFormItem v-if="status !== 'BATCH_DOWNLOAD'" label="数量">
...@@ -1162,6 +1163,7 @@ ...@@ -1162,6 +1163,7 @@
:cell-style="onCellStyle" :cell-style="onCellStyle"
:row-style="getRowStyle" :row-style="getRowStyle"
:row-class-name="getRowClassName" :row-class-name="getRowClassName"
:show-overflow-tooltip="false"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<template #serialNumber="{ row, index }"> <template #serialNumber="{ row, index }">
...@@ -1487,13 +1489,15 @@ ...@@ -1487,13 +1489,15 @@
</div> </div>
</div> </div>
<div class="goods-item-info">
<div class="goods-item-info">
<div class="goods-item-info-item"> <div class="goods-item-info-item">
<span class="goods-item-info-item-label" <span class="goods-item-info-item-label"
>英文报关名称:</span >英文报关名称:</span
> >
<span :title="item.customsNameEnglish" class="goods-item-info-item-value"> <span
:title="item.customsNameEnglish"
class="goods-item-info-item-value"
>
{{ item.customsNameEnglish }} {{ item.customsNameEnglish }}
</span> </span>
</div> </div>
...@@ -1501,7 +1505,10 @@ ...@@ -1501,7 +1505,10 @@
<span class="goods-item-info-item-label" <span class="goods-item-info-item-label"
>中文报关名称:</span >中文报关名称:</span
> >
<span :title="item.customsNameChinese" class="goods-item-info-item-value"> <span
:title="item.customsNameChinese"
class="goods-item-info-item-value"
>
{{ item.customsNameChinese }} {{ item.customsNameChinese }}
</span> </span>
</div> </div>
...@@ -1509,7 +1516,7 @@ ...@@ -1509,7 +1516,7 @@
<span class="goods-item-info-item-label" <span class="goods-item-info-item-label"
>申报重量(g):</span >申报重量(g):</span
> >
<span :title="item.customsWeight" class="goods-item-info-item-value"> <span :title="item.customsWeight" class="goods-item-info-item-value">
{{ item.customsWeight }} {{ item.customsWeight }}
</span> </span>
</div> </div>
...@@ -1517,12 +1524,14 @@ ...@@ -1517,12 +1524,14 @@
<span class="goods-item-info-item-label" <span class="goods-item-info-item-label"
>申报价值($):</span >申报价值($):</span
> >
<span :title="item.customsValue" class="goods-item-info-item-value"> <span
{{ item.customsValue }} :title="item.customsValue"
class="goods-item-info-item-value"
>
{{ item.customsValue }}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
...@@ -2908,7 +2917,6 @@ import { ...@@ -2908,7 +2917,6 @@ import {
printPrintOrderApi, printPrintOrderApi,
stockOutCheckApi, stockOutCheckApi,
toBePickingApi, toBePickingApi,
loadWarehouseListApi,
getLogisticsCalculation, getLogisticsCalculation,
refreshProductInformationApi, refreshProductInformationApi,
composingDesignImages, composingDesignImages,
...@@ -2946,10 +2954,11 @@ import { ...@@ -2946,10 +2954,11 @@ import {
printNormalPdf, printNormalPdf,
printNormalProducePdf, printNormalProducePdf,
changeLogisticsApi, changeLogisticsApi,
getEmployeeListApi,
allErpCodeListApi, allErpCodeListApi,
updateProductOutOfStockApi, updateProductOutOfStockApi,
} from '@/api/podCnOrder' } from '@/api/podCnOrder'
import { loadWarehouseListApi, getEmployeeListApi } from '@/api/common'
// import { logisticsCompanyAllCodelist } from '@/api/logistics.ts' // import { logisticsCompanyAllCodelist } from '@/api/logistics.ts'
import { BaseRespData } from '@/types/api' import { BaseRespData } from '@/types/api'
...@@ -2968,7 +2977,6 @@ import { ...@@ -2968,7 +2977,6 @@ import {
ProductionClient, ProductionClient,
SearchForm, SearchForm,
Tab, Tab,
WarehouseListData,
cardImages, cardImages,
AddressInfo, AddressInfo,
LogisticsData, LogisticsData,
...@@ -2976,6 +2984,7 @@ import { ...@@ -2976,6 +2984,7 @@ import {
CraftListData, CraftListData,
ExportParams, ExportParams,
} from '@/types/api/podCnOrder' } from '@/types/api/podCnOrder'
import { WarehouseListData } from '@/types'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
import { showConfirm } from '@/utils/ui' import { showConfirm } from '@/utils/ui'
...@@ -3002,6 +3011,7 @@ import { ...@@ -3002,6 +3011,7 @@ import {
type RouteLocationNormalized, type RouteLocationNormalized,
} from 'vue-router' } from 'vue-router'
import UpdateCustomDeclarationInfoDialog from './components/UpdateCustomDeclarationInfoDialog.vue' import UpdateCustomDeclarationInfoDialog from './components/UpdateCustomDeclarationInfoDialog.vue'
import { userData } from '@/types/api/user.ts'
declare global { declare global {
interface Window { interface Window {
...@@ -3077,7 +3087,7 @@ const sourceList = [ ...@@ -3077,7 +3087,7 @@ const sourceList = [
}, },
] ]
const sizes = ['FS', 'XS', 'S', 'M', 'L', 'XL', 'XXL', '3XL', '4XL', '5XL'] const sizes = ['FS', 'XS', 'S', 'M', 'L', 'XL', 'XXL', '3XL', '4XL', '5XL']
const employeeList = ref<{ account: string; id: number }[]>([]) const employeeList = ref<userData[]>([])
const exportData = () => { const exportData = () => {
exportVisible.value = true exportVisible.value = true
...@@ -3616,38 +3626,40 @@ const interceptChange = async (status: boolean) => { ...@@ -3616,38 +3626,40 @@ const interceptChange = async (status: boolean) => {
} }
} }
const rejectOrder = async (type: string) => { const rejectOrder = async (type: string) => {
if (selection.value.length === 0 && cardSelection.value.length === 0) { const targets = selection.value.length
return ElMessage.warning('请选择数据') ? selection.value.flatMap((v: PodCnOrderListData) => v.productList || [])
} : cardSelection.value
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
if (!targets.length) return ElMessage.warning('请选择数据')
let reason = ''
try { try {
const { value } = await ElMessageBox.prompt('驳回确认', { const { value } = await ElMessageBox.prompt<string>('驳回确认', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
inputType: 'textarea', inputType: 'textarea',
inputPlaceholder: '驳回原因', inputPlaceholder: '驳回原因',
inputPattern: /\S+/, // 非空验证,至少一个非空白字符 inputPattern: /\S+/,
inputErrorMessage: '内容不能为空', inputErrorMessage: '内容不能为空',
}) })
reason = value.trim()
} catch {
return
}
// const loading = ElLoading.service({
// fullscreen: true,
// text: '操作中...',
// background: 'rgba(0, 0, 0, 0.3)',
// })
try {
const res = await rejectToApi({ const res = await rejectToApi({
orderStatus: type, orderStatus: type,
productList: selection.value.length productList: targets,
? selection.value.flatMap( reasonStr: reason,
(item: PodCnOrderListData) => item.productList || [],
)
: cardSelection.value,
reasonStr: value,
}) })
if (res.code !== 200) return
resultInfo.value = res.data if (res.code !== 200) return
resultInfo.value = resultInfo.value.filter((item) => !item.status) resultInfo.value = (res.data || []).filter((item) => !item.status)
if (!resultInfo.value.length) { if (!resultInfo.value.length) {
ElMessage.success('操作成功') ElMessage.success('操作成功')
search() search()
...@@ -3657,9 +3669,10 @@ const rejectOrder = async (type: string) => { ...@@ -3657,9 +3669,10 @@ const rejectOrder = async (type: string) => {
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} finally {
loading.close()
} }
// finally {
// loading.close()
// }
} }
// 拦截分页 // 拦截分页
const handleInterceptionCommand = (current: number, command: number) => { const handleInterceptionCommand = (current: number, command: number) => {
...@@ -3940,7 +3953,7 @@ const tableColumns = computed(() => { ...@@ -3940,7 +3953,7 @@ const tableColumns = computed(() => {
{ {
label: '操作', label: '操作',
slot: 'operate', slot: 'operate',
width: 120, width: 260,
align: 'center', align: 'center',
fixed: 'right', fixed: 'right',
prop: 'operate', prop: 'operate',
...@@ -3984,7 +3997,7 @@ const tableColumns = computed(() => { ...@@ -3984,7 +3997,7 @@ const tableColumns = computed(() => {
{ {
label: '操作', label: '操作',
slot: 'operate', slot: 'operate',
width: 100, width: 260,
align: 'center', align: 'center',
fixed: 'right', fixed: 'right',
prop: 'operate', prop: 'operate',
...@@ -5117,7 +5130,6 @@ const getOrderByIdApi = async (type: string) => { ...@@ -5117,7 +5130,6 @@ const getOrderByIdApi = async (type: string) => {
Fn: (orderIds: (string | number)[]) => Promise<BaseRespData<never>> Fn: (orderIds: (string | number)[]) => Promise<BaseRespData<never>>
} }
} = { } = {
getTrackingNumber: { getTrackingNumber: {
message: '获取跟踪号', message: '获取跟踪号',
Fn: getTrackingNumberApi, Fn: getTrackingNumberApi,
...@@ -5578,7 +5590,9 @@ const getRowStyle = ({ row }: { row: PodCnOrderListData }) => { ...@@ -5578,7 +5590,9 @@ const getRowStyle = ({ row }: { row: PodCnOrderListData }) => {
// 获取行类名方法 // 获取行类名方法
const getRowClassName = ({ row }: { row: PodCnOrderListData }) => { const getRowClassName = ({ row }: { row: PodCnOrderListData }) => {
return selection.value.some((item) => item.id === row.id) ? 'row-selected' : '' return selection.value.some((item) => item.id === row.id)
? 'row-selected'
: ''
} }
const warehouseList = ref<WarehouseListData[]>([]) const warehouseList = ref<WarehouseListData[]>([])
const loadWarehouseList = async () => { const loadWarehouseList = async () => {
...@@ -6082,12 +6096,14 @@ const handleCreateLogistic = () => { ...@@ -6082,12 +6096,14 @@ const handleCreateLogistic = () => {
}) })
} }
const handleShowResult = (data: Array<{ const handleShowResult = (
id: string | number data: Array<{
status: boolean id: string | number
factoryOrderNumber?: string status: boolean
message: string factoryOrderNumber?: string
}>) => { message: string
}>,
) => {
resultInfo.value = data || [] resultInfo.value = data || []
if (resultInfo.value.length > 0) { if (resultInfo.value.length > 0) {
resultRefs.value?.showDialog() resultRefs.value?.showDialog()
...@@ -6223,7 +6239,7 @@ const onUpdateCustomsDeclarationInfo = () => { ...@@ -6223,7 +6239,7 @@ const onUpdateCustomsDeclarationInfo = () => {
.goods-item { .goods-item {
display: grid; display: grid;
// grid-template-columns: 100px 1fr minmax(150px, 1fr) 150px; // grid-template-columns: 100px 1fr minmax(150px, 1fr) 150px;
grid-template-columns: 100px 254px 1fr minmax(150px, 1fr) 150px 200px; grid-template-columns: 100px 254px 1fr minmax(150px, 1fr) 220px 200px;
gap: 15px; gap: 15px;
.goods-item-img { .goods-item-img {
......
...@@ -267,7 +267,7 @@ import { ...@@ -267,7 +267,7 @@ import {
import useUserStore from '@/store/user' import useUserStore from '@/store/user'
import { Check, Refresh } from '@element-plus/icons-vue' import { Check, Refresh } from '@element-plus/icons-vue'
import socket from '@/utils/websocket' import socket from '@/utils/websocket'
import { WarehouseListData } from '@/types/api/podUsOrder' import { WarehouseListData } from '@/types/index'
import { filePath } from '@/api/axios.ts' import { filePath } from '@/api/axios.ts'
import { ElButton, ElIcon } from 'element-plus' import { ElButton, ElIcon } from 'element-plus'
......
...@@ -227,6 +227,7 @@ ...@@ -227,6 +227,7 @@
<el-radio-button label="single">单面</el-radio-button> <el-radio-button label="single">单面</el-radio-button>
<el-radio-button label="multiple">多面</el-radio-button> <el-radio-button label="multiple">多面</el-radio-button>
<el-radio-button label="normal">普品</el-radio-button> <el-radio-button label="normal">普品</el-radio-button>
<el-radio-button value="custom_normal">胚衣</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="status !== 'BATCH_DOWNLOAD'" label="数量"> <ElFormItem v-if="status !== 'BATCH_DOWNLOAD'" label="数量">
...@@ -1211,6 +1212,7 @@ ...@@ -1211,6 +1212,7 @@
:cell-class-name="onCellClassName" :cell-class-name="onCellClassName"
:row-style="getRowStyle" :row-style="getRowStyle"
:row-class-name="getRowClassName" :row-class-name="getRowClassName"
:show-overflow-tooltip="false"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<template #serialNumber="{ row, index }"> <template #serialNumber="{ row, index }">
...@@ -3161,7 +3163,7 @@ ...@@ -3161,7 +3163,7 @@
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
import LogisticsWaySelect from '../../logistics/components/LogisticsWaySelect.tsx' import LogisticsWaySelect from '../../logistics/components/LogisticsWaySelect.tsx'
import { getUserMarkList } from '@/api/common' import { getUserMarkList, loadWarehouseListApi } from '@/api/common'
// import { AnyObject } from '@/types/api/warehouse' // import { AnyObject } from '@/types/api/warehouse'
import uploadBox from '@/components/uploadBox.vue' import uploadBox from '@/components/uploadBox.vue'
import { convertToChinaTime } from '@/utils/index' import { convertToChinaTime } from '@/utils/index'
...@@ -3194,7 +3196,6 @@ import { ...@@ -3194,7 +3196,6 @@ import {
printPrintOrderApi, printPrintOrderApi,
stockOutCheckApi, stockOutCheckApi,
toBePickingApi, toBePickingApi,
loadWarehouseListApi,
getLogisticsCalculation, getLogisticsCalculation,
refreshMaterialApi, refreshMaterialApi,
getTrackingNumberApi, getTrackingNumberApi,
...@@ -3240,7 +3241,6 @@ import { ...@@ -3240,7 +3241,6 @@ import {
getLogisticsWayApi, getLogisticsWayApi,
printNormalPickPdfApi, printNormalPickPdfApi,
updatePRNDownloadStatus, updatePRNDownloadStatus,
getEmployeeListApi,
updateProductOutOfStockApi, updateProductOutOfStockApi,
} from '@/api/podUsOrder' } from '@/api/podUsOrder'
import { BaseRespData } from '@/types/api' import { BaseRespData } from '@/types/api'
...@@ -3248,7 +3248,7 @@ import { BaseRespData } from '@/types/api' ...@@ -3248,7 +3248,7 @@ import { BaseRespData } from '@/types/api'
import UpdateAddress from './components/updateAddress.vue' import UpdateAddress from './components/updateAddress.vue'
import { getAllCountryApi } from '@/api/logistics.ts' import { getAllCountryApi } from '@/api/logistics.ts'
import { getEmployeeListApi } from '@/api/common'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import { import {
LogListData, LogListData,
...@@ -3257,7 +3257,6 @@ import { ...@@ -3257,7 +3257,6 @@ import {
ProductionClient, ProductionClient,
SearchForm, SearchForm,
Tab, Tab,
WarehouseListData,
cardImages, cardImages,
AddressInfo, AddressInfo,
LogisticsData, LogisticsData,
...@@ -3293,6 +3292,7 @@ import platformJson from '../../../json/platform.json' ...@@ -3293,6 +3292,7 @@ import platformJson from '../../../json/platform.json'
import { getToken } from '@/api/axios' import { getToken } from '@/api/axios'
import usePermissionBtnStore from '@/store/permission' import usePermissionBtnStore from '@/store/permission'
import ReissueOrderComponent from './components/ReissueOrder.vue' import ReissueOrderComponent from './components/ReissueOrder.vue'
import { WarehouseListData } from '@/types'
const permissionBtns = usePermissionBtnStore() const permissionBtns = usePermissionBtnStore()
...@@ -3302,6 +3302,7 @@ import { ...@@ -3302,6 +3302,7 @@ import {
type RouteLocationNormalized, type RouteLocationNormalized,
} from 'vue-router' } from 'vue-router'
import PrintWarehouseSkuTag from '@/views/order/components/printWarehouseSkuTag.vue' import PrintWarehouseSkuTag from '@/views/order/components/printWarehouseSkuTag.vue'
import { userData } from '@/types/api/user.ts'
declare global { declare global {
interface Window { interface Window {
...@@ -3339,7 +3340,7 @@ const printWarehouseSkuDialogRef = ref() ...@@ -3339,7 +3340,7 @@ const printWarehouseSkuDialogRef = ref()
const isAuto = ref(true) const isAuto = ref(true)
const countryList = ref([]) const countryList = ref([])
const logisticsWayList = ref<{ name: string; id: number }[]>([]) const logisticsWayList = ref<{ name: string; id: number }[]>([])
const employeeList = ref<{ account: string; id: number }[]>([]) const employeeList = ref<userData[]>([])
const currentRow = ref<AddressInfo>({ const currentRow = ref<AddressInfo>({
receiverName: '', receiverName: '',
receiverPhone: '', receiverPhone: '',
...@@ -5593,38 +5594,38 @@ const getOrderByIdApi = async (type: string) => { ...@@ -5593,38 +5594,38 @@ const getOrderByIdApi = async (type: string) => {
// 驳回 // 驳回
const rejectOrder = async (type: string) => { const rejectOrder = async (type: string) => {
if (selection.value.length === 0 && cardSelection.value.length === 0) { const targets = selection.value.length
return ElMessage.warning('请选择数据') ? selection.value.flatMap((v: PodUsOrderListData) => v.productList || [])
} : cardSelection.value
const loading = ElLoading.service({ if (!targets.length) return ElMessage.warning('请选择数据')
fullscreen: true, let reason = ''
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try { try {
const { value } = await ElMessageBox.prompt('驳回确认', { const { value } = await ElMessageBox.prompt<string>('驳回确认', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
inputType: 'textarea', inputType: 'textarea',
inputPlaceholder: '驳回原因', inputPlaceholder: '驳回原因',
inputPattern: /\S+/, // 非空验证,至少一个非空白字符 inputPattern: /\S+/,
inputErrorMessage: '内容不能为空', inputErrorMessage: '内容不能为空',
}) })
reason = value.trim()
} catch {
return
}
// const loading = ElLoading.service({
// fullscreen: true,
// text: '操作中...',
// background: 'rgba(0, 0, 0, 0.3)',
// })
try {
const res = await rejectToApi({ const res = await rejectToApi({
orderStatus: type, orderStatus: type,
productList: selection.value.length productList: targets,
? selection.value.flatMap( reasonStr: reason,
(item: PodUsOrderListData) => item.productList || [],
)
: cardSelection.value,
reasonStr: value,
}) })
if (res.code !== 200) return if (res.code !== 200) return
resultInfo.value = (res.data || []).filter((item) => !item.status)
resultInfo.value = res.data
resultInfo.value = resultInfo.value.filter((item) => !item.status)
if (!resultInfo.value.length) { if (!resultInfo.value.length) {
ElMessage.success('操作成功') ElMessage.success('操作成功')
search() search()
...@@ -5634,9 +5635,10 @@ const rejectOrder = async (type: string) => { ...@@ -5634,9 +5635,10 @@ const rejectOrder = async (type: string) => {
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} finally {
loading.close()
} }
// finally {
// loading.close()
// }
} }
const handleStockOut = async (row: PodUsOrderListData) => { const handleStockOut = async (row: PodUsOrderListData) => {
......
...@@ -107,9 +107,9 @@ ...@@ -107,9 +107,9 @@
import { computed, ref, onMounted } from 'vue' import { computed, ref, onMounted } from 'vue'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { loadWarehouseListApi } from '@/api/podCnOrder' import { loadWarehouseListApi } from '@/api/common'
import type { WarehouseListData } from '@/types/api/podCnOrder' import { WarehouseListData } from '@/types'
import { SearchForm, OutOfStockItem } from '@/types/api/outOfStockStatistics' import { SearchForm, OutOfStockItem } from '@/types/api/supply/outOfStockStatistics'
import { import {
getOutOfStockStatisticsListApi, getOutOfStockStatisticsListApi,
exportOutOfStockStatisticsListApi, exportOutOfStockStatisticsListApi,
......
<template>
<ElDialog
v-model="visible"
:title="
editOrderType === 'add'
? '新增备货单'
: editOrderType === 'edit'
? '编辑备货单'
: '审核备货单'
"
width="1400px"
top="15vh"
:close-on-click-modal="false"
destroy-on-close
>
<div class="add-stocking-order">
<div class="form-section">
<ElForm
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
inline
>
<div class="form-row">
<ElFormItem label="备货单号" class="form-item">
<ElInput
v-model="formData.stockingUpManageNo"
disabled
placeholder="系统自动生成"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="供应商" prop="supplierId" class="form-item">
<ElSelect
v-model="formData.supplierId"
placeholder="请选择"
filterable
clearable
:disabled="editOrderType === 'audit'"
style="width: 100%"
@change="handleSupplierChange"
>
<ElOption
v-for="item in supplierList"
:key="item.id"
:label="item.supplierName"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem
label="备货员"
prop="stockingUpUserId"
class="form-item"
>
<ElSelect
v-model="formData.stockingUpUserId"
placeholder="请选择"
filterable
:disabled="editOrderType === 'audit'"
clearable
style="width: 100%"
>
<ElOption
v-for="item in stockKeeperList"
:key="item.id"
:label="item.account"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="备货仓库" prop="warehouseId" class="form-item">
<ElSelect
v-model="formData.warehouseId"
placeholder="请选择"
filterable
:disabled="editOrderType === 'audit'"
clearable
style="width: 100%"
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
</div>
<div class="form-row">
<ElFormItem label="期望交货日期" class="form-item">
<ElDatePicker
v-model="formData.expectDeliveryTime"
type="datetime"
:disabled="editOrderType === 'audit'"
placeholder="请选择"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="备货总额" class="form-item">
<ElInput
v-model="totalAmount"
disabled
placeholder="0"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="币种" class="form-item">
<ElSelect
v-model="formData.currencyCode"
placeholder="请选择"
filterable
clearable
disabled
style="width: 100%"
>
<ElOption
v-for="item in currencyList"
:key="item.id"
:label="`${item.currencyName}(${item.currencyCode})`"
:value="item.currencyCode"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="备注" class="form-item">
<ElInput
v-model="formData.remark"
placeholder="请输入备注"
clearable
:disabled="editOrderType === 'audit'"
style="width: 100%"
/>
</ElFormItem>
</div>
</ElForm>
</div>
<div class="table-section">
<TableView
:paginated-data="formData.detailsList"
:serial-numberable="true"
:columns="tableColumns"
:selectionable="true"
@selection-change="handleSelectionChange"
>
<template #buyAmount="{ row }">
<ElInput
v-model="row.buyAmount"
clearable
:disabled="editOrderType === 'audit'"
size="small"
style="width: 80px"
@blur="validateField(row, 'buyAmount')"
/>
<div v-if="row._buyAmountError" class="field-error">
{{ row._buyAmountError }}
</div>
</template>
<template #price="{ row }">
<ElInput
v-model="row.price"
clearable
:disabled="editOrderType === 'audit'"
size="small"
style="width: 80px"
:class="{ 'is-error': row._priceError }"
@blur="validateField(row, 'price')"
/>
<div v-if="row._priceError" class="field-error">
{{ row._priceError }}
</div>
</template>
</TableView>
</div>
<div
v-if="editOrderType === 'add' || editOrderType === 'edit'"
class="add-sku-section"
>
<span class="label">添加SKU</span>
<ElInput
v-model="productSku"
placeholder="商品父SKU"
clearable
style="width: 200px"
@keyup.enter="handleSearchSku"
/>
<ElPopover
ref="popoverRef"
v-model:visible="popoverVisible"
placement="top-start"
:width="900"
trigger="manual"
popper-class="sku-search-popover"
>
<template #reference>
<ElButton type="primary" @click="handleSearchSku">查询</ElButton>
</template>
<div class="popover-table">
<TableView
ref="searchTableRef"
:paginated-data="productList"
:columns="searchTableColumns"
:serial-numberable="true"
>
<template #operation="{ row }">
<ElButton
type="success"
circle
size="small"
@click="handleAddSingleProduct(row)"
>
<ElIcon><Plus /></ElIcon>
</ElButton>
</template>
</TableView>
</div>
</ElPopover>
<ElButton type="danger" @click="handleDeleteSelected">删除</ElButton>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton size="large" @click="handleCancel">取消</ElButton>
<ElButton
v-if="editOrderType === 'add' || editOrderType === 'edit'"
size="large"
type="primary"
@click="handleSave(false)"
>保存</ElButton
>
<ElButton
v-if="editOrderType === 'add' || editOrderType === 'edit'"
size="large"
type="success"
@click="handleSaveAndSubmit"
>
保存并提交审核
</ElButton>
<ElButton
v-if="editOrderType === 'audit'"
size="large"
type="success"
@click="handleSave(false)"
>审核通过</ElButton
>
<ElButton
v-if="editOrderType === 'audit'"
size="large"
type="danger"
@click="handleReject"
>审核驳回</ElButton
>
</div>
</template>
</ElDialog>
</template>
<script setup lang="tsx">
import { ref, computed } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import type {
AddStockingOrderForm,
StockingOrderProduct,
TableData,
} from '@/types/api/supply/stockingOrder'
import type { CurrencyCodeData, SupplierItem, WarehouseListData } from '@/types'
import type { userData } from '@/types/api/user'
import {
addStockingOrderApi,
getProductBySkuApi,
getStockingOrderDetailByIdApi,
rejectedStockingOrderApi,
} from '@/api/supplier/stockingOrder'
import { BigNumber } from 'bignumber.js'
const calcRowTotalPrice = (row: StockingOrderProduct): string => {
if (!row.buyAmount || !row.price) return ''
row.totalPrice = new BigNumber(row.buyAmount ?? 0)
.multipliedBy(row.price ?? 0)
.toString()
return row.totalPrice
}
const props = defineProps<{
supplierList: SupplierItem[]
stockKeeperList: userData[]
warehouseList: WarehouseListData[]
currencyList: CurrencyCodeData[]
}>()
const emit = defineEmits<{
(e: 'refresh'): void
}>()
const visible = ref(false)
const editOrderType = ref<string>('add')
const editId = ref<number | undefined>(undefined)
const formRef = ref<FormInstance>()
const formData = ref<AddStockingOrderForm>({
stockingUpManageNo: '',
supplierId: undefined,
stockingUpUserId: undefined,
warehouseId: undefined,
totalPrice: '',
expectDeliveryTime: '',
currencyCode: '',
remark: '',
detailsList: [],
})
type StockingOrderProductWithError = StockingOrderProduct & {
_buyAmountError?: string
_priceError?: string
}
const validateField = (
row: StockingOrderProductWithError,
field: 'buyAmount' | 'price',
) => {
const value = row[field]
const strValue = value === 0 || value === '0' ? '0' : String(value ?? '')
const fieldLabel = field === 'buyAmount' ? '备货数量' : '备货单价'
if (!strValue) {
if (field === 'buyAmount') row._buyAmountError = '必填项'
else row._priceError = '必填项'
} else if (field === 'buyAmount') {
// 备货数量只能输入正整数
if (!/^[1-9]\d*$/.test(strValue)) {
row._buyAmountError = '请输入正整数'
} else {
row._buyAmountError = ''
}
} else if (field === 'price') {
if (!/^\d+(\.\d+)?$/.test(strValue)) {
row._priceError = '请输入有效数字'
} else if (Number(strValue) === 0) {
row._priceError = `${fieldLabel}不能为0`
} else {
row._priceError = ''
}
}
}
const validateDetailsList = (): boolean => {
if (!formData.value.detailsList?.length) {
return true
}
let isValid = true
formData.value.detailsList.forEach((row) => {
const extendedRow = row as StockingOrderProductWithError
validateField(extendedRow, 'buyAmount')
validateField(extendedRow, 'price')
if (extendedRow._buyAmountError || extendedRow._priceError) {
isValid = false
}
})
return isValid
}
const formRules: FormRules = {
supplierId: [{ required: true, message: '请选择供应商', trigger: 'change' }],
stockingUpUserId: [
{ required: true, message: '请选择备货员', trigger: 'change' },
],
warehouseId: [
{ required: true, message: '请选择备货仓库', trigger: 'change' },
],
}
const selectedProducts = ref<StockingOrderProduct[]>([])
const productSku = ref('')
const popoverVisible = ref(false)
const tableColumns = [
{
label: 'SKU图片',
prop: 'skuImage',
width: 180,
align: 'center',
render: (item: StockingOrderProduct) => (
<el-image
preview-teleported
src={item.warehouseSkuImage}
preview-src-list={[item.warehouseSkuImage]}
z-index={9999}
style="width: 60px;height: 60px;text-align: center;"
/>
),
},
{ label: '商品名称', prop: 'warehouseSkuName', minWidth: 150 },
{ label: '款号', prop: 'productNo', align: 'center', width: 120 },
{ label: '库存SKU', prop: 'warehouseSku', align: 'center', width: 180 },
{
label: '备货数量',
prop: 'buyAmount',
slot: 'buyAmount',
width: 120,
align: 'center',
},
{
label: '备货单价',
prop: 'price',
slot: 'price',
width: 120,
align: 'center',
},
{
label: '备货金额',
prop: 'totalPrice',
width: 120,
align: 'center',
render: (item: StockingOrderProduct) => calcRowTotalPrice(item),
},
]
const searchTableColumns = computed(() => {
return [
{
label: 'SKU图片',
prop: 'skuImage',
slot: 'skuImage',
width: 120,
align: 'center',
render: (item: StockingOrderProduct) => (
<el-image
preview-teleported
src={item.warehouseSkuImage}
preview-src-list={[item.warehouseSkuImage]}
z-index={9999}
style="width: 60px;height: 60px;text-align: center;"
/>
),
},
{ label: '商品名称', prop: 'warehouseSkuName', minWidth: 150 },
{ label: '库存SKU', prop: 'warehouseSku', align: 'center', minWidth: 180 },
{ label: '款号', prop: 'productNo', align: 'center', width: 100 },
{ label: '币种', prop: 'currencyCode', align: 'center', width: 80 },
{ label: '成本价', prop: 'price', align: 'center', width: 80 },
{
label: '操作',
prop: 'operation',
slot: 'operation',
width: 70,
align: 'center',
fixed: 'right',
},
]
})
const productList = ref<StockingOrderProduct[]>([])
const totalAmount = computed(() => {
return formData.value.detailsList?.reduce((sum, cur) => {
return new BigNumber(sum || 0)
.plus(new BigNumber(cur.totalPrice || 0).toNumber())
.toString()
}, new BigNumber(0).toString())
})
const handleSelectionChange = (selection: StockingOrderProduct[]) => {
selectedProducts.value = selection
}
const handleSearchSku = async () => {
if (!productSku.value) {
ElMessage.warning('请输入商品父SKU')
popoverVisible.value = false
return
}
if (!formData.value.supplierId) {
ElMessage.warning('请先选择供应商')
popoverVisible.value = false
return
}
try {
const res = await getProductBySkuApi(
productSku.value,
formData.value.supplierId,
)
if (res.code !== 200) {
ElMessage.warning(res.message)
popoverVisible.value = false
return
}
productList.value = res.data || []
if (productList.value.length === 0) {
ElMessage.warning('未查询到商品数据')
popoverVisible.value = false
} else {
popoverVisible.value = true
}
} catch (error) {
console.error(error)
popoverVisible.value = false
}
}
const handleAddSingleProduct = (row: StockingOrderProduct) => {
if (
formData.value.detailsList?.some(
(item) => item.warehouseSku === row.warehouseSku,
)
) {
ElMessage.warning('商品已添加')
return
}
if (
formData.value.currencyCode &&
row.currencyCode !== formData.value.currencyCode
) {
ElMessage.warning('商品币种与主单币种不一致,添加失败!')
return
}
if (!formData.value.currencyCode && row.currencyCode) {
formData.value.currencyCode = row.currencyCode
}
formData.value.detailsList?.push(row)
const index = productList.value.findIndex(
(item: StockingOrderProduct) => item.warehouseSku === row.warehouseSku,
)
if (index !== -1) {
productList.value.splice(index, 1)
}
if (productList.value.length === 0) {
popoverVisible.value = false
}
}
const handleDeleteSelected = () => {
if (selectedProducts.value.length === 0) {
ElMessage.warning('请先选择要删除的商品')
return
}
const selectedSkus = selectedProducts.value.map((item) => item.warehouseSku)
formData.value.detailsList = formData.value.detailsList?.filter(
(item) => !selectedSkus.includes(item.warehouseSku),
)
selectedProducts.value = []
// 如果商品列表为空,清空币种
if (!formData.value.detailsList?.length) {
formData.value.currencyCode = ''
}
}
const handleCancel = () => {
visible.value = false
}
const handleSave = async (isSubmit: boolean = false) => {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch (e) {
return
}
if (!formData.value.detailsList?.length) {
ElMessage.warning('请先添加商品')
return
}
if (!validateDetailsList()) {
ElMessage.warning('请检查备货数量和备货单价是否正确填写')
return
}
const { supplierId, warehouseId, stockingUpUserId } = formData.value
const supplierName = props.supplierList.find(
(item) => item.id === supplierId,
)?.supplierName
const warehouseName = props.warehouseList.find(
(item) => item.id === warehouseId,
)?.name
const stockingUpUserName = props.stockKeeperList.find(
(item) => item.id === stockingUpUserId,
)?.account
const url =
editOrderType.value === 'add'
? 'factory/supply/stockingUpManage/add'
: editOrderType.value === 'edit'
? `factory/supply/stockingUpManage/update`
: `factory/supply/stockingUpManage/examine`
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await addStockingOrderApi(url, {
...formData.value,
id: editId.value,
supplierName,
warehouseName,
stockingUpUserName,
totalPrice: totalAmount.value,
submission: editOrderType.value === 'audit' ? undefined : isSubmit,
})
if (res.code !== 200) {
ElMessage.warning(res.message)
return
}
ElMessage.success('保存成功')
emit('refresh')
visible.value = false
} catch (error) {
console.error(error)
} finally {
loading.close()
}
}
const handleSaveAndSubmit = async () => {
await handleSave(true)
}
const handleReject = async () => {
if (!editId.value) return
try {
const { value: rejectReason } = await ElMessageBox.prompt(
'请输入驳回原因',
'审核驳回',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
inputType: 'textarea',
inputPlaceholder: '请输入驳回原因',
inputValidator: (val) => {
if (!val || !val.trim()) {
return '驳回原因不能为空'
}
return true
},
},
)
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await rejectedStockingOrderApi({
id: editId.value,
turnDownReason: rejectReason.trim(),
})
if (res.code !== 200) return
ElMessage.success('驳回成功')
emit('refresh')
visible.value = false
} finally {
loading.close()
}
} catch {
// 用户取消操作,不做处理
}
}
const resetForm = () => {
formRef.value?.resetFields()
formData.value = {
stockingUpManageNo: '',
supplierId: undefined,
stockingUpUserId: undefined,
warehouseId: undefined,
expectDeliveryTime: '',
currencyCode: '',
totalPrice: '',
remark: '',
detailsList: [],
}
selectedProducts.value = []
productSku.value = ''
productList.value = []
popoverVisible.value = false
editId.value = undefined
editOrderType.value = 'add'
}
const handleSupplierChange = () => {
formData.value.currencyCode = ''
formData.value.detailsList = []
}
const getStockingOrderDetailById = async (id: number) => {
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await getStockingOrderDetailByIdApi(id)
if (res.code !== 200) {
ElMessage.warning(res.message)
return
}
formData.value = res.data
visible.value = true
} catch (error) {
console.error(error)
} finally {
loading.close()
}
}
const open = (row: TableData | null, type: string) => {
if (row) {
productSku.value = ''
editId.value = row.id
editOrderType.value = type
getStockingOrderDetailById(row.id)
} else {
visible.value = true
resetForm()
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.add-stocking-order {
.form-section {
.form-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.form-item {
flex: 1;
min-width: 280px;
}
:deep(.el-select),
:deep(.el-input),
:deep(.el-date-editor) {
width: 160px;
}
}
.table-section {
height: 500px;
.empty-tip {
text-align: center;
color: #909399;
padding: 20px;
}
}
.add-sku-section {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
.label {
font-size: 14px;
color: #606266;
}
}
}
.dialog-footer {
text-align: center;
}
.popover-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
.popover-title {
font-size: 14px;
font-weight: bold;
color: #303133;
margin-right: auto;
}
.close-icon {
cursor: pointer;
color: #909399;
font-size: 18px;
&:hover {
color: #409eff;
}
}
}
.popover-table {
height: 600px;
.sku-image {
width: 50px;
height: 50px;
object-fit: contain;
}
}
.is-error {
:deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #f56c6c inset;
}
}
.field-error {
color: #f56c6c;
font-size: 12px;
line-height: 1;
margin-top: 2px;
}
</style>
<style lang="scss">
.sku-search-popover {
.el-table {
.el-button.is-circle {
padding: 6px;
}
}
}
</style>
<template>
<ElTabs
v-model="activeTab"
v-loading="loading"
class="detail-tabs"
@tab-click="handleTabClick"
>
<ElTabPane name="stockProducts" label="备货商品">
<div class="detail-table-content">
<TableView
:serial-numberable="true"
:columns="stockProductsColumns"
:paginated-data="stockProductsData"
/>
</div>
</ElTabPane>
<ElTabPane name="relatedDocuments" label="关联单据">
<div class="detail-table-content">
<TableView
:serial-numberable="true"
:columns="relatedDocumentsColumns"
:paginated-data="relatedDocumentsData"
/>
</div>
</ElTabPane>
<ElTabPane name="internalMemo" label="内部便签">
<div class="detail-table-content memo-content">
<div class="memo-input-wrapper">
<ElInput
v-model="memoInputValue"
placeholder="请输入内容"
clearable
class="memo-input"
/>
<ElButton type="primary" :loading="addingMemo" @click="handleAddMemo">
添加
</ElButton>
</div>
<div class="memo-list">
<template v-if="internalMemoData.length > 0">
<div
v-for="item in internalMemoData"
:key="item.id"
class="memo-item"
>
<span class="memo-user">{{ item.operatorEmployeeName }}</span>
<span class="memo-text">{{ item.content }}</span>
<span class="memo-time">({{ item.operatorTime }})</span>
</div>
</template>
<div v-else class="empty-content">暂无数据</div>
</div>
</div>
</ElTabPane>
<ElTabPane name="operationLog" label="操作日志">
<div class="detail-table-content">
<LogList :log-list="operationLogData" />
<div class="empty-content">暂无数据</div>
</div>
</ElTabPane>
</ElTabs>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import type { TabsPaneContext } from 'element-plus'
import TableView from '@/components/TableView.vue'
import {
InternalMemoList,
LogListData,
RelatedDocumentList,
StockingOrderProduct,
TableData,
} from '@/types/api/supply/stockingOrder'
import ImageView from '@/components/ImageView.vue'
import {
addStockingOrderInternalTagApi,
getStockingOrderDetailListByIdApi,
getStockingOrderInternalMemoListByIdApi,
getStockingOrderLogListByIdApi,
getStockingOrderRelatedDocumentListByIdApi,
} from '@/api/supplier/stockingOrder'
import { ElMessage } from 'element-plus'
import LogList from '@/components/LogList.vue'
const relatedDocumentsColumns = computed(() => {
return [
{
label: '关联单据',
prop: 'warehouseApplyNo',
align: 'center',
},
{
label: '关联单号',
prop: 'manageNo',
align: 'center',
},
{
label: '制单人',
width: 120,
prop: 'createUserName',
align: 'center',
},
{
label: '制单时间',
width: 160,
prop: 'createTime',
align: 'center',
},
{
label: 'SKU个数',
width: 120,
align: 'right',
prop: 'skuTotal',
},
{
label: '入库数量',
width: 120,
align: 'right',
prop: 'buyStored',
},
{
label: '申请数量',
width: 120,
prop: 'total',
align: 'right',
},
{
label: '预计到货日期',
width: 160,
align: 'center',
prop: 'expectDeliveryTime',
},
{
label: '实际到货日期',
width: 160,
align: 'center',
prop: 'deliveryTime',
},
]
})
const stockProductsColumns = computed(() => {
return [
{
label: 'SKU图片',
prop: 'skuImage',
width: 100,
align: 'center',
render: (item: StockingOrderProduct) => (
<ImageView src={item.warehouseSkuImage} width="50px" height="50px" />
),
},
{
label: '商品名称',
prop: 'warehouseSkuName',
minWidth: 200,
align: 'left',
},
{
label: '款号',
prop: 'productNo',
width: 120,
align: 'center',
},
{
label: '库存SKU',
prop: 'warehouseSku',
width: 180,
align: 'center',
},
{
label: '备货数量',
prop: 'buyAmount',
width: 100,
align: 'right',
},
{
label: '备货单价',
prop: 'price',
width: 100,
align: 'right',
},
{
label: '备货金额',
prop: 'totalPrice',
width: 120,
align: 'right',
},
{
label: '已发货数量',
prop: 'shipmentQuantity',
width: 120,
align: 'right',
},
{
label: '已入库数量',
prop: 'buyStored',
width: 120,
align: 'right',
},
{
label: '不良品数量',
prop: 'rejectsAmount',
width: 120,
align: 'right',
},
]
})
const props = defineProps<{
selectedRow: TableData | null
}>()
const stockProductsData = ref<StockingOrderProduct[]>([])
const relatedDocumentsData = ref<RelatedDocumentList[]>([])
const internalMemoData = ref<InternalMemoList[]>([])
const operationLogData = ref<LogListData[]>([])
const activeTab = ref('stockProducts')
const loading = ref(false)
const memoInputValue = ref('')
const addingMemo = ref(false)
const handleAddMemo = async () => {
if (!memoInputValue.value.trim()) {
ElMessage.warning('请输入内容')
return
}
if (!props.selectedRow) return
addingMemo.value = true
try {
const res = await addStockingOrderInternalTagApi(
memoInputValue.value.trim(),
[props.selectedRow.id],
)
if (res.code === 200) {
ElMessage.success('添加成功')
memoInputValue.value = ''
loadInternalMemo(props.selectedRow)
}
} catch (e) {
console.error(e)
} finally {
addingMemo.value = false
}
}
watch(
() => props.selectedRow,
(newVal) => {
if (newVal) {
if (activeTab.value === 'stockProducts') {
loadStockProducts(newVal)
} else if (activeTab.value === 'relatedDocuments') {
loadRelatedDocuments(newVal)
} else if (activeTab.value === 'internalMemo') {
loadInternalMemo(newVal)
} else if (activeTab.value === 'operationLog') {
loadOperationLog(newVal)
}
} else {
stockProductsData.value = []
relatedDocumentsData.value = []
internalMemoData.value = []
operationLogData.value = []
}
},
)
const loadStockProducts = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingOrderDetailListByIdApi(row.id)
if (res.code !== 200) return
stockProductsData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadRelatedDocuments = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingOrderRelatedDocumentListByIdApi(row.id)
if (res.code !== 200) return
relatedDocumentsData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadInternalMemo = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingOrderInternalMemoListByIdApi(row.id)
if (res.code !== 200) return
internalMemoData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadOperationLog = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingOrderLogListByIdApi(row.id)
if (res.code !== 200) return
operationLogData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const handleTabClick = (tab: TabsPaneContext) => {
activeTab.value = tab.paneName as string
if (!props.selectedRow) return
if (activeTab.value === 'stockProducts') {
loadStockProducts(props.selectedRow as TableData)
} else if (activeTab.value === 'relatedDocuments') {
loadRelatedDocuments(props.selectedRow as TableData)
} else if (activeTab.value === 'internalMemo') {
loadInternalMemo(props.selectedRow as TableData)
} else if (activeTab.value === 'operationLog') {
loadOperationLog(props.selectedRow as TableData)
}
}
</script>
<style lang="scss" scoped>
.detail-tabs {
height: 100%;
display: flex;
:deep(.el-tab-pane) {
height: 100%;
overflow: hidden;
}
}
.detail-table-content {
height: 100%;
overflow: hidden;
}
.empty-content {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #909399;
font-size: 14px;
}
.memo-content {
display: flex;
flex-direction: column;
}
.memo-input-wrapper {
display: flex;
align-items: center;
gap: 12px;
.memo-input {
flex: 1;
max-width: 300px;
}
}
.memo-list {
flex: 1;
overflow-y: auto;
}
.memo-item {
padding: 8px 0;
font-size: 14px;
line-height: 1.5;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.memo-user {
color: #409eff;
}
.memo-text {
color: #303133;
}
.memo-time {
color: #909399;
margin-left: 8px;
}
}
</style>
<template>
<ElDialog
v-model="visible"
title="供应商发货"
width="1400px"
top="15vh"
:close-on-click-modal="false"
destroy-on-close
>
<div class="supplier-dispatch-order-page">
<div class="form-section">
<ElForm ref="formRef" :model="formData" label-width="110px" inline>
<div class="form-row">
<ElFormItem label="备货单号" class="form-item">
<ElInput
v-model="formData.stockingUpManageNo"
disabled
placeholder="系统自动带出"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem
label="备货仓库"
prop="warehouseId"
class="form-item"
:rules="[
{
required: true,
message: '请选择备货仓库',
trigger: 'change',
},
]"
>
<ElSelect
v-model="formData.warehouseId"
placeholder="请选择"
filterable
clearable
style="width: 100%"
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem
label="预计到货日期"
prop="expectDeliveryTime"
class="form-item"
:rules="[
{
required: true,
message: '请选择预计到货日期',
trigger: 'change',
},
]"
>
<ElDatePicker
v-model="formData.expectDeliveryTime"
type="datetime"
placeholder="请选择"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
style="width: 100%"
/>
</ElFormItem>
</div>
</ElForm>
</div>
<div class="table-section">
<TableView
ref="tableViewRef"
:paginated-data="formData.detailsList ?? []"
:serial-numberable="true"
:columns="tableColumns"
:selectionable="true"
@selection-change="handleSelectionChange"
>
<template #currentShipQuantity="{ row }">
<ElInput
v-model="row.currentShipQuantity"
clearable
size="small"
style="width: 100px"
@blur="validateField(row, 'currentShipQuantity')"
/>
<div v-if="row._currentShipQuantityError" class="field-error">
{{ row._currentShipQuantityError }}
</div>
</template>
</TableView>
</div>
<div class="action-section">
<ElButton type="success" @click="handleAutoFillQuantity">
剩余待发货数量>>本次发货数量
</ElButton>
<ElButton type="danger" @click="handleDeleteSelected">删除</ElButton>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton size="large" @click="visible = false">取消</ElButton>
<ElButton size="large" type="danger" @click="handleDispatch">
发货
</ElButton>
</div>
</template>
</ElDialog>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import type { FormInstance } from 'element-plus'
import type {
TableData,
StockingOrderProduct,
AddStockingOrderForm,
} from '@/types/api/supply/stockingOrder'
import type { WarehouseListData } from '@/types'
import {
getStockingOrderDetailByIdApi,
supplierDispatchApi,
} from '@/api/supplier/stockingOrder'
import TableView from '@/components/TableView.vue'
import { BigNumber } from 'bignumber.js'
const props = defineProps<{
warehouseList: WarehouseListData[]
}>()
const emit = defineEmits<{
(e: 'refresh'): void
}>()
const visible = ref(false)
const currentRow = ref<TableData | null>(null)
const formRef = ref<FormInstance>()
const formData = ref<AddStockingOrderForm>({
stockingUpManageNo: '',
warehouseId: undefined as number | undefined,
expectDeliveryTime: '',
detailsList: [],
})
const selectedProducts = ref<StockingOrderProduct[]>([])
const tableColumns = computed(() => [
{
label: 'SKU图片',
prop: 'skuImage',
width: 100,
align: 'center',
render: (item: StockingOrderProduct) => (
<el-image
preview-teleported
src={item.warehouseSkuImage}
preview-src-list={[item.warehouseSkuImage]}
z-index={9999}
style="width: 60px;height: 60px;text-align: center;"
/>
),
},
{
label: '商品名称',
prop: 'warehouseSkuName',
minWidth: 150,
align: 'left',
showOverflowTooltip: true,
},
{
label: '款号',
prop: 'productNo',
width: 120,
align: 'center',
},
{
label: '库存SKU',
prop: 'warehouseSku',
width: 180,
align: 'center',
},
{
label: '备货数量',
prop: 'buyAmount',
width: 100,
align: 'right',
},
{
label: '已入库数量',
prop: 'buyStored',
width: 100,
align: 'right',
render: (item: StockingOrderProduct) => (
<span style="color: #409EFF; font-weight: 500;">
{item.buyStored ?? 0}
</span>
),
},
{
label: '已发货数量',
prop: 'shipmentQuantity',
width: 100,
align: 'right',
render: (item: StockingOrderProduct) => (
<span style="color: #E6A23C; font-weight: 500;">
{item.shipmentQuantity ?? 0}
</span>
),
},
{
label: '剩余待发货数量',
prop: 'remainingQuantity',
width: 140,
align: 'right',
render: (item: StockingOrderProduct) => (
<span style="color: #F56C6C; font-weight: 500;">
{item.remainingQuantity ?? 0}
</span>
),
},
{
label: '本次发货数量',
slot: 'currentShipQuantity',
width: 140,
align: 'center',
},
])
const handleAutoFillQuantity = () => {
if (selectedProducts.value.length === 0) {
ElMessage.warning('请选择要自动填充的商品')
return
}
selectedProducts.value.forEach((item) => {
if (item.remainingQuantity && item.remainingQuantity > 0) {
item.currentShipQuantity = item.remainingQuantity
}
})
}
const handleSelectionChange = (selection: StockingOrderProduct[]) => {
selectedProducts.value = selection
}
const handleDeleteSelected = () => {
if (selectedProducts.value.length === 0) {
ElMessage.warning('请先选择要删除的商品')
return
}
const selectedSkus = selectedProducts.value.map((item) => item.warehouseSku)
formData.value.detailsList = formData.value.detailsList?.filter(
(item) => !selectedSkus.includes(item.warehouseSku),
)
selectedProducts.value = []
}
const validateDetailsList = (): boolean => {
if (!formData.value.detailsList?.length) {
return true
}
let isValid = true
formData.value.detailsList.forEach((row) => {
const extendedRow = row as StockingOrderProductWithError
validateField(extendedRow, 'currentShipQuantity')
if (extendedRow._currentShipQuantityError) {
isValid = false
}
})
return isValid
}
const handleDispatch = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch {
return
}
if (formData.value.detailsList?.length === 0) {
ElMessage.warning('没有可发货的商品')
return
}
if (!validateDetailsList()) {
ElMessage.warning('请检查本次发货数量是否正确填写')
return
}
const overQuantitySkus = (formData.value.detailsList || [])
.filter(
(item) =>
Number(item.currentShipQuantity) > (item.remainingQuantity ?? 0),
)
.map((item) => item.warehouseSku)
if (overQuantitySkus.length > 0) {
try {
await ElMessageBox.alert(
`<div style="line-height: 1.6;"><span style="white-space: nowrap; flex-shrink: 0;">库存SKU:</span><span style="word-break: break-all; margin-left: 4px;">${overQuantitySkus.join(
',',
)}</span><span style="margin-top: 12px;">本次发货数量大于剩余待发货数量,是否确认发货?</span></div>`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'over-quantity-alert-box',
dangerouslyUseHTMLString: true,
},
)
} catch {
return
}
}
const loading = ElLoading.service({
lock: true,
text: '正在提交...',
background: 'rgba(0, 0, 0, 0.7)',
})
const {
stockingUpManageNo,
warehouseId,
expectDeliveryTime,
currencyCode,
detailsList,
} = formData.value
const warehouseName = props.warehouseList.find(
(item) => item.id === warehouseId,
)?.name
try {
const res = await supplierDispatchApi({
manageId: currentRow.value?.id as number,
manageNo: stockingUpManageNo as string,
warehouseId: warehouseId as number,
warehouseName: warehouseName as string,
expectDeliveryTime: expectDeliveryTime as string,
currencyCode: currencyCode as string,
detailsList:
detailsList?.map((e) => ({
...e,
shipmentQuantity: Number(e.currentShipQuantity ?? 0),
currentShipQuantity: undefined,
remainingQuantity: undefined,
})) || [],
})
if (res.code !== 200) return
ElMessage.success('发货成功')
emit('refresh')
visible.value = false
} catch (error) {
console.error(error)
} finally {
loading.close()
}
}
const open = async (row: TableData) => {
if (!row?.id) return
selectedProducts.value = []
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await getStockingOrderDetailByIdApi(row.id)
if (res.code !== 200) {
ElMessage.warning(res.message)
return
}
const {
stockingUpManageNo,
warehouseId,
detailsList,
warehouseName,
currencyCode,
} = res.data
formData.value = {
stockingUpManageNo,
warehouseId,
warehouseName,
currencyCode,
detailsList: detailsList?.map((item) => ({
...item,
remainingQuantity: new BigNumber(item.buyAmount || 0)
.minus(item.shipmentQuantity || 0)
.toNumber(),
currentShipQuantity: undefined,
})),
}
currentRow.value = row
visible.value = true
} catch (error) {
console.error(error)
} finally {
loading.close()
}
}
type StockingOrderProductWithError = StockingOrderProduct & {
_currentShipQuantityError?: string
}
const validateField = (
row: StockingOrderProductWithError,
field: 'currentShipQuantity',
) => {
const value = row[field]
const strValue = value === 0 || value === '0' ? '0' : String(value ?? '')
// const fieldLabel = '本次发货数量'
if (!strValue) {
if (field === 'currentShipQuantity')
row._currentShipQuantityError = '必填项'
} else if (!/^\d+$/.test(strValue)) {
if (field === 'currentShipQuantity')
row._currentShipQuantityError = '只能输入正整数'
} else {
if (field === 'currentShipQuantity') row._currentShipQuantityError = ''
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.supplier-dispatch-order-page {
.form-section {
margin-bottom: 16px;
.form-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.form-item {
flex: 1;
min-width: 280px;
max-width: 350px;
}
:deep(.el-select),
:deep(.el-input),
:deep(.el-date-editor) {
width: 100%;
}
}
.table-section {
height: 450px;
border: 1px solid #ebeef5;
border-radius: 4px;
}
.action-section {
display: flex;
align-items: center;
gap: 10px;
margin-top: 16px;
}
}
.dialog-footer {
text-align: center;
}
.field-error {
color: #f56c6c;
font-size: 12px;
line-height: 1;
margin-top: 2px;
}
</style>
<style>
.over-quantity-alert-box {
width: 1000px !important;
max-width: 90vw !important;
}
.over-quantity-alert-box .el-message-box__message {
word-break: break-all;
line-height: 1.6;
padding: 10px 0;
}
</style>
<template>
<div class="stocking-order-page card h-100 flex overflow-hidden">
<div class="order-status">
<ElTree
ref="treeRef"
default-expand-all
:expand-on-click-node="false"
:default-expanded-keys="[]"
:highlight-current="true"
node-key="code"
:data="treeData"
:props="{ children: 'children', label: 'name' }"
@node-click="nodeClick"
>
<template #default="{ data }">
<div class="tree-node">
<div class="tree-node-label">{{ data.name }}</div>
<div
v-if="data.countQuantity || data.countQuantity === 0"
class="tree-node-count"
>
{{ `(${data.countQuantity})` }}
</div>
</div>
</template>
</ElTree>
</div>
<div class="order-content flex-1 flex-column overflow-hidden">
<div class="header">
<div class="header-search-form">
<ElForm
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
class="search-form"
>
<ElFormItem label="备货仓库" prop="warehouseId">
<ElSelect
v-model="searchForm.warehouseId"
placeholder="请选择"
filterable
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="订单时间" prop="orderTime">
<ElDatePicker
v-model="rangeTime"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</ElFormItem>
<ElFormItem label="备货单号" prop="stockingUpManageNo">
<ElInput
v-model="searchForm.stockingUpManageNo"
clearable
placeholder="备货单号"
/>
</ElFormItem>
<ElFormItem label="供应商" prop="supplierId">
<ElSelect
v-model="searchForm.supplierId"
placeholder="请选择"
filterable
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<ElOption
v-for="item in supplierList"
:key="item.id"
:label="item.supplierName"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="发货状态" prop="shippingStatusList">
<ElSelect
v-model="searchForm.shippingStatusList"
placeholder="请选择"
filterable
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<ElOption
v-for="item in deliveryStatusList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="库存SKU" prop="warehouseSku">
<ElInput
v-model="searchForm.warehouseSku"
clearable
placeholder="库存SKU"
/>
</ElFormItem>
<ElFormItem label="备货员" prop="stockingUpUserId">
<ElSelect
v-model="searchForm.stockingUpUserId"
placeholder="请选择"
filterable
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<ElOption
v-for="item in stockKeeperList"
:key="item.id"
:label="item.account"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="商品名称" prop="warehouseSkuName">
<ElInput
v-model="searchForm.warehouseSkuName"
clearable
placeholder="商品名称"
/>
</ElFormItem>
<ElFormItem label="备注" prop="remark">
<ElInput
v-model="searchForm.remark"
clearable
placeholder="备注或内部便签"
/>
</ElFormItem>
<ElFormItem class="form-item-buttons">
<ElButton type="primary" @click="() => search()">查询</ElButton>
<ElButton @click="() => resetSearchForm()">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="header-operation">
<span v-if="status === 'PENDING_SUBMIT'" class="item">
<ElButton type="success" @click="handleAddOrder">新增</ElButton>
</span>
<span v-if="status === 'PENDING_SUBMIT'" class="item">
<ElButton type="primary" @click="handleSubmitAudit"
>提交审核</ElButton
>
</span>
<span v-if="status !== 'COMPLETED' && status !== 'CANCELLED'" class="item">
<ElButton type="success" @click="addInternalTag"
>添加内部便签</ElButton
>
</span>
<span
v-if="['PENDING_SUBMIT', 'STOCKING_UP'].includes(status)"
class="item"
>
<ElButton type="warning" @click="handleCancelOrder">取消</ElButton>
</span>
<span v-if="status === 'PENDING_SUBMIT'" class="item">
<ElButton type="danger" @click="handleDeleteOrder">删除</ElButton>
</span>
</div>
</div>
<div class="table-content">
<SplitDiv size="60">
<template #top>
<div class="table-list flex-1 overflow-hidden">
<TableView
ref="tableRef"
highlight-current-row
:selectionable="true"
:serial-numberable="true"
:columns="tableColumns"
:paginated-data="tableData"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
/>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[50, 100, 200, 300, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</template>
<template #bottom>
<StockingOrderDetailTabs :selected-row="selectedRow" />
</template>
</SplitDiv>
</div>
</div>
<AddStockingOrderDialog
ref="addStockingOrderDialogRef"
:supplier-list="supplierList"
:stock-keeper-list="stockKeeperList"
:warehouse-list="warehouseList"
:currency-list="currencyList"
@refresh="onRefresh"
/>
<SupplierDispatchOrderDialog
ref="supplierDispatchOrderDialogRef"
:warehouse-list="warehouseList"
@refresh="onRefresh"
/>
</div>
</template>
<script setup lang="tsx">
import { computed, ref } from 'vue'
import dayjs from 'dayjs'
import {
getStockingOrderListApi,
stockingCompleteApi,
submitStockingOrderAuditApi,
deleteStockingOrderApi,
cancelStockingOrderApi,
addStockingOrderInternalTagApi,
} from '@/api/supplier/stockingOrder'
import TableView from '@/components/TableView.vue'
import StockingOrderDetailTabs from './StockingOrderDetailTabs.vue'
import AddStockingOrderDialog from './AddStockingOrderDialog.vue'
import SupplierDispatchOrderDialog from './SupplierDispatchOrderDialog.vue'
import {
TreeData,
SearchForm,
TableData,
} from '@/types/api/supply/stockingOrder'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import { CurrencyCodeData, SupplierItem, WarehouseListData } from '@/types'
import {
getBaseCurrencyInfoApi,
getEmployeeListApi,
getSupplierListApi,
loadWarehouseListApi,
} from '@/api/common'
import { getStockingOrderStatusTreeApi } from '@/api/supplier/stockingOrder'
import { userData } from '@/types/api/user'
import { ElButton, ElTag } from 'element-plus'
const deliveryStatusList = ref([
{ label: '待发货', value: '0' },
{ label: '部分发货', value: '1' },
{ label: '全部发货', value: '2' },
])
const tableColumns = computed(() => {
return [
{
label: '备货单号',
prop: 'stockingUpManageNo',
minWidth: 200,
align: 'center',
render: (row: TableData) => {
const isDelayed =
row.expectDeliveryTime &&
dayjs()
.startOf('day')
.diff(dayjs(row.expectDeliveryTime).startOf('day'), 'day') > 0
return (
<div style="position: relative; width: 100%;">
{isDelayed && (
<ElTag
type="danger"
size="small"
style="position: absolute; left: -10px; top: 2px; transform: scale(0.8);"
>
延期
</ElTag>
)}
<span>{row.stockingUpManageNo}</span>
</div>
)
},
},
{
label: '备货仓库',
prop: 'warehouseName',
width: 120,
align: 'center',
},
{
label: '供应商',
prop: 'supplierName',
width: 120,
align: 'center',
},
{
label: '订单状态',
prop: 'status',
width: 100,
align: 'center',
render: (row: TableData) => {
if (row.status === 'PENDING_SUBMIT') {
return <ElTag type="primary">待提交</ElTag>
}
if (row.status === 'PENDING_AUDIT') {
return <ElTag type="warning">待审核</ElTag>
}
if (row.status === 'STOCKING_UP') {
return <ElTag type="success">备货中</ElTag>
}
if (row.status === 'COMPLETED') {
return <ElTag type="success">备货完成</ElTag>
}
if (row.status === 'CANCELLED') {
return <ElTag type="danger">已取消</ElTag>
}
},
},
{
label: '期望交货日期',
prop: 'expectDeliveryTime',
width: 160,
align: 'center',
},
{
label: 'SKU个数',
prop: 'skuTotal',
width: 100,
align: 'right',
},
{
label: '备货总数',
prop: 'total',
width: 100,
align: 'right',
},
{
label: '备货总额',
prop: 'totalPrice',
width: 120,
align: 'right',
},
{
label: '币种',
prop: 'currencyCode',
width: 80,
align: 'center',
},
{
label: '发货状态',
prop: 'shippingStatus',
width: 100,
align: 'center',
render: (row: TableData) => {
return (
<span>
{row.shippingStatus && (
<ElTag type="primary">
{getDeliveryStatusName(row.shippingStatus)}
</ElTag>
)}
</span>
)
},
},
{
label: '备货员',
prop: 'stockingUpUserName',
width: 100,
align: 'center',
},
{
label: '最后交货日期',
prop: 'lastDeliveryTime',
width: 160,
align: 'center',
},
{
label: '订单完成日期',
prop: 'completeTime',
width: 160,
align: 'center',
},
{
label: '延误天数',
prop: 'delayDays',
width: 100,
align: 'center',
render: (row: TableData) => {
if (!row.expectDeliveryTime) return <span>-</span>
let delayDays: number
if (row.completeTime) {
delayDays = dayjs(row.completeTime)
.startOf('day')
.diff(dayjs(row.expectDeliveryTime).startOf('day'), 'day')
} else {
delayDays = dayjs()
.startOf('day')
.diff(dayjs(row.expectDeliveryTime).startOf('day'), 'day')
}
if (delayDays > 0 && row.status !== 'CANCELLED') {
return (
<span style="color: #f56c6c; font-weight: bold;">{delayDays}</span>
)
}
return <span>-</span>
},
},
{
label: '制单人',
prop: 'createUserName',
width: 100,
align: 'center',
},
{
label: '审核人',
prop: 'auditName',
width: 100,
align: 'center',
},
{
label: '制单时间',
prop: 'createTime',
width: 160,
align: 'center',
sortable: true,
},
{
label: '审核时间',
prop: 'auditTime',
width: 160,
align: 'center',
sortable: true,
},
{
label: '备注',
prop: 'remark',
minWidth: 150,
align: 'left',
showOverflowTooltip: true,
},
{
label: '操作',
prop: 'operation',
width: 180,
align: 'center',
fixed: 'right',
render: (row: TableData) => {
return (
<span>
{row.status === 'PENDING_SUBMIT' && (
<ElButton
link
type="warning"
onClick={() => handleOrder(row, 'edit')}
>
编辑
</ElButton>
)}
{row.status === 'PENDING_AUDIT' && (
<ElButton
link
type="success"
onClick={() => handleOrder(row, 'audit')}
>
审核
</ElButton>
)}
{row.status === 'STOCKING_UP' && (
<ElButton
link
type="primary"
onClick={() => handleSupplierDispatchOrder(row)}
>
供应商发货
</ElButton>
)}
{row.status === 'STOCKING_UP' && (
<ElButton
link
type="warning"
onClick={() => stockingComplete(row)}
>
备货完成
</ElButton>
)}
</span>
)
},
},
]
})
const treeData = ref<TreeData[]>([])
const status = ref<string>('PENDING_SUBMIT')
const selectedRow = ref<TableData | null>(null)
const warehouseList = ref<WarehouseListData[]>([])
const supplierList = ref<SupplierItem[]>([])
const stockKeeperList = ref<userData[]>([])
const currencyList = ref<CurrencyCodeData[]>([])
const selection = ref<TableData[]>([])
const rangeTime = ref<string[]>([])
const [searchForm, resetSearchForm] = useValue<SearchForm>({} as SearchForm)
const treeRef = ref()
const tableRef = ref()
const supplierDispatchOrderDialogRef = ref()
const addStockingOrderDialogRef = ref()
const loadWarehouseList = async () => {
try {
const res = await loadWarehouseListApi()
if (res.code !== 200) return
warehouseList.value = res.data || []
} catch (e) {
console.error(e)
}
}
const loadEmployeeList = async () => {
try {
const res = await getEmployeeListApi()
if (res.code !== 200) return
stockKeeperList.value = res.data || []
} catch (e) {
console.error(e)
}
}
const loadSupplierList = async () => {
try {
const res = await getSupplierListApi()
if (res.code !== 200) return
supplierList.value = res.data || []
} catch (e) {
console.error(e)
}
}
const loadCurrencyList = async () => {
try {
const res = await getBaseCurrencyInfoApi()
if (res.code !== 200) return
currencyList.value = res.data || []
} catch (e) {
console.error(e)
}
}
const loadTreeData = async () => {
try {
const res = await getStockingOrderStatusTreeApi()
if (res.code !== 200) return
treeData.value = [{ code: '-1', name: '全部', children: res.data }]
await nextTick(() => {
treeRef.value!.setCurrentKey(status.value, true)
})
} catch (e) {
console.error(e)
}
}
const nodeClick = (data: TreeData) => {
status.value = data.code
search()
}
const getDeliveryStatusName = (shippingStatus: number) => {
if (!shippingStatus) return ''
return deliveryStatusList.value.find(
(item) => item.value === shippingStatus.toString(),
)?.label
}
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList<TableData>({
query: (page, pageSize) =>
getStockingOrderListApi(
{
...searchForm.value,
startDate: rangeTime.value?.[0],
endDate: rangeTime.value?.[1],
status: status.value === '-1' ? undefined : status.value,
},
page,
pageSize,
).then(async (res) => {
const { records } = res.data
await nextTick(() => {
tableRef.value.setCurrentRow(records[0])
selectedRow.value = (records as never)[0]
})
return res.data
}),
initLoad: true,
})
const handleSelectionChange = (val: TableData[]) => {
selection.value = val
}
const handleRowClick = (row: TableData) => {
if (row) {
selectedRow.value = row
} else {
selectedRow.value = null
}
}
const handleOrder = (row: TableData, type: string) => {
addStockingOrderDialogRef.value.open(row, type)
}
const handleSupplierDispatchOrder = (row: TableData) => {
supplierDispatchOrderDialogRef.value.open(row)
}
const stockingComplete = async (row: TableData) => {
try {
await ElMessageBox.confirm(
'备货完成将把未完成入库申请单自动取消,是否继续对选中的订单操作备货完成?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
} catch {
return
}
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const res = await stockingCompleteApi(row.id)
if (res.code !== 200) return
ElMessage.success('备货完成成功')
search()
loadTreeData()
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const handleAddOrder = () => {
addStockingOrderDialogRef.value.open(null, 'add')
}
const handleSubmitAudit = async () => {
if (selection.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
try {
await ElMessageBox.confirm('确定将选中的订单提交至审核?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await submitStockingOrderAuditApi(
selection.value.map((el) => el.id).join(','),
)
if (res.code !== 200) return
ElMessage.success('提交审核成功')
search()
loadTreeData()
} catch (e) {
console.error(e)
}
}
const handleDeleteOrder = async () => {
if (selection.value.length === 0) {
return ElMessage.warning('请选择一条数据')
}
if (selection.value.length !== 1) {
return ElMessage.warning('不支持批量删除!')
}
try {
await ElMessageBox.confirm('确定删除选中的订单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await deleteStockingOrderApi(selection.value[0].id)
if (res.code !== 200) return
ElMessage.success('删除成功')
onRefresh()
} catch (e) {
console.error(e)
}
}
const handleCancelOrder = async () => {
if (selection.value.length === 0) {
return ElMessage.warning('请选择一条数据')
}
if (selection.value.length !== 1) {
return ElMessage.warning('不支持批量取消!')
}
ElMessageBox.prompt('', '取消原因', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputType: 'textarea',
inputPlaceholder: '请输入取消原因',
inputPattern: /.+/,
inputErrorMessage: '取消原因不能为空',
}).then(async ({ value }: { value: string }) => {
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const res = await cancelStockingOrderApi(selection.value[0].id, value)
if (res.code !== 200) return
ElMessage.success('取消成功')
onRefresh()
} catch (e) {
console.error(e)
} finally {
loading.close()
}
})
}
const addInternalTag = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
ElMessageBox.prompt('', '添加内部便签', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputType: 'textarea',
inputPlaceholder: '请输入内部便签',
inputPattern: /.+/,
inputErrorMessage: '内部便签不能为空',
}).then(async ({ value }: { value: string }) => {
try {
const res = await addStockingOrderInternalTagApi(
value,
selection.value.map((item) => item.id),
)
ElMessage.success(res.message)
search()
} catch (e) {
// showError(e)
}
})
}
const onRefresh = () => {
search()
loadTreeData()
}
onMounted(() => {
loadWarehouseList()
loadEmployeeList()
loadSupplierList()
loadTreeData()
loadCurrencyList()
})
</script>
<style lang="scss" scoped>
.order-status {
width: 160px;
:deep(.el-tree-node__content) {
height: 30px;
line-height: 30px;
}
:deep(.el-tree-node__label) {
font-size: 13px;
cursor: pointer;
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.is-current) {
.tree-node-label,
.tree-node-count {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.tree-node-label,
.tree-node-count {
background-color: transparent !important;
color: black !important;
}
}
}
:deep(.el-tree-node__label) {
font-size: 13px;
color: rgb(96, 98, 102);
cursor: pointer;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.el-tree-node__label) {
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.is-current) {
.el-tree-node__label {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.el-tree-node__label {
background-color: transparent !important;
color: black !important;
}
}
}
}
.tree-node {
display: flex;
color: #333;
font-weight: 500;
}
.table-content {
flex: 1;
margin-top: 10px;
overflow: hidden;
:deep(#top) {
height: 100%;
}
}
.search-form {
display: grid;
grid-template-columns: repeat(6, 1fr);
align-items: flex-start;
:deep(.el-form-item) {
margin-right: 10px;
margin-bottom: 10px;
}
}
</style>
...@@ -190,10 +190,10 @@ import { ...@@ -190,10 +190,10 @@ import {
getSupplierDetailApi, getSupplierDetailApi,
getPropertyByCateIdApi, getPropertyByCateIdApi,
getProductInfoBySpuApi, getProductInfoBySpuApi,
getBaseCurrencyInfoApi,
addSupplierApi, addSupplierApi,
updateSupplierApi, updateSupplierApi,
} from '@/api/supplier/supplierManagement.ts' } from '@/api/supplier/supplierManagement.ts'
import { getBaseCurrencyInfoApi } from '@/api/common'
import Dialog from './components/dialog.tsx' import Dialog from './components/dialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx' import CustomizeForm from '@/components/CustomizeForm.tsx'
......
...@@ -272,6 +272,7 @@ ...@@ -272,6 +272,7 @@
header-align="center" header-align="center"
align="center" align="center"
></ElTableColumn> ></ElTableColumn>
<ElTableColumn <ElTableColumn
v-if="nodeCode === 'PENDING_SUBMIT'" v-if="nodeCode === 'PENDING_SUBMIT'"
width="100" width="100"
...@@ -386,6 +387,13 @@ ...@@ -386,6 +387,13 @@
prop="remark" prop="remark"
/> />
<ElTableColumn <ElTableColumn
label="所属客户"
show-overflow-tooltip
prop="userMark"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
label="创建时间" label="创建时间"
...@@ -604,6 +612,12 @@ ...@@ -604,6 +612,12 @@
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
label="所属客户"
prop="userMark"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
width="240" width="240"
label="备注" label="备注"
prop="remark" prop="remark"
...@@ -624,7 +638,7 @@ ...@@ -624,7 +638,7 @@
clearable clearable
size="small" size="small"
></el-input> ></el-input>
<el-popover placement="top-start" width="1000" trigger="click"> <el-popover placement="top-start" width="1200" trigger="click">
<div v-if="skuData.length > 0" style="height: 50vh"> <div v-if="skuData.length > 0" style="height: 50vh">
<ElTable size="small" :data="skuData" height="100%" border> <ElTable size="small" :data="skuData" height="100%" border>
<ElTableColumn <ElTableColumn
...@@ -687,6 +701,12 @@ ...@@ -687,6 +701,12 @@
prop="locationCode" prop="locationCode"
/> />
<ElTableColumn <ElTableColumn
show-overflow-tooltip
align="center"
label="所属客户"
prop="userMark"
/>
<ElTableColumn
width="80" width="80"
align="center" align="center"
header-align="center" header-align="center"
...@@ -836,9 +856,7 @@ ...@@ -836,9 +856,7 @@
maxlength="1000" maxlength="1000"
show-word-limit show-word-limit
/> />
<div style="margin-top: 12px; color: #777"> <div style="margin-top: 12px; color: #777">多个字段使用","隔开</div>
{{ '多个字段使用_##_,_##_隔开' }}
</div>
</div> </div>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
...@@ -1268,6 +1286,11 @@ const handleLocalImport = async ({ ...@@ -1268,6 +1286,11 @@ const handleLocalImport = async ({
return { return {
skuImage: skuItem.image, skuImage: skuItem.image,
customerId: skuItem.customerId,
userMark: skuItem.userMark,
customerName: skuItem.customerName,
currencyName: skuItem.currencyName,
currencyCode: skuItem.currencyCode,
warehouseSku: skuItem.warehouseSku, warehouseSku: skuItem.warehouseSku,
skuName: skuItem.skuName, skuName: skuItem.skuName,
productNo: skuItem.productNumber, productNo: skuItem.productNumber,
...@@ -1388,8 +1411,11 @@ const skudblclick = (val: InterskuList) => { ...@@ -1388,8 +1411,11 @@ const skudblclick = (val: InterskuList) => {
// 使用可选链和空值合并运算符处理可能的null值 // 使用可选链和空值合并运算符处理可能的null值
const { const {
locationCode = '', locationCode = '',
customerId = '',
customerName = '',
price = null, price = null,
productNo = '', productNo = '',
userMark = '',
warehouseSku = '', warehouseSku = '',
skuName = '', skuName = '',
image = '', image = '',
...@@ -1415,8 +1441,11 @@ const skudblclick = (val: InterskuList) => { ...@@ -1415,8 +1441,11 @@ const skudblclick = (val: InterskuList) => {
{ {
skuImage: image, skuImage: image,
warehouseSku, warehouseSku,
customerId,
customerName,
skuName, skuName,
productNo, productNo,
userMark,
locationCode: locationCode ?? '', // 确保空值处理 locationCode: locationCode ?? '', // 确保空值处理
locationId: locationId ?? null, // 确保空值处理 locationId: locationId ?? null, // 确保空值处理
costPrice: price, costPrice: price,
...@@ -1752,6 +1781,11 @@ const submitPurchase = async () => { ...@@ -1752,6 +1781,11 @@ const submitPurchase = async () => {
const mergedProductList = filteredSkusList.map((skuItem) => { const mergedProductList = filteredSkusList.map((skuItem) => {
return { return {
skuImage: skuItem.image, skuImage: skuItem.image,
customerId: skuItem.customerId,
userMark: skuItem.userMark,
customerName: skuItem.customerName,
currencyName: skuItem.currencyName,
currencyCode: skuItem.currencyCode,
warehouseSku: skuItem.warehouseSku, warehouseSku: skuItem.warehouseSku,
skuName: skuItem.skuName, skuName: skuItem.skuName,
productNo: skuItem.productNumber, productNo: skuItem.productNumber,
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<splitDiv size="50"> <splitDiv size="50">
<template #top> <template #top>
<div class="header-filter-form"> <div class="header-filter-form">
<ElForm :model="searchForm" inline ref="searchFormRef"> <ElForm ref="searchFormRef" :model="searchForm" inline>
<ElFormItem label="采购仓库"> <ElFormItem label="采购仓库">
<ElSelect <ElSelect
v-model="searchForm.warehouseId" v-model="searchForm.warehouseId"
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
/> />
</ElFormItem> </ElFormItem>
<ElFormItem> <ElFormItem>
<ElButton type="primary" @click="search" ref="searchBtnRef" <ElButton ref="searchBtnRef" type="primary" @click="search"
>查询</ElButton >查询</ElButton
> >
</ElFormItem> </ElFormItem>
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
提交审核 提交审核
</el-button> </el-button>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="nodeCode === 'PENDING_AUDIT'"> <ElFormItem v-if="nodeCode === 'PENDING_SUBMIT'">
<el-button type="danger" @click="auditOrder('invalid')"> <el-button type="danger" @click="auditOrder('invalid')">
作废 作废
</el-button> </el-button>
...@@ -184,7 +184,7 @@ ...@@ -184,7 +184,7 @@
align="center" align="center"
></ElTableColumn> ></ElTableColumn>
<ElTableColumn <ElTableColumn
label="备货单号" label="来源单号"
show-overflow-tooltip show-overflow-tooltip
prop="sourceOn" prop="sourceOn"
width="130" width="130"
...@@ -278,7 +278,16 @@ ...@@ -278,7 +278,16 @@
label="操作" label="操作"
> >
<template #default="{ row }"> <template #default="{ row }">
<ElButton type="primary" link @click="addDialog(2, row)" <ElButton
:disabled="row.source === 'StockingUpWarehouseApply'"
:title="
row.source === 'StockingUpWarehouseApply'
? '来源为入库申请单,不允许编辑!请先操作取消,再重新提交入库单'
: ''
"
type="primary"
link
@click="addDialog(2, row)"
>编辑 >编辑
</ElButton> </ElButton>
</template> </template>
...@@ -299,7 +308,7 @@ ...@@ -299,7 +308,7 @@
</div> </div>
</template> </template>
<template #bottom> <template #bottom>
<el-tabs v-model="tabsValue" @tab-click="tabsClick"> <el-tabs v-loading="showDetailLoading" v-model="tabsValue" @tab-click="tabsClick">
<el-tab-pane name="0" label="入库商品"> <el-tab-pane name="0" label="入库商品">
<div class="table-wrap"> <div class="table-wrap">
<ElTable size="small" :data="detailList" height="100%" border> <ElTable size="small" :data="detailList" height="100%" border>
...@@ -331,7 +340,9 @@ ...@@ -331,7 +340,9 @@
label="库存SKU" label="库存SKU"
width="180" width="180"
prop="warehouseSku" prop="warehouseSku"
/> >
</ElTableColumn>
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
...@@ -380,6 +391,12 @@ ...@@ -380,6 +391,12 @@
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
label="所属客户"
prop="userMark"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
width="240" width="240"
label="备注" label="备注"
prop="remark" prop="remark"
...@@ -448,6 +465,12 @@ ...@@ -448,6 +465,12 @@
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<div class="import-dialog"> <div class="import-dialog">
<div class="label" style="margin-bottom: 10px">
<span style="margin-right: 10px">所属客户:</span>
<el-select v-model="importUserMark" size="small" style="width: 100px;">
<el-option v-for="user in userMarkList" :key="user.userId" :label="user.userMark" :value="user.userId"></el-option>
</el-select>
</div>
<div class="import-content"> <div class="import-content">
<UploadExcel <UploadExcel
v-model="importedFileUrl" v-model="importedFileUrl"
...@@ -462,7 +485,7 @@ ...@@ -462,7 +485,7 @@
<ElDialog <ElDialog
v-model="newDialogVisible" v-model="newDialogVisible"
:title="formId ? '编辑' : '新增'" :title="formId ? '编辑' : '新增'"
width="80%" width="85%"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<div class="dialog-form"> <div class="dialog-form">
...@@ -538,13 +561,16 @@ ...@@ -538,13 +561,16 @@
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
width="140"
label="库存SKU" label="库存SKU"
prop="warehouseSku" prop="warehouseSku"
/> >
</ElTableColumn>
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
width="160"
label="商品名称" label="商品名称"
prop="skuName" prop="skuName"
/> />
...@@ -605,9 +631,15 @@ ...@@ -605,9 +631,15 @@
</template> </template>
</ElTableColumn> </ElTableColumn>
<ElTableColumn <ElTableColumn
align="center"
width="100"
label="所属客户"
prop="userMark"
/>
<ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
width="240" width="140"
label="备注" label="备注"
prop="remark" prop="remark"
> >
...@@ -619,7 +651,11 @@ ...@@ -619,7 +651,11 @@
</div> </div>
<template #footer> <template #footer>
<div class="product-dialog-footer"> <div class="product-dialog-footer">
<div> <div style="display: flex;align-items: center">
<span style="margin-right: 10px;font-style:13px;color: gray">搜索:</span>
<el-select v-model="userMark" size="small" style="width: 100px;">
<el-option v-for="user in userMarkList" :key="user.userId" :label="user.userMark" :value="user.userId"></el-option>
</el-select>
<el-input <el-input
v-model.trim="selectSku" v-model.trim="selectSku"
placeholder="库存SKU" placeholder="库存SKU"
...@@ -627,7 +663,7 @@ ...@@ -627,7 +663,7 @@
clearable clearable
size="small" size="small"
></el-input> ></el-input>
<el-popover placement="top-start" width="1000" trigger="click"> <el-popover placement="top-start" width="1200" trigger="click">
<div v-if="skuData.length > 0" style="height: 50vh"> <div v-if="skuData.length > 0" style="height: 50vh">
<ElTable size="small" :data="filterSkuData" height="100%" border> <ElTable size="small" :data="filterSkuData" height="100%" border>
<ElTableColumn <ElTableColumn
...@@ -645,15 +681,15 @@ ...@@ -645,15 +681,15 @@
prop="image" prop="image"
> >
<template #default="{ row }"> <template #default="{ row }">
<ImageView :src="row.image" width="40px" height="40px" /> <ImageView :src="row.skuImage" width="40px" height="40px" />
</template> </template>
</ElTableColumn> </ElTableColumn>
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
align="center" align="center"
label="库存SKU" label="商品名称"
width="200" width="200"
prop="skuName" prop="productName"
/> />
<ElTableColumn <ElTableColumn
...@@ -661,7 +697,7 @@ ...@@ -661,7 +697,7 @@
align="center" align="center"
width="200" width="200"
label="库存SKU" label="库存SKU"
prop="sku" prop="warehouseSku"
/> />
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
...@@ -681,7 +717,7 @@ ...@@ -681,7 +717,7 @@
align="center" align="center"
label="成本价" label="成本价"
width="80" width="80"
prop="factoryPrice" prop="costPrice"
/> />
<ElTableColumn <ElTableColumn
show-overflow-tooltip show-overflow-tooltip
...@@ -690,6 +726,12 @@ ...@@ -690,6 +726,12 @@
prop="locationCode" prop="locationCode"
/> />
<ElTableColumn <ElTableColumn
show-overflow-tooltip
align="center"
label="所属客户"
prop="userMark"
/>
<ElTableColumn
width="80" width="80"
align="center" align="center"
header-align="center" header-align="center"
...@@ -829,6 +871,12 @@ ...@@ -829,6 +871,12 @@
width="500px" width="500px"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<div class="label" style="margin-bottom: 10px">
<span style="margin-right: 10px">所属客户:</span>
<el-select v-model="batchUserMark" size="small" style="width: 100px;">
<el-option v-for="user in userMarkList" :key="user.userId" :label="user.userMark" :value="user.userId"></el-option>
</el-select>
</div>
<div> <div>
<el-input <el-input
v-model.trim="purchaseTextarea" v-model.trim="purchaseTextarea"
...@@ -839,9 +887,7 @@ ...@@ -839,9 +887,7 @@
maxlength="1000" maxlength="1000"
show-word-limit show-word-limit
/> />
<div style="margin-top: 12px; color: #777"> <div style="margin-top: 12px; color: #777">多个字段使用","隔开</div>
{{ '多个字段使用_##_,_##_隔开' }}
</div>
</div> </div>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
...@@ -864,7 +910,6 @@ import { ...@@ -864,7 +910,6 @@ import {
getInRecordStatusTree, getInRecordStatusTree,
warehouseInRecordListPageApi, warehouseInRecordListPageApi,
getWarehouseInRecordDetailApi, getWarehouseInRecordDetailApi,
getBySkuApi,
warehouseInfoGetAll, warehouseInfoGetAll,
getByWareHouseIdAndCodeApi, getByWareHouseIdAndCodeApi,
addInRecordApi, addInRecordApi,
...@@ -877,11 +922,11 @@ import { ...@@ -877,11 +922,11 @@ import {
warehouseInfo, warehouseInfo,
InRecordBatchCheckPrintApi, InRecordBatchCheckPrintApi,
factoryWarehouseInventoryPrint, factoryWarehouseInventoryPrint,
warehouseInRecordExport, warehouseInRecordExport, getLocalFactoryList, getBySkuAndUserMarkApi,
} from '@/api/warehouse' } from '@/api/warehouse'
import { filePath } from '@/api/axios.ts' import { filePath } from '@/api/axios.ts'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { ref, onMounted, watch, nextTick } from 'vue' import { ref, onMounted, watch, nextTick, computed } from 'vue'
import 'element-plus/dist/index.css' import 'element-plus/dist/index.css'
import { import {
warehouseSearchForm, warehouseSearchForm,
...@@ -894,10 +939,14 @@ import { ...@@ -894,10 +939,14 @@ import {
} from '@/types/api/warehouse' } from '@/types/api/warehouse'
import ImageView from '@/components/ImageView.vue' import ImageView from '@/components/ImageView.vue'
import UploadExcel from '@/components/UploadExcel.vue' import UploadExcel from '@/components/UploadExcel.vue'
// import { debounce } from 'lodash-es'
import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts' import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts'
interface UserMark{
userId: number
userMark: string
userName: string
}
const warehouseList = ref<warehouseInfo[]>([]) const warehouseList = ref<warehouseInfo[]>([])
const userMarkList = ref<UserMark[]>([])
const pickerOptions = { const pickerOptions = {
shortcuts: [ shortcuts: [
{ {
...@@ -1017,6 +1066,9 @@ function getStartTime() { ...@@ -1017,6 +1066,9 @@ function getStartTime() {
return `${year}-${month}-${day} 00:00:00` return `${year}-${month}-${day} 00:00:00`
} }
const selectSku = ref('') const selectSku = ref('')
const userMark = ref(0)
const batchUserMark = ref(0)
const importUserMark = ref(0)
const treeData = ref<InterWarehouseTree[]>() const treeData = ref<InterWarehouseTree[]>()
const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({}) const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({})
...@@ -1027,6 +1079,10 @@ const tabsValue = ref<string>('0') ...@@ -1027,6 +1079,10 @@ const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<InstanceType<typeof ElTable>>()
const currentRow = ref<InterWarehousePage | null>(null) const currentRow = ref<InterWarehousePage | null>(null)
const logList = ref<LogListData[]>([]) const logList = ref<LogListData[]>([])
const detailLoading = ref(false)
const showDetailLoading = computed(() => {
return detailLoading.value && !newDialogVisible.value
})
const rules = { const rules = {
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }], warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
} }
...@@ -1136,13 +1192,23 @@ async function handlePrintProductTag() { ...@@ -1136,13 +1192,23 @@ async function handlePrintProductTag() {
window.open(filePath + res.message, '_blank') window.open(filePath + res.message, '_blank')
} }
const batchAddCommodity = async (sku: string): Promise<InterskuList[]> => { const batchAddCommodity = async (sku: string,type:string): Promise<InterskuList[]> => {
if (!editForm.value.warehouseId) { if (!editForm.value.warehouseId) {
ElMessage.error('请选择仓库') ElMessage.error('请选择仓库')
return [] return []
} }
try { try {
const res = await getBySkuApi(editForm.value.warehouseId, sku) let userValue: string | number = ''
if(type==='1'){
userValue = userMark.value
}else if(type==='2'){
userValue = batchUserMark.value
}else if(type==='3'){
userValue = importUserMark.value
}
const item = userMarkList.value.find(e=>e.userId===userValue)
const user = userValue===0 ? null : item?.userMark
const res = await getBySkuAndUserMarkApi(editForm.value.warehouseId, sku,user)
const arr: InterskuList[] = res.data || [] const arr: InterskuList[] = res.data || []
const ids: Record<string, boolean> = {} const ids: Record<string, boolean> = {}
// 过滤掉商品列表已经加了的 // 过滤掉商品列表已经加了的
...@@ -1231,7 +1297,7 @@ const handleLocalImport = async ({ ...@@ -1231,7 +1297,7 @@ const handleLocalImport = async ({
// 2. 提取导入的 SKU 列表 // 2. 提取导入的 SKU 列表
const importedSkus = importedData.map((item) => item.warehouseSku).join(',') const importedSkus = importedData.map((item) => item.warehouseSku).join(',')
// 3. 调用 batchAddCommodity 获取商品的完整信息并过滤掉已有的 SKU // 3. 调用 batchAddCommodity 获取商品的完整信息并过滤掉已有的 SKU
const filteredSkusList = await batchAddCommodity(importedSkus) // 使用 await 等待结果 const filteredSkusList = await batchAddCommodity(importedSkus,'3') // 使用 await 等待结果
if (filteredSkusList.length === 0) { if (filteredSkusList.length === 0) {
ElMessage.warning('导入的商品SKU已存在或无效') ElMessage.warning('导入的商品SKU已存在或无效')
importedFileUrl.value = path importedFileUrl.value = path
...@@ -1242,24 +1308,30 @@ const handleLocalImport = async ({ ...@@ -1242,24 +1308,30 @@ const handleLocalImport = async ({
const mergedProductList = filteredSkusList.map((skuItem) => { const mergedProductList = filteredSkusList.map((skuItem) => {
// 在导入数据中找到匹配的备注信息 // 在导入数据中找到匹配的备注信息
const importedItem = importedData.find( const importedItem = importedData.find(
(item) => item.warehouseSku === skuItem.sku, (item) => skuItem.warehouseSku?.includes(item.warehouseSku),
) )
const target = locationList.value.find((item: InterProductList) => { const target = locationList.value.find((item: InterProductList) => {
return item.locationCode == importedItem?.locationCode return item.locationCode == importedItem?.locationCode
}) })
const item = userMarkList.value.find(e=>e.userId===importUserMark.value)
return { return {
skuImage: skuItem.image, customerId:importUserMark.value !== 0 ?importUserMark.value : null,
warehouseSku: skuItem.sku, customerName: importUserMark.value !== 0 ? item?.userName || '' : null,
skuName: skuItem.skuName, userMark: importUserMark.value !== 0 ? item?.userMark : null,
skuImage: skuItem.skuImage,
currencyName: skuItem.currencyName,
currencyCode: skuItem.currencyCode,
warehouseSku: skuItem.warehouseSku,
skuName: skuItem.productName,
productNo: skuItem.productNo, productNo: skuItem.productNo,
locationCode: target?.locationCode ?? '', locationCode: target?.locationCode ?? '',
locationId: target?.locationId, locationId: target?.locationId,
costPrice: skuItem.factoryPrice, costPrice: skuItem.costPrice,
buyStored: importedItem?.buyStored ?? null, buyStored: importedItem?.buyStored ?? null,
totalPrice: new BigNumber( totalPrice: new BigNumber(
(importedItem?.buyStored ?? 0) as number | string, (importedItem?.buyStored ?? 0) as number | string,
) )
.multipliedBy(skuItem.factoryPrice ?? 0) .multipliedBy(skuItem.costPrice ?? 0)
.toNumber(), .toNumber(),
usableInventory: skuItem.usableInventory, usableInventory: skuItem.usableInventory,
remark: importedItem?.remark ?? null, remark: importedItem?.remark ?? null,
...@@ -1337,7 +1409,9 @@ const skuData = ref<InterskuList[]>([]) ...@@ -1337,7 +1409,9 @@ const skuData = ref<InterskuList[]>([])
const selectbySku = async () => { const selectbySku = async () => {
if (!editForm.value.warehouseId) return ElMessage.error('请选择仓库') if (!editForm.value.warehouseId) return ElMessage.error('请选择仓库')
try { try {
const res = await getBySkuApi(editForm.value.warehouseId, selectSku.value) const item = userMarkList.value.find(e=>e.userId===userMark.value)
const user = userMark.value===0 ? null : item?.userMark
const res = await getBySkuAndUserMarkApi(editForm.value.warehouseId, selectSku.value,user)
skuData.value = res.data || [] skuData.value = res.data || []
} catch (e) { } catch (e) {
console.error(e) console.error(e)
...@@ -1347,11 +1421,11 @@ const skudblclick = (val: InterskuList) => { ...@@ -1347,11 +1421,11 @@ const skudblclick = (val: InterskuList) => {
// 使用可选链和空值合并运算符处理可能的null值 // 使用可选链和空值合并运算符处理可能的null值
const { const {
locationCode = '', locationCode = '',
factoryPrice = null, costPrice = null,
productNo = '', productNo = '',
sku = '', warehouseSku = '',
skuName = '', productName = '',
image = '', skuImage = '',
locationId = null, locationId = null,
currencyName = '', currencyName = '',
currencyCode = null, currencyCode = null,
...@@ -1366,16 +1440,20 @@ const skudblclick = (val: InterskuList) => { ...@@ -1366,16 +1440,20 @@ const skudblclick = (val: InterskuList) => {
return return
} }
} }
const item = userMarkList.value.find(e=>e.userId===userMark.value)
otherPurchaseData.value = [ otherPurchaseData.value = [
...otherPurchaseData.value, ...JSON.parse(JSON.stringify(otherPurchaseData.value)),
{ {
skuImage: image, skuImage,
warehouseSku: sku, warehouseSku,
skuName, customerId:userMark.value !== 0 ?userMark.value : null,
customerName: userMark.value !== 0 ? item?.userName || '' : null,
userMark: userMark.value !== 0 ? item?.userMark : null,
skuName:productName,
productNo, productNo,
locationCode: locationCode ?? '', // 确保空值处理 locationCode: locationCode ?? '', // 确保空值处理
locationId: locationId ?? null, // 确保空值处理 locationId: locationId ?? null, // 确保空值处理
costPrice: factoryPrice, costPrice,
buyStored: null, buyStored: null,
totalPrice: null, totalPrice: null,
currencyName, currencyName,
...@@ -1418,7 +1496,17 @@ const editFormRef = ref() ...@@ -1418,7 +1496,17 @@ const editFormRef = ref()
const editForm2 = ref({}) const editForm2 = ref({})
const formId = ref<number | undefined>(undefined) const formId = ref<number | undefined>(undefined)
const otherPurchaseData = ref<InterProductList[]>([]) const otherPurchaseData = ref<InterProductList[]>([])
const getUserMark = async () => {
const {data} = await getLocalFactoryList()
userMarkList.value = [
...[{userMark:'九猫商品', userId: 0,userName:''}],
...data
]
}
const addDialog = async (i: number, v: InterWarehousePage | null) => { const addDialog = async (i: number, v: InterWarehousePage | null) => {
await getUserMark()
if (i === 2) { if (i === 2) {
if (v) formId.value = v.id if (v) formId.value = v.id
if (v) getProduct(v.id) if (v) getProduct(v.id)
...@@ -1426,6 +1514,7 @@ const addDialog = async (i: number, v: InterWarehousePage | null) => { ...@@ -1426,6 +1514,7 @@ const addDialog = async (i: number, v: InterWarehousePage | null) => {
// editForm.value = JSON.parse(JSON.stringify(v)) // editForm.value = JSON.parse(JSON.stringify(v))
} else { } else {
await nextTick() await nextTick()
editForm.value = JSON.parse(JSON.stringify(editForm2.value)) editForm.value = JSON.parse(JSON.stringify(editForm2.value))
resetEditForm() resetEditForm()
const userJson = localStorage.getItem('user') const userJson = localStorage.getItem('user')
...@@ -1506,10 +1595,21 @@ const auditOrder = (key: string) => { ...@@ -1506,10 +1595,21 @@ const auditOrder = (key: string) => {
dataVersion, dataVersion,
}), }),
) )
await auditOrderApi(url, data) const loading = ElLoading.service({
ElMessage.success('操作成功') fullscreen: true,
search() text: '操作中...',
await getTreeNum() background: 'rgba(0, 0, 0, 0.3)',
})
try {
await auditOrderApi(url, data)
ElMessage.success('操作成功')
search()
await getTreeNum()
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}) })
} }
const rejectedInRecord = () => { const rejectedInRecord = () => {
...@@ -1601,7 +1701,13 @@ const addOtherCurrency = async () => { ...@@ -1601,7 +1701,13 @@ const addOtherCurrency = async () => {
const filterSkuData = computed(() => { const filterSkuData = computed(() => {
const skuList = otherPurchaseData.value.map((el) => el.warehouseSku) const skuList = otherPurchaseData.value.map((el) => el.warehouseSku)
// console.log(skuList, skuData.value) // console.log(skuList, skuData.value)
return skuData.value.filter((el) => !skuList.includes(el.sku)) const item = userMarkList.value.find(u=>u.userId===userMark.value)
return skuData.value.filter((el) => !skuList.includes(el.warehouseSku)).map(e=>{
return {
...e,
userMark:userMark.value===0?'':item?.userMark
}
})
}) })
const addSection = async () => { const addSection = async () => {
const params = { ...editForm.value } const params = { ...editForm.value }
...@@ -1668,6 +1774,7 @@ const addPurchaseVisible = ref(false) ...@@ -1668,6 +1774,7 @@ const addPurchaseVisible = ref(false)
const purchaseTextarea = ref('') const purchaseTextarea = ref('')
const addPurchase = async () => { const addPurchase = async () => {
addPurchaseVisible.value = true addPurchaseVisible.value = true
batchUserMark.value = 0
purchaseTextarea.value = '' purchaseTextarea.value = ''
} }
const submitPurchase = async () => { const submitPurchase = async () => {
...@@ -1675,16 +1782,22 @@ const submitPurchase = async () => { ...@@ -1675,16 +1782,22 @@ const submitPurchase = async () => {
ElMessage.warning('请输入库存 SKU') ElMessage.warning('请输入库存 SKU')
return return
} }
const filteredSkusList = await batchAddCommodity(purchaseTextarea.value) const item = userMarkList.value.find(e=>e.userId===batchUserMark.value)
const filteredSkusList = await batchAddCommodity(purchaseTextarea.value,'2')
const mergedProductList = filteredSkusList.map((skuItem) => { const mergedProductList = filteredSkusList.map((skuItem) => {
return { return {
skuImage: skuItem.image, customerId:batchUserMark.value !== 0 ?batchUserMark.value : null,
warehouseSku: skuItem.sku, customerName: batchUserMark.value !== 0 ? item?.userName || '' : null,
skuName: skuItem.skuName, userMark: batchUserMark.value !== 0 ? item?.userMark : null,
skuImage: skuItem.skuImage,
warehouseSku: skuItem.warehouseSku,
currencyName: skuItem.currencyName,
currencyCode: skuItem.currencyCode,
skuName: skuItem.productName,
productNo: skuItem.productNo, productNo: skuItem.productNo,
locationCode: skuItem.locationCode ?? '', locationCode: skuItem.locationCode ?? '',
locationId: skuItem.locationId ?? null, locationId: skuItem.locationId ?? null,
costPrice: skuItem.factoryPrice, costPrice: skuItem.costPrice,
buyStored: null, buyStored: null,
totalPrice: null, totalPrice: null,
usableInventory: skuItem.usableInventory, usableInventory: skuItem.usableInventory,
...@@ -1707,6 +1820,7 @@ const importedFileUrl = ref('') ...@@ -1707,6 +1820,7 @@ const importedFileUrl = ref('')
const importData = async () => { const importData = async () => {
importDialogVisible.value = true importDialogVisible.value = true
importedFileUrl.value = '' importedFileUrl.value = ''
importUserMark.value = 0
} }
const handleBatchDelete = async () => { const handleBatchDelete = async () => {
if (!selections.value.length) { if (!selections.value.length) {
...@@ -1734,19 +1848,29 @@ const nodeClick = (data: InterWarehouseTree) => { ...@@ -1734,19 +1848,29 @@ const nodeClick = (data: InterWarehouseTree) => {
// total: 0, // total: 0,
// }) // })
const searchDetail = async () => { const searchDetail = async () => {
detailLoading.value = true
try { try {
const res = await getWarehouseInRecordDetailApi(currentRow.value?.id) const res = await getWarehouseInRecordDetailApi(currentRow.value?.id)
res.data.productList.forEach(item => {
item.currencyName = res.data.currencyName || ''
item.currencyCode = res.data.currencyCode || ''
})
detailList.value = res.data?.productList || [] detailList.value = res.data?.productList || []
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} finally {
detailLoading.value = false
} }
} }
const getLogList = async () => { const getLogList = async () => {
detailLoading.value = true
try { try {
const res = await getInRecordLogApi(currentRow.value?.id) const res = await getInRecordLogApi(currentRow.value?.id)
logList.value = res.data logList.value = res.data
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} finally {
detailLoading.value = false
} }
} }
const locationList = ref<ILocation[]>([]) const locationList = ref<ILocation[]>([])
...@@ -2010,7 +2134,6 @@ $border: solid 1px #ddd; ...@@ -2010,7 +2134,6 @@ $border: solid 1px #ddd;
::v-deep(.el-tree-node) { ::v-deep(.el-tree-node) {
cursor: pointer; cursor: pointer;
margin-bottom: 5px;
} }
::v-deep(.el-tree-node__label) { ::v-deep(.el-tree-node__label) {
......
<template>
<ElTabs
v-model="activeTab"
v-loading="loading"
class="detail-tabs"
@tab-click="handleTabClick"
>
<ElTabPane name="stockProducts" label="入库商品">
<div class="detail-table-content">
<TableView
:serial-numberable="true"
:columns="stockProductsColumns"
:paginated-data="stockProductsData"
/>
</div>
</ElTabPane>
<ElTabPane name="relatedDocuments" label="关联单据">
<div class="detail-table-content">
<TableView
:serial-numberable="true"
:columns="relatedDocumentsColumns"
:paginated-data="relatedDocumentsData"
/>
</div>
</ElTabPane>
<ElTabPane name="operationLog" label="操作日志">
<div class="detail-table-content">
<LogList :log-list="operationLogData" />
<div class="empty-content">暂无数据</div>
</div>
</ElTabPane>
</ElTabs>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import type { TabsPaneContext } from 'element-plus'
import TableView from '@/components/TableView.vue'
import {
RelatedDocumentList,
StockingApplyOrderDetailList,
TableData,
} from '@/types/api/warehouse/stockingApplyOrder'
import ImageView from '@/components/ImageView.vue'
import {
getStockingApplyOrderDetailListByIdApi,
getStockingApplyOrderLogListByIdApi,
getStockingApplyOrderRelatedDocumentListByIdApi,
} from '@/api/warehouse/stockingApplyOrder'
import LogList from '@/components/LogList.vue'
import { LogListData } from '@/types/api/supply/stockingOrder'
const relatedDocumentsColumns = computed(() => {
return [
{
label: '关联单据',
prop: 'inNo',
align: 'center',
},
{
label: '关联单号',
prop: 'sourceOn',
align: 'center',
},
{
label: '制单人',
width: 120,
prop: 'checkerName',
align: 'center',
},
{
label: '制单时间',
width: 160,
prop: 'createTime',
align: 'center',
},
{
label: 'SKU个数',
width: 120,
align: 'right',
prop: 'skuAmount',
},
{
label: '入库数量',
width: 120,
align: 'right',
prop: 'total',
},
]
})
const stockProductsColumns = computed(() => {
return [
{
label: 'SKU图片',
prop: 'skuImage',
width: 100,
align: 'center',
render: (item: StockingApplyOrderDetailList) => (
<ImageView src={item.warehouseSkuImage} width="50px" height="50px" />
),
},
{
label: '商品名称',
prop: 'warehouseSkuName',
minWidth: 200,
align: 'left',
},
{
label: '款号',
prop: 'productNo',
width: 120,
align: 'center',
},
{
label: '库存SKU',
prop: 'warehouseSku',
width: 180,
align: 'center',
},
{
label: '申请数量',
prop: 'shipmentQuantity',
width: 100,
align: 'right',
},
{
label: '入库数量',
prop: 'buyStored',
width: 120,
align: 'right',
},
{
label: '不良品数',
prop: 'rejectsAmount',
width: 120,
align: 'right',
},
{
label: '实际质检数',
prop: 'finallyShipmentQuantity',
width: 120,
align: 'right',
},
{
label: '入库待审核数',
prop: 'storedPending',
width: 120,
align: 'right',
},
{
label: '币种',
prop: 'currencyCode',
width: 100,
align: 'center',
},
{
label: '入库单价',
prop: 'price',
width: 100,
align: 'right',
},
]
})
const props = defineProps<{
selectedRow: TableData | null
}>()
const stockProductsData = ref<StockingApplyOrderDetailList[]>([])
const relatedDocumentsData = ref<RelatedDocumentList[]>([])
const activeTab = ref('stockProducts')
const loading = ref(false)
const operationLogData = ref<LogListData[]>([])
watch(
() => props.selectedRow,
(newVal) => {
if (newVal) {
if (activeTab.value === 'stockProducts') {
loadStockProducts(newVal)
} else if (activeTab.value === 'relatedDocuments') {
loadRelatedDocuments(newVal)
} else if (activeTab.value === 'operationLog') {
loadOperationLog(newVal)
}
} else {
stockProductsData.value = []
relatedDocumentsData.value = []
}
},
)
const loadStockProducts = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingApplyOrderDetailListByIdApi(row.id)
if (res.code !== 200) return
stockProductsData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadRelatedDocuments = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingApplyOrderRelatedDocumentListByIdApi(
row.warehouseApplyNo,
)
if (res.code !== 200) return
relatedDocumentsData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadOperationLog = async (row: TableData) => {
try {
const res = await getStockingApplyOrderLogListByIdApi(row.id)
if (res.code !== 200) return
operationLogData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const handleTabClick = (tab: TabsPaneContext) => {
activeTab.value = tab.paneName as string
if (activeTab.value === 'stockProducts') {
loadStockProducts(props.selectedRow as TableData)
} else if (activeTab.value === 'relatedDocuments') {
loadRelatedDocuments(props.selectedRow as TableData)
} else if (activeTab.value === 'operationLog') {
loadOperationLog(props.selectedRow as TableData)
}
}
</script>
<style lang="scss" scoped>
.detail-tabs {
height: 100%;
display: flex;
:deep(.el-tab-pane) {
height: 100%;
overflow: hidden;
}
}
.detail-table-content {
height: 100%;
overflow: hidden;
}
.empty-content {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #909399;
font-size: 14px;
}
</style>
<template>
<ElDialog
v-model="visible"
title="提交入库"
width="1400px"
top="10vh"
:close-on-click-modal="false"
destroy-on-close
>
<div class="submit-warehousing">
<ElForm
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
inline
>
<div class="form-row">
<ElFormItem label="入库申请单" class="form-item">
<ElInput
v-model="formData.warehouseApplyNo"
disabled
placeholder="自动带出"
/>
</ElFormItem>
<ElFormItem label="入库仓库" class="form-item">
<ElInput
v-model="formData.warehouseName"
disabled
placeholder="自动带出"
/>
</ElFormItem>
<ElFormItem label="质检员" prop="checkerUserId" class="form-item">
<ElSelect
v-model="formData.checkerUserId"
placeholder="请选择"
filterable
clearable
>
<ElOption
v-for="item in userList"
:key="item.id"
:label="item.account"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="备注" class="form-item remark-item">
<ElInput v-model="formData.remark" placeholder="请输入" clearable />
</ElFormItem>
</div>
</ElForm>
<div class="table-section">
<TableView
:selectionable="true"
:serial-numberable="true"
:data="formData.detailsList"
:columns="tableColumns"
@selection-change="handleSelectionChange"
>
<template #buyStored="{ row }">
<ElInput
v-model="row.buyStored"
clearable
size="small"
style="width: 70px"
@blur="validateField(row, 'buyStored')"
/>
<div v-if="row._buyStoredError" class="field-error">
{{ row._buyStoredError }}
</div>
</template>
<template #rejectsAmount="{ row }">
<ElInput
v-model="row.rejectsAmount"
clearable
size="small"
style="width: 70px"
@blur="validateField(row, 'rejectsAmount')"
/>
<div v-if="row._rejectsAmountError" class="field-error">
{{ row._rejectsAmountError }}
</div>
</template>
<template #locationName="{ row }">
<ElSelect
v-model="row.locationId"
placeholder="请选择"
clearable
style="width: 100px"
>
<ElOption
v-for="item in allLocationList"
:key="item.id"
:label="item.locationCode"
:value="item.id"
/>
</ElSelect>
</template>
</TableView>
</div>
<div class="batch-action">
<ElButton type="primary" @click="batchSetBuyStored">
申请数量>>本次入库数量
</ElButton>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="handleCancel">取消</ElButton>
<ElButton type="primary" @click="handleSubmit"> 入库 </ElButton>
</div>
</template>
</ElDialog>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { getUserListApi, LocationInfoGetAll } from '@/api/common'
import { loactionData } from '@/api/warehouse'
import {
getLocationListApi,
getStockingApplyOrderDetailById,
submitWarehousingApi,
} from '@/api/warehouse/stockingApplyOrder'
import {
StockingApplyOrderDetailData,
StockingApplyOrderDetailList,
LocationDataBySkuData,
} from '@/types/api/warehouse/stockingApplyOrder'
import { userData } from '@/types/api/user'
import { BigNumber } from 'bignumber.js'
const emit = defineEmits<{
(e: 'refresh'): void
}>()
const visible = ref(false)
const formRef = ref<FormInstance>()
const userList = ref<userData[]>([])
const locationList = ref<LocationDataBySkuData[]>([])
const selectedRows = ref<StockingApplyOrderDetailList[]>([])
const formData = ref<StockingApplyOrderDetailData>({
detailsList: [],
id: 0,
warehouseApplyNo: '',
warehouseName: '',
warehouseId: undefined,
checkerUserId: undefined,
remark: '',
})
type StockingApplyOrderDetailListWithError = StockingApplyOrderDetailList & {
_buyStoredError?: string
_rejectsAmountError?: string
}
const tableColumns = ref([
{
label: 'SKU图片',
prop: 'warehouseSkuImage',
width: 90,
align: 'center',
render: (row: StockingApplyOrderDetailList) => (
<el-image
preview-teleported
src={row.warehouseSkuImage}
preview-src-list={[row.warehouseSkuImage]}
z-index={9999}
style="width: 60px;height: 60px;text-align: center;"
/>
),
},
{
label: '商品名称',
prop: 'warehouseSkuName',
align: 'left',
},
{
label: '款号',
width: 140,
prop: 'productNo',
align: 'center',
},
{
label: '库存SKU',
prop: 'warehouseSku',
width: 180,
align: 'center',
},
{
label: '申请数量',
width: 120,
prop: 'shipmentQuantity',
align: 'right',
},
{
label: '入库数量',
prop: 'buyStored',
width: 100,
align: 'right',
slot: 'buyStored',
},
{
label: '不良品数',
prop: 'rejectsAmount',
width: 100,
align: 'right',
slot: 'rejectsAmount',
},
{
label: '实际质检数',
prop: 'finallyShipmentQuantity',
width: 100,
align: 'right',
render: (row: StockingApplyOrderDetailList) => {
const buyStored = new BigNumber(row.buyStored || 0).toNumber()
const rejectsAmount = new BigNumber(row.rejectsAmount || 0).toNumber()
return new BigNumber(buyStored).plus(rejectsAmount).toNumber()
},
},
{
label: '库位',
prop: 'locationId',
width: 140,
align: 'right',
slot: 'locationName',
},
])
const formRules: FormRules = {
checkerUserId: [
{ required: true, message: '请选择质检员', trigger: 'change' },
],
}
const loadUserList = async () => {
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await getUserListApi()
if (res.code === 200) {
userList.value = res.data || []
}
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const loadDetailList = async (id: number) => {
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await getStockingApplyOrderDetailById(id)
if (res.code !== 200) return
if (!res.data.checkerUserId) {
res.data.checkerUserId = JSON.parse(
localStorage.getItem('user') || '{}',
).id
}
const skuList = res.data.detailsList.map((item) => item.warehouseSku)
try {
const locationRes = await getLocationListApi(
skuList.join(','),
res.data.warehouseId as string | number,
)
if (locationRes.code !== 200) return
const { detailsList } = res.data
if (detailsList && detailsList.length > 0) {
if (detailsList.some((e) => (e.storedPending as number) > 0)) {
return ElMessageBox.alert(
'已存在待审核的入库单,请勿重复操作!',
'提示',
{
confirmButtonText: '确定',
type: 'warning',
},
)
}
}
locationList.value = locationRes.data || []
res.data.detailsList.forEach((item) => {
if (!item.buyStored) {
item.buyStored = item.shipmentQuantity
}
if (locationList.value.length > 0) {
const location = locationList.value.find(
(location) => location.warehouseSku === item.warehouseSku,
)
if (location) {
item.locationId = location.locationId
}
}
})
} catch (e) {
console.error(e)
}
formData.value = res.data
visible.value = true
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const allLocationList = ref<loactionData[]>([])
const loadAllLocationList = async (
warehouseId: string | number | undefined,
) => {
if (!warehouseId) return
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await LocationInfoGetAll(warehouseId)
if (res.code !== 200) return
allLocationList.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const open = async (row: {
id: number
warehouseId: string | number | undefined
}) => {
selectedRows.value = []
loadUserList()
await loadAllLocationList(row.warehouseId)
loadDetailList(row.id)
}
const handleCancel = () => {
visible.value = false
}
const handleSelectionChange = (rows: StockingApplyOrderDetailList[]) => {
selectedRows.value = rows
}
const batchSetBuyStored = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择数据')
return
}
selectedRows.value.forEach((row) => {
row.buyStored = row.shipmentQuantity
})
}
const validateField = (
row: StockingApplyOrderDetailListWithError,
field: 'buyStored' | 'rejectsAmount',
) => {
const value = row[field]
const strValue = value === 0 || value === '0' ? '0' : String(value ?? '')
const fieldLabel = field === 'buyStored' ? '入库数量' : '不良品数'
if (!strValue) {
if (field === 'buyStored') row._buyStoredError = '必填项'
} else if (!/^\d+$/.test(strValue)) {
if (field === 'buyStored') row._buyStoredError = '只能输入正整数'
else if (field === 'rejectsAmount')
row._rejectsAmountError = '只能输入正整数'
} else if (Number(strValue) === 0) {
if (field === 'buyStored') row._buyStoredError = `${fieldLabel}不能为0`
else if (field === 'rejectsAmount') row._rejectsAmountError = ''
} else {
if (field === 'buyStored') row._buyStoredError = ''
else row._rejectsAmountError = ''
}
}
const validateDetailsList = (): boolean => {
if (!formData.value.detailsList?.length) {
return true
}
let isValid = true
formData.value.detailsList.forEach((row) => {
const extendedRow = row as StockingApplyOrderDetailListWithError
validateField(extendedRow, 'buyStored')
validateField(extendedRow, 'rejectsAmount')
if (extendedRow._buyStoredError || extendedRow._rejectsAmountError) {
isValid = false
}
})
return isValid
}
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch {
return
}
if (!validateDetailsList()) {
ElMessage.warning('请检查入库数量和不良品数是否正确填写')
return
}
const overQuantitySkus = []
for (const item of formData.value.detailsList) {
if (Number(item.buyStored) > Number(item.shipmentQuantity)) {
overQuantitySkus.push(item.warehouseSku)
}
const locationCode = allLocationList.value.find(
(location: loactionData) => location.id === Number(item.locationId),
)?.locationCode
item.locationCode = locationCode as string | undefined
const buyStored = new BigNumber(item.buyStored || 0).toNumber()
const rejectsAmount = new BigNumber(item.rejectsAmount || 0).toNumber()
item.finallyShipmentQuantity = new BigNumber(buyStored)
.plus(rejectsAmount)
.toNumber()
}
if (overQuantitySkus.length > 0) {
ElMessageBox.alert(
`<div style="line-height: 1.6;"><span style="white-space: nowrap; flex-shrink: 0;">库存SKU:</span><span style="word-break: break-all; margin-left: 4px;">${overQuantitySkus.join(
',',
)}</span><span style="margin-top: 12px;">入库数量不能大于申请数量</span></div>`,
'提示',
{
confirmButtonText: '确定',
type: 'warning',
customClass: 'over-quantity-alert-box',
dangerouslyUseHTMLString: true,
},
)
return
}
const checkerName = userList.value.find(
(item) => item.id === formData.value.checkerUserId,
)?.account
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await submitWarehousingApi({ ...formData.value, checkerName })
if (res.code === 200) {
ElMessage.success('入库成功')
visible.value = false
emit('refresh')
}
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.submit-warehousing {
height: 600px;
display: flex;
flex-direction: column;
overflow: hidden;
.form-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: flex-start;
}
.table-section {
flex: 1;
overflow: hidden;
}
.batch-action {
margin-top: 10px;
}
.form-item {
flex: 0 0 auto;
margin-right: 20px;
:deep(.el-input),
:deep(.el-select) {
width: 180px;
}
}
.remark-item {
flex: 1;
:deep(.el-input) {
width: 100%;
}
}
}
.dialog-footer {
text-align: center;
}
.field-error {
color: #f56c6c;
font-size: 12px;
line-height: 1;
margin-top: 2px;
text-align: center;
}
</style>
<style>
.over-quantity-alert-box {
width: 1000px !important;
max-width: 90vw !important;
}
.over-quantity-alert-box .el-message-box__message {
word-break: break-all;
line-height: 1.6;
padding: 10px 0;
}
</style>
<template>
<div class="stocking-application-order-page card h-100 flex overflow-hidden">
<div class="order-status">
<ElTree
ref="treeRef"
default-expand-all
:expand-on-click-node="false"
:default-expanded-keys="[]"
:highlight-current="true"
node-key="code"
:data="treeData"
:props="{ children: 'children', label: 'name' }"
@node-click="nodeClick"
>
<template #default="{ data }">
<div class="tree-node">
<div class="tree-node-label">{{ data.name }}</div>
<div
v-if="data.countQuantity || data.countQuantity === 0"
class="tree-node-count"
>
{{ `(${data.countQuantity})` }}
</div>
</div>
</template>
</ElTree>
</div>
<div class="order-content flex-1 flex-column overflow-hidden">
<div class="header">
<div class="header-search-form">
<ElForm
ref="searchFormRef"
:model="searchForm"
inline
label-width="100px"
class="search-form"
>
<ElFormItem label="入库申请单号" prop="warehouseApplyNo">
<ElInput
v-model="searchForm.warehouseApplyNo"
clearable
placeholder="入库申请单号"
/>
</ElFormItem>
<ElFormItem label="备货仓库" prop="warehouseId">
<ElSelect
v-model="searchForm.warehouseId"
placeholder="请选择"
filterable
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="关联单号" prop="manageNo">
<ElInput
v-model="searchForm.manageNo"
clearable
placeholder="关联单号"
/>
</ElFormItem>
<ElFormItem label="库存SKU" prop="warehouseSku">
<ElInput
v-model="searchForm.warehouseSku"
clearable
placeholder="库存SKU"
/>
</ElFormItem>
<ElFormItem label="商品名称" prop="warehouseSkuName">
<ElInput
v-model="searchForm.warehouseSkuName"
clearable
placeholder="商品名称"
/>
</ElFormItem>
<ElFormItem class="form-item-buttons">
<ElButton type="primary" @click="() => search()">查询</ElButton>
<ElButton @click="() => resetSearchForm()">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="header-operation"></div>
</div>
<div class="table-content">
<SplitDiv size="60">
<template #top>
<div v-loading="loading" class="table-list flex-1 overflow-hidden">
<TableView
ref="tableRef"
highlight-current-row
:selectionable="true"
:serial-numberable="true"
:columns="tableColumns"
:paginated-data="tableData"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
>
<template #action="{ row }">
<ElButton
v-if="status === 'TO_BE_RECEIPT'"
link
type="success"
@click="warehouseReceipt(row)"
>仓库收货</ElButton
>
<ElButton
v-if="status === 'TO_BE_INSPECTED'"
link
type="warning"
@click="submitWarehousing(row)"
>提交入库</ElButton
>
</template>
</TableView>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[50, 100, 200, 300, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</template>
<template #bottom>
<StockingApplyOrderDetailTabs :selected-row="selectedRow" />
</template>
</SplitDiv>
</div>
</div>
<!-- 提交入库弹框 -->
<SubmitWarehousingDialog ref="submitWarehousingDialogRef" @refresh="onRefresh" />
</div>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import { TreeData } from '@/types/api/supply/stockingOrder'
import { loadWarehouseListApi } from '@/api/common'
import { WarehouseListData } from '@/types'
import usePageList from '@/utils/hooks/usePageList'
import {
getStockingApplyOrderListApi,
getStockingApplyOrderTreeApi,
warehouseReceiptApi,
} from '@/api/warehouse/stockingApplyOrder'
import { TableData, SearchForm } from '@/types/api/warehouse/stockingApplyOrder'
import { useValue } from '@/utils/hooks/useValue'
import { ElTag, ElMessageBox, ElMessage } from 'element-plus'
import StockingApplyOrderDetailTabs from './StockingApplyOrderDetailTabs.vue'
import SubmitWarehousingDialog from './SubmitWarehousingDialog.vue'
const treeData = ref<TreeData[]>([])
const treeRef = ref()
const tableRef = ref()
const selectedRow = ref<TableData | null>(null)
const submitWarehousingDialogRef = ref<InstanceType<typeof SubmitWarehousingDialog>>()
const [searchForm, resetSearchForm] = useValue<SearchForm>({} as SearchForm)
const status = ref<string>('TO_BE_RECEIPT')
const warehouseList = ref<WarehouseListData[]>([])
const getStockingApplyOrderStatusName = (status: string) => {
const statusText = treeData.value[0]?.children?.find(
(item) => item.code === status,
)?.name
return statusText || ''
}
const tableColumns = computed(() => [
{
label: '入库申请单号',
prop: 'warehouseApplyNo',
width: 160,
align: 'center',
},
{
label: '关联单号',
prop: 'manageNo',
width: 160,
align: 'center',
},
{
label: '单据状态',
prop: 'storeStatus',
width: 120,
align: 'center',
render: (row: TableData) => {
return (
<ElTag type="primary">
{getStockingApplyOrderStatusName(row.storeStatus)}
</ElTag>
)
},
},
{
label: '入库仓库',
prop: 'warehouseName',
width: 160,
align: 'center',
},
{
label: 'SKU个数',
prop: 'skuTotal',
width: 100,
align: 'right',
},
{
label: '申请数量',
prop: 'total',
width: 100,
align: 'right',
},
{
label: '入库数量',
prop: 'buyStored',
width: 100,
align: 'right',
},
{
label: '预计到货日期',
prop: 'expectDeliveryTime',
width: 160,
align: 'center',
},
{
label: '实际到货日期',
prop: 'deliveryTime',
width: 160,
align: 'center',
},
{
label: '制单人',
prop: 'createUserName',
width: 100,
align: 'center',
},
{
label: '制单时间',
prop: 'createTime',
width: 160,
align: 'center',
},
{
label: '操作',
prop: 'action',
fixed: 'right',
width: 100,
align: 'center',
slot: 'action',
},
])
const selection = ref<TableData[]>([])
const loadWarehouseList = async () => {
try {
const res = await loadWarehouseListApi()
if (res.code !== 200) return
warehouseList.value = res.data || []
} catch (e) {
console.error(e)
}
}
const loadTreeData = async () => {
try {
const res = await getStockingApplyOrderTreeApi()
if (res.code !== 200) return
treeData.value = [
{ code: '-1', name: '全部', children: res.data || [] } as TreeData,
]
await nextTick(() => {
treeRef.value!.setCurrentKey(status.value, true)
})
} catch (e) {
console.error(e)
}
}
const nodeClick = (data: TreeData) => {
status.value = data.code
search()
}
const {
currentPage,
pageSize,
loading,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList<TableData>({
query: (page, pageSize) =>
getStockingApplyOrderListApi(
{
...searchForm.value,
storeStatus: status.value === '-1' ? undefined : status.value,
},
page,
pageSize,
).then(async (res) => {
const { records } = res.data
await nextTick(() => {
tableRef.value.setCurrentRow(records[0])
selectedRow.value = (records as never)[0]
})
return res.data
}),
initLoad: true,
})
const handleRowClick = (row: TableData) => {
if (row) {
selectedRow.value = row
} else {
selectedRow.value = null
}
}
const handleSelectionChange = (val: TableData[]) => {
selection.value = val
}
const warehouseReceipt = async (row: TableData) => {
try {
await ElMessageBox.confirm(
`<div>
<p style="margin-bottom: 10px;">SKU个数:<span style="color: #F56C6C; font-weight: bold;">${row.skuTotal}</span>,预计到货数量:<span style="color:#F56C6C; font-weight: bold;">${row.total}</span></p>
<p>确认仓库是否收到货?</p>
</div>`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true,
},
)
} catch {
return
}
try {
const res = await warehouseReceiptApi(row.id)
if (res.code !== 200) return
ElMessage.success('收货成功')
search()
loadTreeData()
} catch (e) {
console.error(e)
}
}
const submitWarehousing = (row: TableData) => {
submitWarehousingDialogRef.value?.open({
id: row.id,
warehouseId: row.warehouseId,
})
}
const onRefresh = () => {
search()
loadTreeData()
}
onMounted(async () => {
await loadWarehouseList()
await loadTreeData()
})
</script>
<style lang="scss" scoped>
.order-status {
width: 160px;
:deep(.el-tree-node__content) {
height: 30px;
line-height: 30px;
}
:deep(.el-tree-node__label) {
font-size: 13px;
cursor: pointer;
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.is-current) {
.tree-node-label,
.tree-node-count {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.tree-node-label,
.tree-node-count {
background-color: transparent !important;
color: black !important;
}
}
}
:deep(.el-tree-node__label) {
font-size: 13px;
color: rgb(96, 98, 102);
cursor: pointer;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.el-tree-node__label) {
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.is-current) {
.el-tree-node__label {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.el-tree-node__label {
background-color: transparent !important;
color: black !important;
}
}
}
}
.tree-node {
display: flex;
color: #333;
font-weight: 500;
}
.table-content {
flex: 1;
margin-top: 10px;
overflow: hidden;
:deep(#top) {
height: 100%;
}
}
.search-form {
display: grid;
grid-template-columns: repeat(6, 1fr);
align-items: flex-start;
:deep(.el-form-item) {
margin-right: 10px;
margin-bottom: 10px;
}
}
</style>
...@@ -60,11 +60,11 @@ ...@@ -60,11 +60,11 @@
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem label="备货单号"> <ElFormItem label="备货计划单号">
<ElInput <ElInput
v-model.trim="searchForm.inNo" v-model.trim="searchForm.inNo"
clearable clearable
placeholder="请输入备货单号" placeholder="请输入备货计划单号"
style="width: 160px" style="width: 160px"
/> />
</ElFormItem> </ElFormItem>
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
align="center" align="center"
/> />
<ElTableColumn <ElTableColumn
label="备货单号" label="备货计划单号"
show-overflow-tooltip show-overflow-tooltip
prop="inNo" prop="inNo"
width="130" width="130"
...@@ -553,7 +553,7 @@ ...@@ -553,7 +553,7 @@
inline inline
label-width="90px" label-width="90px"
> >
<ElFormItem label="备货单号" prop="account"> <ElFormItem label="备货计划单号" prop="account">
<ElInput v-model.trim="editForm.inNo" clearable disabled /> <ElInput v-model.trim="editForm.inNo" clearable disabled />
</ElFormItem> </ElFormItem>
<ElFormItem label="工厂编号:" prop="factoryCode"> <ElFormItem label="工厂编号:" prop="factoryCode">
...@@ -926,9 +926,7 @@ ...@@ -926,9 +926,7 @@
maxlength="1000" maxlength="1000"
show-word-limit show-word-limit
/> />
<div style="margin-top: 12px; color: #777"> <div style="margin-top: 12px; color: #777">多个字段使用","隔开</div>
{{ '多个字段使用_##_,_##_隔开' }}
</div>
</div> </div>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
...@@ -943,7 +941,7 @@ ...@@ -943,7 +941,7 @@
width="500px" width="500px"
> >
<ElTable :data="updateShipmentNumberForm" border> <ElTable :data="updateShipmentNumberForm" border>
<ElTableColumn prop="inNo" label="备货单号" /> <ElTableColumn prop="inNo" label="备货计划单号" />
<ElTableColumn prop="shipmentNumber" label="物流单号"> <ElTableColumn prop="shipmentNumber" label="物流单号">
<template #default="{ row }"> <template #default="{ row }">
<el-input <el-input
...@@ -968,7 +966,7 @@ ...@@ -968,7 +966,7 @@
<el-input <el-input
ref="scanInputRef" ref="scanInputRef"
v-model="scanInput" v-model="scanInput"
placeholder="请输入备货单号" placeholder="请输入备货计划单号"
style="width: 90%" style="width: 90%"
clearable clearable
@keyup.enter="scan" @keyup.enter="scan"
...@@ -980,7 +978,7 @@ ...@@ -980,7 +978,7 @@
<el-descriptions-item label="备货仓库"> <el-descriptions-item label="备货仓库">
{{ scanData.warehouseName || '-' }} {{ scanData.warehouseName || '-' }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="备货单号"> <el-descriptions-item label="备货计划单号">
{{ scanData.inNo || '-' }} {{ scanData.inNo || '-' }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="物流单号"> <el-descriptions-item label="物流单号">
...@@ -2063,7 +2061,7 @@ const scan = async () => { ...@@ -2063,7 +2061,7 @@ const scan = async () => {
console.log(scanInput.value, 'scanInput.value') console.log(scanInput.value, 'scanInput.value')
if (!scanInput.value) { if (!scanInput.value) {
return ElMessage.warning('请输入备货单号') return ElMessage.warning('请输入备货计划单号')
} }
scanInput.value = scanInput.value.trim() scanInput.value = scanInput.value.trim()
// 将中文破折号转换为下划线 // 将中文破折号转换为下划线
...@@ -2289,7 +2287,6 @@ $border: solid 1px #ddd; ...@@ -2289,7 +2287,6 @@ $border: solid 1px #ddd;
::v-deep(.el-tree-node) { ::v-deep(.el-tree-node) {
cursor: pointer; cursor: pointer;
margin-bottom: 5px;
} }
::v-deep(.el-tree-node__label) { ::v-deep(.el-tree-node__label) {
......
...@@ -6,14 +6,13 @@ import { ...@@ -6,14 +6,13 @@ import {
updateLocationApi, updateLocationApi,
updateProductNoApi, updateProductNoApi,
updateCustomSkuApi, updateCustomSkuApi,
LocationInfoGetAll,
getWarehouseInventoryInfo, getWarehouseInventoryInfo,
factoryLogWarehouseLog, factoryLogWarehouseLog,
LogListData, LogListData,
WarehouseWarning, WarehouseWarning,
factoryWarehouseInventoryPrint, factoryWarehouseInventoryPrint,
exportWarehouseInfo, exportWarehouseInfo,
loactionData, loactionData, getLocalFactoryList,
} from '@/api/warehouse.ts' } from '@/api/warehouse.ts'
import { AnyObject } from '@/types/api/warehouse' import { AnyObject } from '@/types/api/warehouse'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
...@@ -21,11 +20,18 @@ import SplitDiv from '@/components/splitDiv/splitDiv.vue' ...@@ -21,11 +20,18 @@ import SplitDiv from '@/components/splitDiv/splitDiv.vue'
import { filePath } from '@/api/axios.ts' import { filePath } from '@/api/axios.ts'
import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts' import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts'
import { getInventoryLowerLimitApi } from '@/api/externalAuth' import { getInventoryLowerLimitApi } from '@/api/externalAuth'
interface UserMark{
userId: number | string
userMark: string
userName: string
}
import { LocationInfoGetAll } from '@/api/common'
const searchForm = ref({ const searchForm = ref({
warehouseId: '', warehouseId: '',
inventoryStart: '', inventoryStart: '',
inventoryEnd: '', inventoryEnd: '',
userMark: '',
productNo: '', productNo: '',
skuName: '', skuName: '',
warehouseSku: '', warehouseSku: '',
...@@ -38,6 +44,7 @@ const searchForm = ref({ ...@@ -38,6 +44,7 @@ const searchForm = ref({
occupyInventoryStart: '', occupyInventoryStart: '',
}) })
const warehouseList = ref<warehouseInfo[]>([]) const warehouseList = ref<warehouseInfo[]>([])
const userMarkList = ref<UserMark[]>([])
const logList = ref<LogListData[]>([]) const logList = ref<LogListData[]>([])
const selections = ref<WarehouseWarning[]>([]) const selections = ref<WarehouseWarning[]>([])
const leftData = ref<WarehouseWarning[]>([]) const leftData = ref<WarehouseWarning[]>([])
...@@ -56,6 +63,13 @@ const isSameWarehouse = computed(() => { ...@@ -56,6 +63,13 @@ const isSameWarehouse = computed(() => {
const modifyLocationDialog = ref(false) const modifyLocationDialog = ref(false)
const modifyProductNoDialog = ref(false) const modifyProductNoDialog = ref(false)
const modifyCustomSkuDialog = ref(false) const modifyCustomSkuDialog = ref(false)
const getUserMark = async () => {
const {data} = await getLocalFactoryList()
userMarkList.value = [
...[{userMark:'九猫商品', userId: 'JMP',userName:''}],
...data
]
}
/** 修改库位 */ /** 修改库位 */
const locationForm = ref({ const locationForm = ref({
...@@ -351,6 +365,7 @@ const getInventoryLowerLimit = async () => { ...@@ -351,6 +365,7 @@ const getInventoryLowerLimit = async () => {
} }
onMounted(async () => { onMounted(async () => {
getData() getData()
getUserMark()
getWarehouse() getWarehouse()
getInventoryLowerLimit() getInventoryLowerLimit()
}) })
...@@ -387,6 +402,7 @@ onMounted(async () => { ...@@ -387,6 +402,7 @@ onMounted(async () => {
prop="productNo" prop="productNo"
label="款号 " label="款号 "
></el-table-column> ></el-table-column>
<el-table-column align="center" prop="number" label="打印数量"> <el-table-column align="center" prop="number" label="打印数量">
<template #default="{ row }"> <template #default="{ row }">
<el-input <el-input
...@@ -407,10 +423,11 @@ onMounted(async () => { ...@@ -407,10 +423,11 @@ onMounted(async () => {
</el-dialog> </el-dialog>
<el-card> <el-card>
<el-form <el-form
ref="searchFormRef"
class="search-form" class="search-form"
inline inline
label-width="80px"
:model="searchForm" :model="searchForm"
ref="searchFormRef"
> >
<el-form-item label="仓库"> <el-form-item label="仓库">
<el-select <el-select
...@@ -418,7 +435,7 @@ onMounted(async () => { ...@@ -418,7 +435,7 @@ onMounted(async () => {
clearable clearable
filterable filterable
placeholder="请输入仓库" placeholder="请输入仓库"
style="width: 140px" style="width: 175px"
> >
<el-option <el-option
v-for="item in warehouseList" v-for="item in warehouseList"
...@@ -497,8 +514,21 @@ onMounted(async () => { ...@@ -497,8 +514,21 @@ onMounted(async () => {
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="所属客户">
<el-select
v-model="searchForm.userMark"
clearable
filterable
placeholder="请输入所属客户"
style="width: 140px"
>
<el-option
v-for="user in userMarkList" :key="user.userMark" :label="user.userMark" :value="user.userMark==='九猫商品'?'JMP':user.userMark"
></el-option>
</el-select>
</el-form-item>
<el-form-item style="margin-top: 5px"> <el-form-item style="margin-top: 5px">
<el-button type="primary" @click="getData" ref="searchBtnRef" <el-button ref="searchBtnRef" type="primary" @click="getData"
>查询</el-button >查询</el-button
> >
</el-form-item> </el-form-item>
...@@ -618,6 +648,11 @@ onMounted(async () => { ...@@ -618,6 +648,11 @@ onMounted(async () => {
></el-table-column> ></el-table-column>
<el-table-column <el-table-column
align="center" align="center"
prop="userMark"
label="所属客户"
></el-table-column>
<el-table-column
align="center"
prop="unit" prop="unit"
label="单位" label="单位"
width="60" width="60"
......
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