增加出库管理

main
HUOJIN\92525 2025-04-25 18:16:53 +08:00
parent cb6ba2c7ce
commit e4c46b4804
16 changed files with 1620 additions and 100 deletions

View File

@ -0,0 +1,48 @@
/**
* api
*
* @Author:
* @Date: 2024-12-26 15:42:11
* @Copyright
*/
import { postRequest, getRequest } from '/@/lib/axios';
export const pickApi = {
/**
* @author
*/
queryPage : (param:object) => {
return postRequest('/pick/queryPage', param);
},
/**
* @author
*/
add: (param:object) => {
return postRequest('/pick/add', param);
},
/**
* @author
*/
update: (param:object) => {
return postRequest('/pick/update', param);
},
/**
* @author
*/
delete: (pickId:number) => {
return getRequest('/pick/delete',{pickId});
},
/**
* @author
*/
batchDelete: (idList:number[]) => {
return postRequest('/pick/batchDelete', idList);
},
};

View File

@ -0,0 +1,66 @@
/*
*
*
*/
import {SmartEnum} from '/@/types/smart-enum';
//单据类型
export const PICK_ORDER_TYPE_ENUM: SmartEnum<string> = {
SALES: {
value: 'SALES',
desc: '销售出库',
},
TRANSFER: {
value: 'TRANSFER',
desc: '调拨出库',
},
RETURN_OUT: {
value: 'RETURN_OUT',
desc: '退货出库',
},
OTHER: {
value: 'OTHER',
desc: '其他出库',
},
};
//单据状态
export const PICK_ORDER_STATUS_ENUM: SmartEnum<string> = {
CREATED: {
value: 'CREATED',
desc: '已创建',
},
APPROVING: {
value: 'APPROVING',
desc: '已提交',
},
APPROVED: {
value: 'APPROVED',
desc: '已审核',
},
PARTIALLY_ALLOCATED: {
value: 'PARTIALLY_ALLOCATED',
desc: '部分分配',
},
ALLOCATED: {
value: 'ALLOCATED',
desc: '已分配',
},
PARTIALLY_PICKED: {
value: 'PARTIALLY_PICKED',
desc: '部分拣货',
},
COMPLETED: {
value: 'COMPLETED',
desc: '已完成',
},
CANCELLED: {
value: 'CANCELLED',
desc: '已取消',
},
};
export default {
PICK_ORDER_TYPE_ENUM,
PICK_ORDER_STATUS_ENUM
};

View File

@ -0,0 +1,48 @@
/**
* api
*
* @Author:
* @Date: 2025-01-06 11:04:32
* @Copyright
*/
import { postRequest, getRequest } from '/@/lib/axios';
export const pickDetailApi = {
/**
* @author
*/
queryPage : (param:object) => {
return postRequest('/pickDetail/queryPage', param);
},
/**
* @author
*/
add: (param:object) => {
return postRequest('/pickDetail/add', param);
},
/**
* @author
*/
update: (param:object) => {
return postRequest('/pickDetail/update', param);
},
/**
* @author
*/
delete: (pickDetailId:number) => {
return getRequest('/pickDetail/delete',{pickDetailId});
},
/**
* @author
*/
batchDelete: (idList:number[]) => {
return postRequest('/pickDetail/batchDelete', idList);
},
};

View File

@ -0,0 +1,11 @@
/**
*
*
* @Author:
* @Date: 2025-01-06 11:04:32
* @Copyright
*/
export default {
};

View File

@ -10,9 +10,9 @@
import menu from './system/menu-const';
import goods from './business/erp/goods-const';
import category from './business/erp/category-const';
import { LOGIN_DEVICE_ENUM } from './system/login-device-const';
import { FLAG_NUMBER_ENUM, GENDER_ENUM, USER_TYPE_ENUM } from './common-const';
import { LAYOUT_ENUM } from './layout-const';
import {LOGIN_DEVICE_ENUM} from './system/login-device-const';
import {FLAG_NUMBER_ENUM, GENDER_ENUM, USER_TYPE_ENUM} from './common-const';
import {LAYOUT_ENUM} from './layout-const';
import file from './support/file-const';
import notice from './business/oa/notice-const';
import loginLog from './support/login-log-const';
@ -23,6 +23,7 @@ import changeLogConst from './support/change-log-const';
import jobConst from './support/job-const';
import USAGE_STATUS_ENUM from './business/wms/base/usagestatus-const'
import ASN_ORDER_TYPE_ENUM from '/@/api/business/wms/receive/asn/asn-const'
import PICK_ORDER_TYPE_ENUM from '/@/api/business/wms/shipping/pick/pick-const'
export default {
FLAG_NUMBER_ENUM,
@ -42,5 +43,6 @@ export default {
...changeLogConst,
...jobConst,
...USAGE_STATUS_ENUM,
...ASN_ORDER_TYPE_ENUM
...ASN_ORDER_TYPE_ENUM,
...PICK_ORDER_TYPE_ENUM
};

View File

@ -8,7 +8,7 @@
* @Copyright 1024 https://1024lab.net Since 2012
*/
import { SmartEnum } from '/@/types/smart-enum';
import {SmartEnum} from '/@/types/smart-enum';
// 业务类型
export const DATA_TRACER_TYPE_ENUM: SmartEnum<number> = {
@ -28,9 +28,9 @@ export const DATA_TRACER_TYPE_ENUM: SmartEnum<number> = {
value: 4,
desc: '入库',
},
ASN_DETAIL: {
PICK: {
value: 5,
desc: '入库明细',
desc: '出库',
},
};

View File

@ -46,7 +46,11 @@ export const TABLE_ID_CONST = {
ASN_DETAIL: businessWMSInitTableId + 8,//入库明细
ASN_TASK: businessWMSInitTableId + 9,//入库详情
INVENTORY: businessWMSInitTableId + 10,//库存
PICK: businessWMSInitTableId + 10,//出库单
PICK_DETAIL: businessWMSInitTableId + 11,//出库明细
PICK_TASK: businessWMSInitTableId + 12,//出库详情
INVENTORY: businessWMSInitTableId + 13,//库存
}
},

View File

@ -116,7 +116,6 @@
编辑
</a-button>
<a-button @click="onDelete(record)" type="primary" danger ghost style="width:60px;height: 32px"
:disabled="record.orderQuantity>0 && record.orderQuantity-record.receivedQuantity==0"
v-privilege="'asn:delete'">
删除

View File

@ -193,7 +193,6 @@ async function queryData() {
const queryResult = await asnDetailApi.queryPage(queryForm);
tableData.value = queryResult.data.list;
total.value = queryResult.data.total;
console.log('刷新啦啦啦')
} catch (e) {
smartSentry.captureError(e);
} finally {

View File

@ -56,7 +56,7 @@ function show(rowData: object, asnId: number) {
if (rowData && !_.isEmpty(rowData)) {
Object.assign(form, rowData);
}
//id
//asnId
form.asnId = asnId;
visibleFlag.value = true;
nextTick(() => {

View File

@ -1,5 +1,5 @@
<!--
* 入库明细
* 收货明细
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-08-15 20:15:49
@ -18,14 +18,6 @@
</div>
<div class="smart-table-setting-block">
<a-button class="button-style" type="primary" danger v-privilege="'receive:batchReturn'"
@click="confirmBatchReturn"
:disabled="props.asnId==0 || selectedRowKeyList.length == 0">
<template #icon>
<EditOutlined/>
</template>
批量退货
</a-button>
<TableOperator v-model="columns" :tableId="TABLE_ID_CONST.BUSINESS.WMS.ASN_TASK" :refresh="queryData"/>
</div>
@ -72,11 +64,8 @@ import {reactive, ref, watch} from 'vue';
import {PAGE_SIZE, PAGE_SIZE_OPTIONS} from '/@/constants/common-const';
import {smartSentry} from '/@/lib/smart-sentry';
import {TABLE_ID_CONST} from "/@/constants/support/table-id-const";
import TableOperator from "/@/components/support/table-operator/index.vue";
import TableOperator from '/@/components/support/table-operator/index.vue';
import {taskApi} from "/@/api/business/wms/task/task-api";
import {receiveApi} from "/@/api/business/wms/receive/receive-api";
import {message, Modal} from "ant-design-vue";
import {SmartLoading} from '/@/components/framework/smart-loading';
const props = defineProps({
asnId: {
@ -123,6 +112,7 @@ const defaultQueryForm = {
pageSize: PAGE_SIZE,
asnId: 0,
keyword: undefined,
taskType: 'ASN'//
}
//
@ -171,46 +161,6 @@ function onSelectChange(selectedRowKeys: any) {
selectedRowKeyList.value = selectedRowKeys;
}
//退
function confirmBatchReturn() {
Modal.confirm({
title: '提示',
content: '确定是否批量退货?',
okText: '确认',
okType: 'danger',
onOk() {
requestBatchReturn();
},
cancelText: '取消',
onCancel() {
},
});
}
//退
async function requestBatchReturn() {
try {
SmartLoading.show();
let param = {
asnId: props.asnId,
taskIds: selectedRowKeyList.value,
};
const response = await receiveApi.batchReturn(param);
const {error, success} = JSON.parse(response.data);
if (error) {
message.error(error);
}
if (success) {
message.success(success);
}
await queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
watch(
() => props.asnId,
(e) => {
@ -220,6 +170,7 @@ watch(
},
{immediate: true}
);
defineExpose({
queryData
});

View File

@ -0,0 +1,261 @@
<!--
* 出库单 详情
*
-->
<template>
<div class="detail-header">
<a-page-header :title="form.pickId ? '编辑出库单' : '添加出库单'" :avatar="{ src: '' }">
<template #extra>
<a-button @click="onSubmit" type="primary" v-privilege="'pick:add'" :disabled="form.status=='COMPLETED'">
<template #icon>
<EditOutlined/>
</template>
保存
</a-button>
<a-button @click="onBack" type="primary" danger>
<template #icon>
<ArrowLeftOutlined/>
</template>
返回
</a-button>
</template>
<div>
<a-form ref="formRef" :model="form" :rules="rules">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="客户" name="customerId">
<CustomerSelect style="width: 100%" v-model:value="form.customerId" :disabled-flag="true"
@change="changeCustomerSelect"/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="客户订单号" name="customerNumber">
<a-input style="width: 100%" v-model:value="form.customerNumber" :readonly="form.pickId>0"
placeholder="客户订单号"/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="单据类型" name="orderType">
<SmartEnumSelect style="width: 100%" enum-name="PICK_ORDER_TYPE_ENUM" v-model:value="form.orderType"/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="单位" name="addressId">
<AddressSelect style="width: 100%" v-model:value="form.addressId" :disabled-flag="true"
@change="changeAddressSelect"/>
</a-form-item>
</a-col>
<a-col :span="4">
<a-form-item label="联系人" name="person">
<a-input style="width: 100%" v-model:value="form.person" placeholder="联系人"/>
</a-form-item>
</a-col>
<a-col :span="4">
<a-form-item label="电话" name="telephone">
<a-input style="width: 100%" v-model:value="form.telephone" placeholder="电话"/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="订单日期" name="orderDate">
<a-date-picker style="width: 100%" value-format="YYYY-MM-DD" v-model:value="form.orderDate"/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<a-form-item label="地址" name="address">
<a-textarea v-model:value="form.address" style="width: 100%; height: 100px; outline: none;" auto-size/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</a-page-header>
</div>
<a-card class="smart-margin-top10" size="small">
<a-tabs @change="handleTabChange">
<a-tab-pane key="pickDetail" tab="出库明细">
<ShippingDetailList ref="shippingDetailListRef" :pickId="form.pickId"/>
</a-tab-pane>
<a-tab-pane key="pickTask" tab="出库详情">
<PickTaskList ref="pickTaskListRef" :pickId="form.pickId"/>
</a-tab-pane>
<a-tab-pane key="dataTracer" tab="操作记录">
<DataTracer ref="dataTracerRef" :dataId="form.pickId" :type="DATA_TRACER_TYPE_ENUM.PICK.value"/>
</a-tab-pane>
</a-tabs>
</a-card>
</template>
<script setup lang="ts">
import {onMounted, reactive, ref} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import DataTracer from '/@/components/support/data-tracer/index.vue';
import {DATA_TRACER_TYPE_ENUM} from '/@/constants/support/data-tracer-const';
import CustomerSelect from "/@/views/business/wms/base/customer/customer-select.vue";
import SmartEnumSelect from "/@/components/framework/smart-enum-select/index.vue";
import AddressSelect from "/@/views/business/wms/base/address/address-select.vue";
import dayjs from 'dayjs';
import {message} from "ant-design-vue";
import {SmartLoading} from "/@/components/framework/smart-loading";
import {pickApi} from "/@/api/business/wms/shipping/pick/pick-api";
import {smartSentry} from "/@/lib/smart-sentry";
import PickTaskList from "/@/views/business/wms/shipping/pickTask/pick-task-list.vue";
import ShippingDetailList from "/@/views/business/wms/shipping/pick/shipping-detail-list.vue";
//
const formRef = ref();
const formDefault = {
pickId: undefined,//id
customerId: undefined, //
pickNumber: undefined, //
customerNumber: undefined, //
orderType: undefined,//
status: undefined,//
orderDate: undefined,//
addressId: undefined,//
person: undefined,//
telephone: undefined,//
address: undefined,//
};
let form = reactive({...formDefault});
//
const rules = {
customerId: [{required: true, message: '客户 必填'}],
customerNumber: [{required: true, message: '客户订单号 必填'}],
orderType: [{required: true, message: '单据类型 必填'}],
orderDate: [{required: true, message: '订单日期 必填'}],
addressId: [{required: true, message: '收货单位 必填'}],
};
//
function changeCustomerSelect(selectValue: any) {
if (selectValue) {
form.customerId = selectValue.customerId;
}
}
//
function changeAddressSelect(selectValue: any) {
if (selectValue) {
form.addressId = selectValue.addressId;
form.address = selectValue.address;
form.person = selectValue.person;
form.telephone = selectValue.telephone;
}
}
//
const route = useRoute();
onMounted(() => {
//
const id = route.query.id;
if (typeof id === 'string') {
// sessionStorage
const res = getDataFromSessionStorage(id);
//
if (res != null && res.pickId > 0) {
form.pickId = res.pickId;
form.customerId = res.customerId;
form.pickNumber = res.pickNumber;
form.customerNumber = res.customerNumber;
form.status = res.status;
form.orderType = res.orderType;
form.orderDate = res.orderDate;
form.addressId = res.addressId;
form.person = res.person;
form.telephone = res.telephone;
form.address = res.address;
} else {
//
form.orderDate = dayjs().format('YYYY-MM-DD');
}
//
sessionStorage.removeItem(id);
}
});
//sessionStorage
function getDataFromSessionStorage(id: string) {
try {
const data = sessionStorage.getItem(id);
return data ? JSON.parse(data) : null;
} catch (error) {
message.error('解析 sessionStorage 数据时出错:');
return null;
}
}
//
async function onSubmit() {
try {
await formRef.value.validateFields();
await save();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
///
async function save() {
SmartLoading.show();
try {
if (form.pickId) {
let res = await pickApi.update(form);
form.pickId = res.data.pickId;
} else {
let res = await pickApi.add(form);
form.pickId = res.data.pickId;
}
message.success('操作成功');
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
//
let router = useRouter();
function onBack() {
router.push({
path: '/wms/shipping/pick/pick-list',
query: {refresh: '1'}
});
}
//
const shippingDetailListRef = ref()
const pickTaskListRef = ref()
const dataTracerRef = ref()
function handleTabChange(activeKey: string) {
if (activeKey === 'pickDetail' && shippingDetailListRef.value) {
shippingDetailListRef.value.queryData();
} else if (activeKey === 'pickTask' && pickTaskListRef.value) {
pickTaskListRef.value.queryData();
} else if (activeKey === 'dataTracer' && dataTracerRef.value) {
dataTracerRef.value.ajaxQuery();
}
}
</script>
<style lang="less" scoped>
.detail-header {
background-color: #fff;
padding: 10px;
}
</style>

View File

@ -0,0 +1,471 @@
<!--
* 出库单
*
* @Author: 霍锦
* @Date: 2025-03-26 15:10:02
* @Copyright 友仓
-->
<template>
<!---------- 查询表单form begin ----------->
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="客户订单号" class="smart-query-form-item">
<a-input style="width: 200px" v-model:value="queryForm.customerNumber" placeholder="客户订单号"/>
</a-form-item>
<a-form-item class="smart-query-form-item">
<a-button type="primary" @click="onSearch" class="smart-margin-left10" v-privilege="'pick:query'">
<template #icon>
<SearchOutlined/>
</template>
查询
</a-button>
<a-button @click="resetQuery" class="smart-margin-left10" v-privilege="'pick:query'">
<template #icon>
<ReloadOutlined/>
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<!---------- 查询表单form end ----------->
<a-card size="small" :bordered="false" :hoverable="true">
<!---------- 表格操作行 begin ----------->
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button @click="showForm" type="primary" v-privilege="'pick:add'">
<template #icon>
<PlusOutlined/>
</template>
新建
</a-button>
<a-button @click="confirmBatchDelete" type="primary" danger v-privilege="'pick:batchDelete'"
:disabled="selectedRowKeyList.length == 0">
<template #icon>
<DeleteOutlined/>
</template>
批量删除
</a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator v-model="columns" :tableId="TABLE_ID_CONST.BUSINESS.WMS.PICK" :refresh="queryData"/>
</div>
</a-row>
<!---------- 表格操作行 end ----------->
<!---------- 表格 begin ----------->
<a-table
size="small"
:dataSource="tableData"
:columns="columns"
rowKey="pickId"
bordered
:loading="tableLoading"
:pagination="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
@expand="querySubTableData"
>
<template #expandedRowRender="{ record }">
<a-table
size="small"
:columns="subColumns"
:data-source="subTableData"
:pagination="false"
rowKey="pickDetailId"
>
<template #summary>
<a-table-summary-row style="font-weight: bold">
<a-table-summary-cell :index="0" align="center">合计</a-table-summary-cell>
<a-table-summary-cell :index="1">&nbsp;</a-table-summary-cell>
<a-table-summary-cell :index="2" align="center">
<a-typography-text type="danger">{{ totals.totalOrderQuantity }}</a-typography-text>
</a-table-summary-cell>
<a-table-summary-cell :index="3" align="center">
<a-typography-text type="danger">{{ totals.totalAllocatedQuantity }}</a-typography-text>
</a-table-summary-cell>
<a-table-summary-cell :index="4" align="center">
<a-typography-text type="danger">{{ totals.totalPickedQuantity }}</a-typography-text>
</a-table-summary-cell>
</a-table-summary-row>
</template>
</a-table>
</template>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'pickNumber'">
<a @click="showForm(record)">{{ record.pickNumber }}</a>
</template>
<template v-if="column.dataIndex === 'orderType'">
{{ $smartEnumPlugin.getDescByValue('PICK_ORDER_TYPE_ENUM', text) }}
</template>
<template v-if="column.dataIndex === 'status'">
<a-tag :color="getStatusColor(text)">
{{ $smartEnumPlugin.getDescByValue('PICK_ORDER_STATUS_ENUM', text) }}
</a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-space>
<a-button @click="showForm(record)" type="primary" ghost style="width:60px;height: 32px"
v-privilege="'pick:update'">
编辑
</a-button>
<a-button @click="onDelete(record)" type="primary" danger ghost style="width:60px;height: 32px"
v-privilege="'pick:delete'">
删除
</a-button>
</a-space>
</div>
</template>
</template>
</a-table>
<!---------- 表格 end ----------->
<div class="smart-query-table-page">
<a-pagination
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total:number) => `共${total}条`"
/>
</div>
</a-card>
</template>
<script setup lang="ts">
import {computed, reactive, ref, onMounted, watch} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {SmartLoading} from '/@/components/framework/smart-loading';
import {PAGE_SIZE_OPTIONS} from '/@/constants/common-const';
import {smartSentry} from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import {pickApi} from '/@/api/business/wms/shipping/pick/pick-api';
import {pickDetailApi} from '/@/api/business/wms/shipping/pickDetail/pick-detail-api'
import {TABLE_ID_CONST} from "/@/constants/support/table-id-const";
import {useRoute, useRouter} from "vue-router";
// ---------------------------- ----------------------------
let columns = ref([
{
title: '出库单id',
dataIndex: 'pickId',
ellipsis: true,
},
{
title: '客户',
dataIndex: 'customerName',
ellipsis: true,
},
{
title: '出库单号',
dataIndex: 'pickNumber',
ellipsis: true,
},
{
title: '客户订单号',
dataIndex: 'customerNumber',
ellipsis: true,
},
{
title: '单据类型',
dataIndex: 'orderType',
ellipsis: true,
},
{
title: '状态',
dataIndex: 'status',
ellipsis: true,
},
{
title: '收货单位',
dataIndex: 'name',
ellipsis: true,
},
{
title: '订单数量',
dataIndex: 'orderQuantity',
ellipsis: true,
},
{
title: '分配数量',
dataIndex: 'allocatedQuantity',
ellipsis: true,
},
{
title: '拣货数量',
dataIndex: 'pickedQuantity',
ellipsis: true,
},
{
title: '订单日期',
dataIndex: 'orderDate',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
ellipsis: true,
},
{
title: '操作',
dataIndex: 'action',
fixed: 'right',
align: 'center',
width: 150,
},
]);
//
const subColumns = [
{
title: '物料编码',
dataIndex: 'itemCode',
align: 'center',
ellipsis: true,
},
{
title: '物料名称',
dataIndex: 'itemName',
align: 'center',
ellipsis: true,
},
{
title: '订单数量',
align: 'center',
dataIndex: 'orderQuantity',
ellipsis: true,
},
{
title: '分配数量',
align: 'center',
dataIndex: 'allocatedQuantity',
ellipsis: true,
},
{
title: '拣货数量',
align: 'center',
dataIndex: 'pickedQuantity',
ellipsis: true,
}
];
//
const subTableData = ref([]);
async function querySubTableData(expanded: boolean, record: any) {
if (expanded && !record.children) {
try {
const res = await pickDetailApi.queryPage({pickId: record.pickId, pageNum: 1, pageSize: 100});
subTableData.value = res.data.list;
} catch (e) {
smartSentry.captureError(e);
}
}
}
//
const totals = computed(() => {
let totalOrderQuantity = 0;
let totalAllocatedQuantity = 0;
let totalPickedQuantity = 0;
subTableData.value.forEach(({orderQuantity, allocatedQuantity, pickedQuantity}) => {
totalOrderQuantity += orderQuantity;
totalAllocatedQuantity += allocatedQuantity;
totalPickedQuantity += pickedQuantity;
});
return {totalOrderQuantity, totalAllocatedQuantity, totalPickedQuantity};
});
// ---------------------------- ----------------------------
const queryFormState = {
customerNumber: undefined, //
pageNum: 1,
pageSize: 10,
};
// form
const queryForm = reactive({...queryFormState});
// loading
const tableLoading = ref(false);
//
const tableData = ref([]);
//
const total = ref(0);
//
function resetQuery() {
let pageSize = queryForm.pageSize;
Object.assign(queryForm, queryFormState);
queryForm.pageSize = pageSize;
queryData();
}
//
function onSearch() {
queryForm.pageNum = 1;
queryData();
}
//
async function queryData() {
tableLoading.value = true;
try {
let queryResult = await pickApi.queryPage(queryForm);
tableData.value = queryResult.data.list;
total.value = queryResult.data.total;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
// ---------------------------- / ----------------------------
let router = useRouter();
function showForm(data: any) {
const id = Date.now().toString(); //
//sessionStorage
sessionStorage.setItem(id, JSON.stringify(data));
router.push({
path: '/wms/shipping/pick/pick-form',
query: {
id: id
}
});
}
// ---------------------------- ----------------------------
//
function onDelete(data: object) {
Modal.confirm({
title: '提示',
content: '确定要删除选吗?',
okText: '删除',
okType: 'danger',
onOk() {
requestDelete(data);
},
cancelText: '取消',
onCancel() {
},
});
}
//
async function requestDelete(data: any) {
SmartLoading.show();
try {
await pickApi.delete(data.pickId);
message.success('删除成功');
await queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
// ---------------------------- ----------------------------
//
const selectedRowKeyList = ref([]);
function onSelectChange(selectedRowKeys: any) {
selectedRowKeyList.value = selectedRowKeys;
}
//
function confirmBatchDelete() {
Modal.confirm({
title: '提示',
content: '确定要批量删除这些数据吗?',
okText: '删除',
okType: 'danger',
onOk() {
requestBatchDelete();
},
cancelText: '取消',
onCancel() {
},
});
}
//
async function requestBatchDelete() {
try {
SmartLoading.show();
const response = await pickApi.batchDelete(selectedRowKeyList.value);
console.log(response)
const {error, success} = JSON.parse(response.msg);
if (success) {
message.success(success);
}
if (error) {
message.error(error);
}
await queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
//orange green purple success processing error default warning
function getStatusColor(status: string) {
//
switch (status) {
case 'CREATED':
return 'orange';
case 'APPROVING':
case 'PARTIALLY_ALLOCATED':
return 'cyan';
case 'APPROVED':
case 'ALLOCATED':
return 'purple';
case 'PARTIALLY_PICKED':
return 'blue';
case 'COMPLETED':
return 'green';
case 'CANCELLED':
return 'red';
default:
return 'orange';
}
}
//asn-form
const route = useRoute();
watch(
() => route.query,
(newValue) => {
if (newValue.refresh === '1') {
queryData();
}
}
);
onMounted(queryData);
</script>

View File

@ -0,0 +1,316 @@
<!--
* 出库明细
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-08-15 20:15:49
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<div>
<div class="header">
<div>
关键字
<a-input style="width: 250px" v-model:value="queryForm.keyword" placeholder="姓名/手机号/登录账号"/>
<a-button class="button-style" type="primary" @click="onSearch" v-privilege="'pickDetail:query'"></a-button>
<a-button class="button-style" type="default" @click="resetQuery" v-privilege="'pickDetail:query'">
</a-button>
</div>
<div class="smart-table-setting-block">
<a-button class="button-style" type="primary" v-privilege="'pickDetail:add'" @click="showForm"
:disabled="props.pickId==0">
<template #icon>
<PlusOutlined/>
</template>
添加明细
</a-button>
<a-button class="button-style" type="primary" v-privilege="'pickDetail:batchDelete'" @click="confirmBatchDelete"
danger
:disabled="props.pickId==0 || selectedRowKeyList.length == 0">
<template #icon>
<DeleteOutlined/>
</template>
批量移除
</a-button>
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.BUSINESS.WMS.PICK_DETAIL"
:refresh="queryData"/>
</div>
</div>
<a-table
size="small"
:dataSource="tableData"
:columns="columns"
rowKey="pickDetailId"
bordered
:loading="tableLoading"
:pagination="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
>
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="showForm(record)" type="link" :disabled="record.orderQuantity-record.allocatedQuantity==0"
v-privilege="'pickDetail:update'">
<template #icon>
<EditOutlined/>
</template>
编辑
</a-button>
<a-button @click="onDelete(record)" danger type="link"
:disabled="record.orderQuantity-record.allocatedQuantity==0" v-privilege="'pickDetail:delete'">
<template #icon>
<DeleteOutlined/>
</template>
删除
</a-button>
</div>
</template>
</template>
</a-table>
<div class="smart-query-table-page">
<a-pagination
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total:number) => `共${total}条`"
/>
</div>
<!--添加明细/编辑明细-->
<PICKDetailForm ref="formRef" @reloadList="queryData"/>
</div>
</template>
<script setup lang="ts">
import {reactive, ref, watch} from 'vue';
import {PAGE_SIZE, PAGE_SIZE_OPTIONS} from '/@/constants/common-const';
import {smartSentry} from '/@/lib/smart-sentry';
import {TABLE_ID_CONST} from "/@/constants/support/table-id-const";
import TableOperator from '/@/components/support/table-operator/index.vue';
import {pickDetailApi} from "/@/api/business/wms/shipping/pickDetail/pick-detail-api";
import PICKDetailForm from "/@/views/business/wms/shipping/pickDetail/pick-detail-form.vue";
import {message, Modal} from "ant-design-vue";
import {SmartLoading} from '/@/components/framework/smart-loading';
const props = defineProps({
pickId: {
type: Number,
default: 0,
},
});
let columns = reactive([
{
title: '出库明细id',
dataIndex: 'pickDetailId',
ellipsis: true,
},
{
title: '物料编码',
dataIndex: 'itemCode',
ellipsis: true,
},
{
title: '物料名称',
dataIndex: 'itemName',
ellipsis: true,
},
{
title: '订单数量',
dataIndex: 'orderQuantity',
ellipsis: true,
},
{
title: '分配数量',
dataIndex: 'allocatedQuantity',
ellipsis: true,
},
{
title: '拣货数量',
dataIndex: 'pickedQuantity',
ellipsis: true,
},
{
title: '操作',
dataIndex: 'action',
width: 140,
},
]);
// --------------------------- ---------------------------
const defaultQueryForm = {
pageNum: 1,
pageSize: PAGE_SIZE,
pickId: 0,
keyword: undefined,
}
//
const queryForm = reactive({...defaultQueryForm});
// loading
const tableLoading = ref(false);
//
const tableData = ref([]);
//
const total = ref(0);
//
function resetQuery() {
let pageSize = queryForm.pageSize;
Object.assign(queryForm, defaultQueryForm);
queryForm.pageSize = pageSize;
queryData();
}
//
function onSearch() {
queryForm.pageNum = 1;
queryData();
}
//
async function queryData() {
tableLoading.value = true;
try {
queryForm.pickId = props.pickId;
const queryResult = await pickDetailApi.queryPage(queryForm);
tableData.value = queryResult.data.list;
total.value = queryResult.data.total;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
//
const selectedRowKeyList = ref([]);
function onSelectChange(selectedRowKeys: any) {
selectedRowKeyList.value = selectedRowKeys;
}
///
const formRef = ref();
function showForm(data: object) {
formRef.value.show(data, props.pickId);
}
//
function onDelete(data: object) {
Modal.confirm({
title: '提示',
content: '确定要删除选吗?',
okText: '删除',
okType: 'danger',
onOk() {
requestDelete(data);
},
cancelText: '取消',
onCancel() {
},
});
}
//
async function requestDelete(data: any) {
SmartLoading.show();
try {
const response = await pickDetailApi.delete(data.asnDetailId);
const {error, success} = JSON.parse(response.data);
if (error) {
message.error(error);
}
if (success) {
message.success(success);
}
await queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
//
function confirmBatchDelete() {
Modal.confirm({
title: '提示',
content: '确定要批量删除这些数据吗?',
okText: '删除',
okType: 'danger',
onOk() {
requestBatchDelete();
},
cancelText: '取消',
onCancel() {
},
});
}
//
async function requestBatchDelete() {
try {
SmartLoading.show();
const response = await pickDetailApi.batchDelete(selectedRowKeyList.value);
const {error, success} = JSON.parse(response.data);
if (error) {
message.error(error);
}
if (success) {
message.success(success);
}
await queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
watch(
() => props.pickId,
(value) => {
if (value) {
queryForm.pickId = value;
queryData();
}
},
{
immediate: true,
}
);
defineExpose({
queryData
});
</script>
<style scoped lang="less">
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px
}
.button-style {
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,154 @@
<!--
* 出库明细
*
* @Author: 霍锦
* @Date: 2025-03-26 15:16:28
* @Copyright 友仓
-->
<template>
<a-modal
:title="form.pickDetailId ? '编辑明细' : '添加明细'"
:width="600"
:open="visibleFlag"
@cancel="onClose"
:maskClosable="false"
:destroyOnClose="true"
>
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }">
<a-form-item label="物料" name="itemId">
<ItemSelect style="width: 100%" v-model:value="form.itemId" :disabledFlag="true" @change="changeItemSelect"/>
</a-form-item>
<a-form-item label="数量" name="orderQuantity">
<a-input-number style="width: 100%" v-model:value="form.orderQuantity" :min="1" :max="9999"/>
</a-form-item>
<a-form-item label="批次" name="propC1">
<a-input style="width: 100%" v-model:value="form.propC1"/>
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button @click="onClose"></a-button>
<a-button type="primary" @click="onSubmit"></a-button>
</a-space>
</template>
</a-modal>
</template>
<script setup lang="ts">
import {reactive, ref, nextTick} from 'vue';
import _ from 'lodash';
import {message} from 'ant-design-vue';
import {SmartLoading} from '/@/components/framework/smart-loading';
import {pickDetailApi} from '/@/api/business/wms/shipping/pickDetail/pick-detail-api';
import {smartSentry} from '/@/lib/smart-sentry';
import ItemSelect from "/@/views/business/wms/base/item/item-select.vue";
// ------------------------ ------------------------
const emits = defineEmits(['reloadList']);
// ------------------------ ------------------------
//
const visibleFlag = ref(false);
function show(rowData: object, pickId: number) {
Object.assign(form, formDefault);
if (rowData && !_.isEmpty(rowData)) {
Object.assign(form, rowData);
}
//pickId
form.pickId = pickId;
visibleFlag.value = true;
nextTick(() => {
formRef.value.clearValidate();
});
}
function onClose() {
Object.assign(form, formDefault);
visibleFlag.value = false;
}
//
function changeItemSelect(selectValue: any) {
if (selectValue) {
form.itemId = selectValue.itemId;
}
}
//
function getCurrentDateWithoutSymbols() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}${month}${day}`;
}
// ------------------------ ------------------------
// ref
const formRef = ref();
const formDefault = {
pickDetailId: undefined, //id
pickId: undefined,//id
itemId: undefined, //id
propC1: getCurrentDateWithoutSymbols(),
orderQuantity: 1, //
};
let form = reactive({...formDefault});
const rules = {
itemId: [{required: true, message: '物料id 必填'}],
orderQuantity: [{required: true, message: '订单数量 必填'}],
propC1: [{required: true, message: '批次 必填'}],
};
//
async function onSubmit() {
try {
await formRef.value.validateFields();
await save();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
// API
async function save() {
SmartLoading.show();
try {
if (form.pickDetailId) {
await pickDetailApi.update(form);
} else {
await pickDetailApi.add(form);
}
message.success('操作成功');
emits('reloadList');
continueResetForm()
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
function continueResetForm() {
const pickId = form.pickId;
Object.assign(form, formDefault);
form.pickId = pickId;
nextTick(() => {
formRef.value.clearValidate();
});
}
defineExpose({
show,
});
</script>

View File

@ -0,0 +1,190 @@
<!--
* 发货明细
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-08-15 20:15:49
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<div>
<div class="header">
<div>
关键字
<a-input style="width: 250px" v-model:value="queryForm.keyword" placeholder="姓名/手机号/登录账号"/>
<a-button class="button-style" type="primary" @click="onSearch" v-privilege="'task:query'"></a-button>
<a-button class="button-style" type="default" @click="resetQuery" v-privilege="'task:query'"></a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator v-model="columns" :tableId="TABLE_ID_CONST.BUSINESS.WMS.PICK_TASK" :refresh="queryData"/>
</div>
</div>
<a-table
:loading="tableLoading"
:dataSource="tableData"
:columns="columns"
:pagination="false"
rowKey="taskId"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
size="small"
bordered
>
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
</div>
</template>
</template>
</a-table>
<div class="smart-query-table-page">
<a-pagination
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total:number) => `共${total}条`"
/>
</div>
</div>
</template>
<script setup lang="ts">
import {reactive, ref, watch} from 'vue';
import {PAGE_SIZE, PAGE_SIZE_OPTIONS} from '/@/constants/common-const';
import {smartSentry} from '/@/lib/smart-sentry';
import {TABLE_ID_CONST} from "/@/constants/support/table-id-const";
import TableOperator from '/@/components/support/table-operator/index.vue';
import {taskApi} from "/@/api/business/wms/task/task-api";
const props = defineProps({
pickId: {
type: Number,
default: 0,
},
});
let columns = reactive([
{
title: '任务ID',
dataIndex: 'taskId',
ellipsis: true,
},
{
title: '物料编码',
dataIndex: 'itemCode',
ellipsis: true,
},
{
title: '物料名称',
dataIndex: 'itemName',
ellipsis: true,
},
{
title: '原库位',
dataIndex: 'srcLocationCode',
ellipsis: true,
},
{
title: '目标库位',
dataIndex: 'dstLocationCode',
ellipsis: true,
},
{
title: '拣货数量',
dataIndex: 'moveQty',
ellipsis: true,
}
]);
// --------------------------- ---------------------------
const defaultQueryForm = {
pageNum: 1,
pageSize: PAGE_SIZE,
pickId: 0,
keyword: undefined,
taskType: 'PICK'//
}
//
const queryForm = reactive({...defaultQueryForm});
// loading
const tableLoading = ref(false);
//
const tableData = ref([]);
//
const total = ref(0);
//
function resetQuery() {
let pageSize = queryForm.pageSize;
Object.assign(queryForm, defaultQueryForm);
queryForm.pageSize = pageSize;
queryData();
}
//
function onSearch() {
queryForm.pageNum = 1;
queryData();
}
//
async function queryData() {
tableLoading.value = true;
try {
queryForm.pickId = props.pickId;
let queryResult = await taskApi.queryPage(queryForm);
tableData.value = queryResult.data.list;
total.value = queryResult.data.total;
} catch (e) {
smartSentry.captureError(e);
} finally {
tableLoading.value = false;
}
}
//
const selectedRowKeyList = ref([]);
function onSelectChange(selectedRowKeys: any) {
selectedRowKeyList.value = selectedRowKeys;
}
watch(
() => props.pickId,
(e) => {
if (e) {
queryData();
}
},
{immediate: true}
);
defineExpose({
queryData
});
</script>
<style scoped lang="less">
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px
}
.button-style {
margin-left: 10px;
}
</style>