入库管理、任务管理、库存管理

main
HUOJIN\92525 2025-11-13 16:58:05 +08:00
parent 5374bb5e0a
commit 3aa23d97f5
20 changed files with 1149 additions and 198 deletions

View File

@ -5,4 +5,4 @@ export const GITHUB_URL = 'http://47.103.100.52:3000/huojin/ZhongYou-Vue3.git';
export const DOC_URL = 'https://help.cpte.com'; export const DOC_URL = 'https://help.cpte.com';
// site url // site url
export const SITE_URL = 'http://www.cpte.com'; export const SITE_URL = '10.180.9.60:80';

View File

@ -11,6 +11,8 @@ enum Api {
deleteBatch = '/agvTask/deleteBatch', deleteBatch = '/agvTask/deleteBatch',
importExcel = '/agvTask/importExcel', importExcel = '/agvTask/importExcel',
exportXls = '/agvTask/exportXls', exportXls = '/agvTask/exportXls',
taskReporter = '/api/robot/reporter/task',
callBackTask='/tes/apiv2/callBackTask',
} }
/** /**
@ -70,3 +72,25 @@ export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save; let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false }); return defHttp.post({ url: url, params }, { isTransformResponse: false });
} }
/**
* AGV
* @param params
*/
export const taskReporter = (params) => {
return defHttp.post({url: Api.taskReporter, params}, {
joinParamsToUrl: false,
isTransformResponse: false
});
}
/**
* TES
* @param params
*/
export const callBackTask = (params) => {
return defHttp.post({url: Api.callBackTask, params}, {
joinParamsToUrl: false,
isTransformResponse: false
});
}

View File

@ -53,7 +53,7 @@
import { BasicTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns } from './HikAgv.data'; import { columns } from './HikAgv.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './AgvTask.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, taskReporter } from './AgvTask.api';
import HikAgvModal from './components/HikAgvModal.vue'; import HikAgvModal from './components/HikAgvModal.vue';
import { useUserStore } from '/@/store/modules/user'; import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
@ -144,6 +144,35 @@
await deleteOne({ id: record.id }, handleSuccess); await deleteOne({ id: record.id }, handleSuccess);
} }
/**
* 任务完成事件
*/
async function hanndleComplete(record) {
const params = {
robotTaskCode: record.id,
singleRobotCode: '',
extra: {
async: '0',
values: {
method: 'end',
carrierCode: record.carrierCode,
slotCode: record.endCode,
},
},
};
try {
const res = await taskReporter(params);
if (res && res.code === 'SUCCESS') {
createMessage.success('操作成功');
handleSuccess();
} else {
createMessage.error(res.message || '任务处理失败');
}
} catch (error) {
createMessage.error('请求异常: ' + error.message);
}
}
/** /**
* 批量删除事件 * 批量删除事件
*/ */
@ -177,17 +206,28 @@
function getDropDownAction(record) { function getDropDownAction(record) {
return [ return [
{ {
label: '详情', label: '任务详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, },
{ {
label: '删除', label: '任务完成',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认完成?',
confirm: hanndleComplete.bind(null, record),
placement: 'topLeft',
},
auth: 'agvTask:data_agv_task:edit',
disabled: record.status === 4 || record.status === 1
},
{
label: '任务删除',
popConfirm: {
title: '是否确认删除?',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
placement: 'topLeft', placement: 'topLeft',
}, },
auth: 'agvTask:data_agv_task:delete', auth: 'agvTask:data_agv_task:delete',
disabled: record.status != 1
}, },
]; ];
} }

View File

@ -53,7 +53,7 @@
import { BasicTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns } from './TesAgv.data'; import { columns } from './TesAgv.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './AgvTask.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, callBackTask } from './AgvTask.api';
import TesAgvModal from './components/TesAgvModal.vue'; import TesAgvModal from './components/TesAgvModal.vue';
import { useUserStore } from '/@/store/modules/user'; import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
@ -151,6 +151,32 @@
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess); await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
} }
/**
* 任务完成事件
*/
async function hanndleComplete(record) {
const params = {
messageType: 10,
content: {
bizID: record.id,
status: 4,
podID: record.carrierCode,
},
};
try {
const res = await callBackTask(params);
if (res && res.returnCode === 0) {
createMessage.success('操作成功');
handleSuccess();
} else {
createMessage.error(res.message || '任务处理失败');
}
} catch (error) {
createMessage.error('请求异常: ' + error.message);
}
}
/** /**
* 成功回调 * 成功回调
*/ */
@ -177,17 +203,28 @@
function getDropDownAction(record) { function getDropDownAction(record) {
return [ return [
{ {
label: '详情', label: '任务详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, },
{ {
label: '删除', label: '任务完成',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认完成?',
confirm: hanndleComplete.bind(null, record),
placement: 'topLeft',
},
auth: 'agvTask:data_agv_task:edit',
disabled: record.status === 4 || record.status === 1,
},
{
label: '删除任务',
popConfirm: {
title: '是否确认删除?',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
placement: 'topLeft', placement: 'topLeft',
}, },
auth: 'agvTask:data_agv_task:delete', auth: 'agvTask:data_agv_task:delete',
disabled: record.status != 1,
}, },
]; ];
} }

View File

@ -14,11 +14,11 @@
<JDictSelectTag v-model:value="formData.carrierCode" placeholder="请选择容器" dictCode="base_stock where iz_active=1 and del_flag=0,stock_code,stock_code" allowClear /> <JDictSelectTag v-model:value="formData.carrierCode" placeholder="请选择容器" dictCode="base_stock where iz_active=1 and del_flag=0,stock_code,stock_code" allowClear />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <!-- <a-col :span="24">
<a-form-item label="任务类型" v-bind="validateInfos.taskType" id="AgvTaskForm-taskType" name="taskType"> <a-form-item label="任务类型" v-bind="validateInfos.taskType" id="AgvTaskForm-taskType" name="taskType">
<a-input v-model:value="formData.taskType" placeholder="请输入任务类型" allow-clear ></a-input> <a-input v-model:value="formData.taskType" placeholder="请输入任务类型" allow-clear ></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>-->
<a-col :span="24"> <a-col :span="24">
<a-form-item label="业务类型" v-bind="validateInfos.type" id="AgvTaskForm-type" name="type"> <a-form-item label="业务类型" v-bind="validateInfos.type" id="AgvTaskForm-type" name="type">
<JDictSelectTag <JDictSelectTag

View File

@ -14,11 +14,11 @@
<JDictSelectTag v-model:value="formData.carrierCode" placeholder="请选择容器" dictCode="base_stock where iz_active=1 and del_flag=0,stock_code,stock_code" allowClear /> <JDictSelectTag v-model:value="formData.carrierCode" placeholder="请选择容器" dictCode="base_stock where iz_active=1 and del_flag=0,stock_code,stock_code" allowClear />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <!-- <a-col :span="24">
<a-form-item label="任务类型" v-bind="validateInfos.taskType" id="AgvTaskForm-taskType" name="taskType"> <a-form-item label="任务类型" v-bind="validateInfos.taskType" id="AgvTaskForm-taskType" name="taskType">
<a-input v-model:value="formData.taskType" placeholder="请输入任务类型" allow-clear ></a-input> <a-input v-model:value="formData.taskType" placeholder="请输入任务类型" allow-clear ></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>-->
<a-col :span="24"> <a-col :span="24">
<a-form-item label="业务类型" v-bind="validateInfos.type" id="AgvTaskForm-type" name="type"> <a-form-item label="业务类型" v-bind="validateInfos.type" id="AgvTaskForm-type" name="type">
<JDictSelectTag <JDictSelectTag

View File

@ -7,7 +7,12 @@
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="areaId"> <a-form-item name="areaId">
<template #label><span title="库区">库区</span></template> <template #label><span title="库区">库区</span></template>
<JSearchSelect v-model:value="queryParam.areaId" placeholder="请选择库区" dict="base_area where iz_active=1 and del_flag=0 ,area_name,id" allowClear /> <JSearchSelect
v-model:value="queryParam.areaId"
placeholder="请选择库区"
dict="base_area where iz_active=1 and del_flag=0 ,area_name,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6"> <a-col :lg="6">
@ -141,6 +146,10 @@
width: 120, width: 120,
fixed: 'right', fixed: 'right',
}, },
defSort: {
column: 'areaId',
order: 'asc',
},
beforeFetch: async (params) => { beforeFetch: async (params) => {
for (let key in fieldPickers) { for (let key in fieldPickers) {
if (queryParam[key] && fieldPickers[key]) { if (queryParam[key] && fieldPickers[key]) {

View File

@ -1,64 +1,74 @@
import { BasicColumn } from '/@/components/Table'; import { BasicColumn } from '/@/components/Table';
//列表数据 //列表数据
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
{
title: '容器',
align: "center",
dataIndex: 'stockId_dictText'
},
{ {
title: '物料', title: '物料',
align: "center", align: 'center',
dataIndex: 'itemId_dictText' dataIndex: 'itemId_dictText',
width: 120,
}, },
{ {
title: '库位', title: '库位',
align: "center", align: 'center',
dataIndex: 'pointId_dictText' dataIndex: 'pointId_dictText',
width: 120,
},
{
title: '容器',
align: 'center',
dataIndex: 'stockId_dictText',
width: 120,
}, },
{ {
title: '数量', title: '数量',
align: "center", align: 'center',
dataIndex: 'quantity' dataIndex: 'quantity',
width: 100,
}, },
{ {
title: '占用数', title: '占用数',
align: "center", align: 'center',
dataIndex: 'queuedQty' dataIndex: 'queuedQty',
width: 100,
}, },
{ {
title: '库存状态', title: '库存状态',
align: "center", align: 'center',
dataIndex: 'status_dictText' dataIndex: 'status_dictText',
width: 100,
}, },
{ {
title: '批次号', title: '批次号',
align: "center", align: 'center',
dataIndex: 'propC1' dataIndex: 'propC1',
width: 100,
}, },
{ {
title: '外部仓库', title: '外部仓库',
align: "center", align: 'center',
dataIndex: 'whCode' dataIndex: 'whCode',
width: 100,
}, },
{ {
title: '外部库存状态', title: '外部库存状态',
align: "center", align: 'center',
dataIndex: 'propC3' dataIndex: 'propC3',
width: 110,
}, },
{ {
title: '生产日期', title: '生产日期',
align: "center", align: 'center',
dataIndex: 'propD1', dataIndex: 'propD1',
width: 100,
customRender: ({ text }) => { customRender: ({ text }) => {
text = !text ? "" : (text.length > 10 ? text.substr(0,10) : text); text = !text ? '' : text.length > 10 ? text.substr(0, 10) : text;
return text; return text;
}, },
}, },
{ {
title: '描述', title: '描述',
align: "center", align: 'center',
dataIndex: 'description' dataIndex: 'description',
} },
]; ];

View File

@ -4,23 +4,12 @@
<div class="jeecg-basic-table-form-container"> <div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :lg="6">
<a-form-item name="stockId">
<template #label><span title="容器">容器</span></template>
<JSearchSelect
v-model:value="queryParam.stockId"
placeholder="请选择库位"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item>
</a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="itemId"> <a-form-item name="itemId">
<template #label><span title="物料">物料</span></template> <template #label><span title="物料">物料</span></template>
<JSearchSelect <JSearchSelect
v-model:value="queryParam.itemId" v-model:value="queryParam.itemId"
placeholder="请选择库位" placeholder="请选择物料"
dict="base_item where iz_active=1 and del_flag=0 ,item_code,id" dict="base_item where iz_active=1 and del_flag=0 ,item_code,id"
allowClear allowClear
/> />
@ -37,6 +26,17 @@
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6">
<a-form-item name="stockId">
<template #label><span title="容器">容器</span></template>
<JSearchSelect
v-model:value="queryParam.stockId"
placeholder="请选择容器"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item>
</a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24"> <a-col :xl="6" :lg="7" :md="8" :sm="24">
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons"> <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :lg="6"> <a-col :lg="6">
@ -65,8 +65,12 @@
<!--插槽:table标题--> <!--插槽:table标题-->
<template #tableTitle> <template #tableTitle>
<a-button type="primary" v-auth="'inventory:data_inventory:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> </a-button> <a-button type="primary" v-auth="'inventory:data_inventory:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> </a-button>
<a-button type="primary" v-auth="'inventory:data_inventory:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> </a-button> <a-button type="primary" v-auth="'inventory:data_inventory:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
<j-upload-button type="primary" v-auth="'inventory:data_inventory:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"></j-upload-button> 导出
</a-button>
<j-upload-button type="primary" v-auth="'inventory:data_inventory:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"
>导入
</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0"> <a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay> <template #overlay>
<a-menu> <a-menu>
@ -76,7 +80,8 @@
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</template> </template>
<a-button v-auth="'inventory:data_inventory:deleteBatch'"> <a-button v-auth="'inventory:data_inventory:deleteBatch'"
>批量操作
<Icon icon="mdi:chevron-down"></Icon> <Icon icon="mdi:chevron-down"></Icon>
</a-button> </a-button>
</a-dropdown> </a-dropdown>
@ -85,8 +90,7 @@
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" /> <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template> </template>
<template v-slot:bodyCell="{ column, record, index, text }"> <template v-slot:bodyCell="{ column, record, index, text }"> </template>
</template>
</BasicTable> </BasicTable>
<!-- 表单区域 --> <!-- 表单区域 -->
<InventoryModal ref="registerModal" @success="handleSuccess"></InventoryModal> <InventoryModal ref="registerModal" @success="handleSuccess"></InventoryModal>
@ -95,20 +99,17 @@
<script lang="ts" name="inventory-inventory" setup> <script lang="ts" name="inventory-inventory" setup>
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, superQuerySchema } from './Inventory.data'; import { columns } from './Inventory.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Inventory.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Inventory.api';
import { downloadFile } from '/@/utils/common/renderUtils'; import InventoryModal from './components/InventoryModal.vue';
import InventoryModal from './components/InventoryModal.vue'
import { useUserStore } from '/@/store/modules/user'; import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import {useModal} from '/@/components/Modal';
import { getDateByPicker } from '/@/utils'; import { getDateByPicker } from '/@/utils';
import {JDictSelectTag, JSearchSelect} from "@/components/Form"; import { JDictSelectTag, JSearchSelect } from '@/components/Form';
const fieldPickers = reactive({ const fieldPickers = reactive({});
});
const formRef = ref(); const formRef = ref();
const queryParam = reactive<any>({}); const queryParam = reactive<any>({});
@ -138,21 +139,22 @@
}, },
}, },
exportConfig: { exportConfig: {
name: "库存表", name: '库存表',
url: getExportUrl, url: getExportUrl,
params: queryParam, params: queryParam,
}, },
importConfig: { importConfig: {
url: getImportUrl, url: getImportUrl,
success: handleSuccess success: handleSuccess,
}, },
}); });
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
tableContext;
const labelCol = reactive({ const labelCol = reactive({
xs: 24, xs: 24,
sm: 4, sm: 4,
xl: 6, xl: 6,
xxl:4 xxl: 4,
}); });
const wrapperCol = reactive({ const wrapperCol = reactive({
xs: 24, xs: 24,
@ -212,7 +214,7 @@
{ {
label: '编辑', label: '编辑',
onClick: handleEdit.bind(null, record), onClick: handleEdit.bind(null, record),
auth: 'inventory:data_inventory:edit' auth: 'inventory:data_inventory:edit',
}, },
]; ];
} }
@ -225,16 +227,17 @@
{ {
label: '详情', label: '详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, { },
{
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认删除',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
placement: 'topLeft', placement: 'topLeft',
}, },
auth: 'inventory:data_inventory:delete' auth: 'inventory:data_inventory:delete',
} },
] ];
} }
/** /**
@ -253,35 +256,35 @@
// //
reload(); reload();
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.jeecg-basic-table-form-container { .jeecg-basic-table-form-container {
padding: 0; padding: 0;
.table-page-search-submitButtons { .table-page-search-submitButtons {
display: block; display: block;
margin-bottom: 24px; margin-bottom: 24px;
white-space: nowrap; white-space: nowrap;
} }
.query-group-cust { .query-group-cust {
min-width: 100px !important; min-width: 100px !important;
} }
.query-group-split-cust { .query-group-split-cust {
width: 30px; width: 30px;
display: inline-block; display: inline-block;
text-align: center text-align: center;
} }
.ant-form-item:not(.ant-form-item-with-help) { .ant-form-item:not(.ant-form-item-with-help) {
margin-bottom: 16px; margin-bottom: 16px;
height: 32px; height: 32px;
} }
:deep(.ant-picker),:deep(.ant-input-number){
:deep(.ant-picker),
:deep(.ant-input-number) {
width: 100%; width: 100%;
} }
} }

View File

@ -3,65 +3,89 @@
<JFormContainer :disabled="disabled"> <JFormContainer :disabled="disabled">
<template #detail> <template #detail>
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="InventoryForm"> <a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="InventoryForm">
<a-row> <a-row class="form-row" :gutter="24">
<a-col :span="24"> <a-col :span="8">
<a-form-item label="库位ID" v-bind="validateInfos.pointId" id="InventoryForm-pointId" name="pointId"> <a-form-item label="物料" v-bind="validateInfos.itemId" id="InventoryForm-itemId" name="itemId">
<a-input-number v-model:value="formData.pointId" placeholder="请输入库位ID" style="width: 100%" /> <JSearchSelect
v-model:value="formData.itemId"
placeholder="请选择物料"
dict="base_item where iz_active=1 and del_flag=0 ,item_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="8">
<a-form-item label="容器ID" v-bind="validateInfos.stockId" id="InventoryForm-stockId" name="stockId"> <a-form-item label="库位" v-bind="validateInfos.pointId" id="InventoryForm-pointId" name="pointId">
<a-input-number v-model:value="formData.stockId" placeholder="请输入容器ID" style="width: 100%" /> <JSearchSelect
v-model:value="formData.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="8">
<a-form-item label="容器" v-bind="validateInfos.stockId" id="InventoryForm-stockId" name="stockId">
<JSearchSelect
v-model:value="formData.stockId"
placeholder="请选择容器"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :span="8">
<a-form-item label="数量" v-bind="validateInfos.quantity" id="InventoryForm-quantity" name="quantity"> <a-form-item label="数量" v-bind="validateInfos.quantity" id="InventoryForm-quantity" name="quantity">
<a-input-number v-model:value="formData.quantity" placeholder="请输入数量" style="width: 100%" /> <a-input-number v-model:value="formData.quantity" placeholder="请输入数量" style="width: 100%" />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="8">
<a-form-item label="分配数" v-bind="validateInfos.queuedQty" id="InventoryForm-queuedQty" name="queuedQty"> <a-form-item label="库存状态" v-bind="validateInfos.status" id="InventoryForm-status" name="status">
<a-input-number v-model:value="formData.queuedQty" placeholder="请输入分配数" style="width: 100%" /> <JDictSelectTag v-model:value="formData.status" placeholder="请选择" dictCode="inventory_status" string-to-number="true" allowClear />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="8">
<a-form-item label="外部仓库" v-bind="validateInfos.whCode" id="InventoryForm-whCode" name="whCode">
<a-input v-model:value="formData.whCode" placeholder="请输入外部仓库" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="批次号" v-bind="validateInfos.propC1" id="InventoryForm-propC1" name="propC1"> <a-form-item label="批次号" v-bind="validateInfos.propC1" id="InventoryForm-propC1" name="propC1">
<a-input v-model:value="formData.propC1" placeholder="请输入批次号" allow-clear></a-input> <a-input v-model:value="formData.propC1" placeholder="请输入批次号" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> </a-row>
<a-form-item label="序列号" v-bind="validateInfos.propC2" id="InventoryForm-propC2" name="propC2"> <a-row class="form-row" :gutter="24">
<a-input v-model:value="formData.propC2" placeholder="请输入序列号" allow-clear ></a-input> <a-col :span="8">
<a-form-item label="外部仓库" v-bind="validateInfos.whCode" id="InventoryForm-whCode" name="whCode">
<a-input v-model:value="formData.whCode" placeholder="请输入外部仓库" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="8">
<a-form-item label="外部库存状态" v-bind="validateInfos.propC3" id="InventoryForm-propC3" name="propC3"> <a-form-item label="外部状态" v-bind="validateInfos.propC3" id="InventoryForm-propC3" name="propC3">
<a-input v-model:value="formData.propC3" placeholder="请输入外部库存状态" allow-clear></a-input> <a-input v-model:value="formData.propC3" placeholder="请输入外部库存状态" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="8">
<a-form-item label="库存状态" v-bind="validateInfos.status" id="InventoryForm-status" name="status">
<a-input v-model:value="formData.status" placeholder="请输入库存状态" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="生产日期" v-bind="validateInfos.propD1" id="InventoryForm-propD1" name="propD1"> <a-form-item label="生产日期" v-bind="validateInfos.propD1" id="InventoryForm-propD1" name="propD1">
<a-date-picker placeholder="请选择生产日期" v-model:value="formData.propD1" value-format="YYYY-MM-DD" style="width: 100%" allow-clear /> <a-date-picker
placeholder="请选择生产日期"
v-model:value="formData.propD1"
value-format="YYYY-MM-DD"
style="width: 100%"
allow-clear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :span="24"> <a-col :span="24">
<a-form-item label="描述" v-bind="validateInfos.description" id="InventoryForm-description" name="description"> <a-form-item
<a-input v-model:value="formData.description" placeholder="请输入描述" allow-clear ></a-input> label="描述"
</a-form-item> v-bind="validateInfos.description"
</a-col> id="InventoryForm-description"
<a-col :span="24"> name="description"
<a-form-item label="租户ID" v-bind="validateInfos.tenantId" id="InventoryForm-tenantId" name="tenantId"> :labelCol="{ span: 2 }"
<a-input-number v-model:value="formData.tenantId" placeholder="请输入租户ID" style="width: 100%" /> :wrapperCol="{ span: 24 }"
>
<a-textarea v-model:value="formData.description" :rows="4" placeholder="请输入描述" />
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -72,27 +96,32 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue'; import { ref, reactive, defineExpose, nextTick, defineProps, computed } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker, getValueType } from '/@/utils'; import { getDateByPicker, getValueType } from '/@/utils';
import { getTenantId } from '@/utils/auth';
import { saveOrUpdate } from '../Inventory.api'; import { saveOrUpdate } from '../Inventory.api';
import { Form } from 'ant-design-vue'; import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue'; import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
import { JDictSelectTag, JSearchSelect } from '@/components/Form';
const props = defineProps({ const props = defineProps({
formDisabled: { type: Boolean, default: false }, formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: () => ({}) }, formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true } formBpm: { type: Boolean, default: true },
}); });
const formRef = ref(); const formRef = ref();
const useForm = Form.useForm; const useForm = Form.useForm;
const emit = defineEmits(['register', 'ok']); const emit = defineEmits(['register', 'ok']);
// ID
let tenantId = getTenantId();
const formData = reactive<Record<string, any>>({ const formData = reactive<Record<string, any>>({
id: '', id: '',
itemId: undefined,
pointId: undefined, pointId: undefined,
stockId: undefined, stockId: undefined,
quantity: undefined, quantity: '',
queuedQty: undefined, queuedQty: '',
whCode: '', whCode: '',
propC1: '', propC1: '',
propC2: '', propC2: '',
@ -100,19 +129,23 @@
status: '', status: '',
propD1: '', propD1: '',
description: '', description: '',
tenantId: undefined, tenantId: tenantId,
}); });
const { createMessage } = useMessage(); const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } }); const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 8 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } }); const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 24 } });
const confirmLoading = ref<boolean>(false); const confirmLoading = ref<boolean>(false);
// //
const validatorRules = reactive({ const validatorRules = reactive({
itemId: [{ required: true, message: '请选择物料!' }],
pointId: [{ required: true, message: '请选择库位!' }],
stockId: [{ required: true, message: '请选择容器!' }],
quantity: [{ required: true, message: '请输入数量!' }],
status: [{ required: true, message: '请选择库存状态!' }],
}); });
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false }); const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
// //
const fieldPickers = reactive({ const fieldPickers = reactive({});
});
// //
const disabled = computed(() => { const disabled = computed(() => {
@ -126,7 +159,6 @@
return props.formDisabled; return props.formDisabled;
}); });
/** /**
* 新增 * 新增
*/ */
@ -143,9 +175,9 @@
const tmpData = {}; const tmpData = {};
Object.keys(formData).forEach((key) => { Object.keys(formData).forEach((key) => {
if (record.hasOwnProperty(key)) { if (record.hasOwnProperty(key)) {
tmpData[key] = record[key] tmpData[key] = record[key];
} }
}) });
// //
Object.assign(formData, tmpData); Object.assign(formData, tmpData);
}); });
@ -201,7 +233,6 @@
}); });
} }
defineExpose({ defineExpose({
add, add,
edit, edit,

View File

@ -1,5 +1,4 @@
<template> <template><j-modal :title="title" :maxHeight="500" :width="800" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<j-modal :title="title" maxHeight="500px" :width="800" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<InventoryForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></InventoryForm> <InventoryForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></InventoryForm>
<template #footer> <template #footer>
<a-button @click="handleCancel"></a-button> <a-button @click="handleCancel"></a-button>
@ -15,7 +14,6 @@
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage(); const { createMessage } = useMessage();
const title = ref<string>(''); const title = ref<string>('');
const width = ref<number>(800);
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false); const disableSubmit = ref<boolean>(false);
const registerForm = ref(); const registerForm = ref();
@ -25,7 +23,7 @@
* 新增 * 新增
*/ */
function add() { function add() {
title.value = '新增'; title.value = '新增库存';
visible.value = true; visible.value = true;
nextTick(() => { nextTick(() => {
registerForm.value.add(); registerForm.value.add();
@ -37,7 +35,7 @@
* @param record * @param record
*/ */
function edit(record) { function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑'; title.value = disableSubmit.value ? '库存详情' : '编辑库存';
visible.value = true; visible.value = true;
nextTick(() => { nextTick(() => {
registerForm.value.edit(record); registerForm.value.edit(record);

View File

@ -0,0 +1,72 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/inventoryLog/list',
save='/inventoryLog/add',
edit='/inventoryLog/edit',
deleteOne = '/inventoryLog/delete',
deleteBatch = '/inventoryLog/deleteBatch',
importExcel = '/inventoryLog/importExcel',
exportXls = '/inventoryLog/exportXls',
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
*
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}

View File

@ -0,0 +1,76 @@
import { BasicColumn } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '日志类型',
align: 'center',
dataIndex: 'logType_dictText',
width: 100,
},
{
title: '物料',
align: 'center',
dataIndex: 'itemId_dictText',
width: 120,
},
{
title: '原库位',
align: 'center',
dataIndex: 'fromPointId_dictText',
width: 120,
},
{
title: '目标库位',
align: 'center',
dataIndex: 'toPointId_dictText',
width: 120,
},
{
title: '容器',
align: 'center',
dataIndex: 'stockId_dictText',
width: 120,
},
{
title: '变动前数量',
align: 'center',
dataIndex: 'beforeQty',
width: 100,
},
{
title: '变动数量',
align: 'center',
dataIndex: 'changeQty',
width: 100,
},
{
title: '变动后数量',
align: 'center',
dataIndex: 'afterQty',
width: 100,
},
{
title: '变动前分配数量',
align: 'center',
dataIndex: 'beforeAllocatedQty',
width: 120,
},
{
title: '变动后分配数量',
align: 'center',
dataIndex: 'afterAllocatedQty',
width: 120,
},
{
title: '批次号',
align: 'center',
dataIndex: 'propC1',
width: 100,
},
{
title: '描述',
align: 'center',
dataIndex: 'description',
ellipsis: true,
},
];

View File

@ -0,0 +1,300 @@
<template>
<div class="p-2">
<!--查询区域-->
<div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24">
<a-col :lg="6">
<a-form-item name="itemId">
<template #label><span title="物料">物料</span></template>
<JSearchSelect
v-model:value="queryParam.itemId"
placeholder="请选择物料"
dict="base_item where iz_active=1 and del_flag=0 ,item_code,id"
allowClear
/>
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="pointId">
<template #label><span title="库位">库位</span></template>
<JSearchSelect
v-model:value="queryParam.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="stockId">
<template #label><span title="容器">容器</span></template>
<JSearchSelect
v-model:value="queryParam.stockId"
placeholder="请选择容器"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item>
</a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24">
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :lg="6">
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery"></a-button>
<a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
<a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<Icon :icon="toggleSearchStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
</a>
</a-col>
</span>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :lg="6">
<a-form-item name="logType">
<template #label><span title="日志类型">日志类型</span></template>
<JDictSelectTag v-model:value="queryParam.logType" placeholder="请选择" dictCode="inventory_log_type" allowClear />
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'inventoryLog:data_inventory_log:add'" v-if="false" @click="handleAdd" preIcon="ant-design:plus-outlined">
新增
</a-button>
<a-button type="primary" v-auth="'inventoryLog:data_inventory_log:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
导出
</a-button>
<j-upload-button
type="primary"
v-auth="'inventoryLog:data_inventory_log:importExcel'"
v-if="false"
preIcon="ant-design:import-outlined"
@click="onImportXls"
>导入
</j-upload-button>
<!-- v-if="selectedRowKeys.length > 0"-->
<a-dropdown v-if="false">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button v-auth="'inventoryLog:data_inventory_log:deleteBatch'"
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<template v-slot:bodyCell="{ column, record, index, text }"></template>
</BasicTable>
<!-- 表单区域 -->
<InventoryLogModal ref="registerModal" @success="handleSuccess"></InventoryLogModal>
</div>
</template>
<script lang="ts" name="inventoryLog-inventoryLog" setup>
import { ref, reactive } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns } from './InventoryLog.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './InventoryLog.api';
import InventoryLogModal from './components/InventoryLogModal.vue';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils';
import { JDictSelectTag, JSearchSelect } from '@/components/Form';
const fieldPickers = reactive({});
const formRef = ref();
const queryParam = reactive<any>({});
const toggleSearchStatus = ref<boolean>(false);
const registerModal = ref();
const userStore = useUserStore();
const { createMessage } = useMessage();
//table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '库存日志',
api: list,
columns,
canResize: true,
useSearchForm: false,
actionColumn: {
width: 120,
fixed: 'right',
},
showActionColumn: false,
beforeFetch: async (params) => {
for (let key in fieldPickers) {
if (queryParam[key] && fieldPickers[key]) {
queryParam[key] = getDateByPicker(queryParam[key], fieldPickers[key]);
}
}
return Object.assign(params, queryParam);
},
},
exportConfig: {
name: '库存日志',
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
tableContext;
const labelCol = reactive({
xs: 24,
sm: 4,
xl: 6,
xxl: 4,
});
const wrapperCol = reactive({
xs: 24,
sm: 20,
});
/**
* 新增事件
*/
function handleAdd() {
registerModal.value.disableSubmit = false;
registerModal.value.add();
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
registerModal.value.disableSubmit = false;
registerModal.value.edit(record);
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
registerModal.value.disableSubmit = true;
registerModal.value.edit(record);
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'inventoryLog:data_inventory_log:edit',
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
auth: 'inventoryLog:data_inventory_log:delete',
},
];
}
/**
* 查询
*/
function searchQuery() {
reload();
}
/**
* 重置
*/
function searchReset() {
formRef.value.resetFields();
selectedRowKeys.value = [];
//
reload();
}
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust {
min-width: 100px !important;
}
.query-group-split-cust {
width: 30px;
display: inline-block;
text-align: center;
}
.ant-form-item:not(.ant-form-item-with-help) {
margin-bottom: 16px;
height: 32px;
}
:deep(.ant-picker),
:deep(.ant-input-number) {
width: 100%;
}
}
</style>

View File

@ -0,0 +1,40 @@
-- 注意该页面对应的前台目录为views/inventoryLog文件夹下
-- 如果你想更改到其他目录请修改sql中component字段对应的值
-- 主菜单
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
VALUES ('176275925509501', NULL, '库存日志', '/inventoryLog/inventoryLogList', 'inventoryLog/InventoryLogList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0);
-- 新增
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('176275925509502', '176275925509501', '添加库存日志', NULL, NULL, 0, NULL, NULL, 2, 'inventoryLog:data_inventory_log:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0, 0, '1', 0);
-- 编辑
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('176275925509503', '176275925509501', '编辑库存日志', NULL, NULL, 0, NULL, NULL, 2, 'inventoryLog:data_inventory_log:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0, 0, '1', 0);
-- 删除
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('176275925509504', '176275925509501', '删除库存日志', NULL, NULL, 0, NULL, NULL, 2, 'inventoryLog:data_inventory_log:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0, 0, '1', 0);
-- 批量删除
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('176275925509505', '176275925509501', '批量删除库存日志', NULL, NULL, 0, NULL, NULL, 2, 'inventoryLog:data_inventory_log:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0, 0, '1', 0);
-- 导出excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('176275925509506', '176275925509501', '导出excel_库存日志', NULL, NULL, 0, NULL, NULL, 2, 'inventoryLog:data_inventory_log:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0, 0, '1', 0);
-- 导入excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('176275925509507', '176275925509501', '导入excel_库存日志', NULL, NULL, 0, NULL, NULL, 2, 'inventoryLog:data_inventory_log:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-10 15:20:55', NULL, NULL, 0, 0, '1', 0);
-- 角色授权(以 admin 角色为例role_id 可替换)
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509508', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509501', NULL, '2025-11-10 15:20:55', '127.0.0.1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509509', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509502', NULL, '2025-11-10 15:20:55', '127.0.0.1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509510', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509503', NULL, '2025-11-10 15:20:55', '127.0.0.1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509511', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509504', NULL, '2025-11-10 15:20:55', '127.0.0.1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509512', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509505', NULL, '2025-11-10 15:20:55', '127.0.0.1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509513', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509506', NULL, '2025-11-10 15:20:55', '127.0.0.1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('176275925509514', 'f6817f48af4fb3af11b9e8bf182f618b', '176275925509507', NULL, '2025-11-10 15:20:55', '127.0.0.1');

View File

@ -0,0 +1,219 @@
<template>
<a-spin :spinning="confirmLoading">
<JFormContainer :disabled="disabled">
<template #detail>
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="InventoryLogForm">
<a-row>
<a-col :span="8">
<a-form-item label="日志类型" v-bind="validateInfos.logType" id="InventoryLogForm-logType" name="logType">
<JDictSelectTag v-model:value="formData.logType" placeholder="请选择" dictCode="inventory_log_type" string-to-number="true" allowClear />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="物料ID" v-bind="validateInfos.itemId" id="InventoryLogForm-itemId" name="itemId">
<a-input v-model:value="formData.itemId" placeholder="请输入物料ID" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="库位ID" v-bind="validateInfos.pointId" id="InventoryLogForm-pointId" name="pointId">
<a-input v-model:value="formData.pointId" placeholder="请输入库位ID" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="容器ID" v-bind="validateInfos.stockId" id="InventoryLogForm-stockId" name="stockId">
<a-input v-model:value="formData.stockId" placeholder="请输入容器ID" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="批次号" v-bind="validateInfos.propC1" id="InventoryLogForm-propC1" name="propC1">
<a-input v-model:value="formData.propC1" placeholder="请输入批次号" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="变动数量" v-bind="validateInfos.changeQty" id="InventoryLogForm-changeQty" name="changeQty">
<a-input-number v-model:value="formData.changeQty" placeholder="请输入变动数量" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="变动前数量" v-bind="validateInfos.beforeQty" id="InventoryLogForm-beforeQty" name="beforeQty">
<a-input-number v-model:value="formData.beforeQty" placeholder="请输入变动前数量" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="变动后数量" v-bind="validateInfos.afterQty" id="InventoryLogForm-afterQty" name="afterQty">
<a-input-number v-model:value="formData.afterQty" placeholder="请输入变动后数量" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="变动前已分配数量" v-bind="validateInfos.beforeAllocatedQty" id="InventoryLogForm-beforeAllocatedQty" name="beforeAllocatedQty">
<a-input-number v-model:value="formData.beforeAllocatedQty" placeholder="请输入变动前已分配数量" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="变动后已分配数量" v-bind="validateInfos.afterAllocatedQty" id="InventoryLogForm-afterAllocatedQty" name="afterAllocatedQty">
<a-input-number v-model:value="formData.afterAllocatedQty" placeholder="请输入变动后已分配数量" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="描述" v-bind="validateInfos.description" id="InventoryLogForm-description" name="description">
<a-input v-model:value="formData.description" placeholder="请输入描述" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="租户ID" v-bind="validateInfos.tenantId" id="InventoryLogForm-tenantId" name="tenantId">
<a-input-number v-model:value="formData.tenantId" placeholder="请输入租户ID" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
</JFormContainer>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, reactive, defineExpose, nextTick, defineProps, computed } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker, getValueType } from '/@/utils';
import { saveOrUpdate } from '../InventoryLog.api';
import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
import { JDictSelectTag } from '@/components/Form';
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: () => ({})},
formBpm: { type: Boolean, default: true }
});
const formRef = ref();
const useForm = Form.useForm;
const emit = defineEmits(['register', 'ok']);
const formData = reactive<Record<string, any>>({
id: '',
logType: undefined,
businessNo: '',
businessDetailId: '',
inventoryId: '',
itemId: '',
pointId: '',
stockId: '',
propC1: '',
changeQty: undefined,
beforeQty: undefined,
afterQty: undefined,
beforeAllocatedQty: undefined,
afterAllocatedQty: undefined,
description: '',
tenantId: undefined,
});
const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 10 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//
const validatorRules = reactive({
});
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
//
const fieldPickers = reactive({
});
//
const disabled = computed(()=>{
if(props.formBpm === true){
if(props.formData.disabled === false){
return false;
}else{
return true;
}
}
return props.formDisabled;
});
/**
* 新增
*/
function add() {
edit({});
}
/**
* 编辑
*/
function edit(record) {
nextTick(() => {
resetFields();
const tmpData = {};
Object.keys(formData).forEach((key) => {
if(record.hasOwnProperty(key)){
tmpData[key] = record[key]
}
})
//
Object.assign(formData, tmpData);
});
}
/**
* 提交数据
*/
async function submitForm() {
try {
//
await validate();
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
formRef.value.scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
}
}
return Promise.reject(errorFields);
}
confirmLoading.value = true;
const isUpdate = ref<boolean>(false);
//
let model = formData;
if (model.id) {
isUpdate.value = true;
}
//
for (let data in model) {
//
model[data] = getDateByPicker(model[data], fieldPickers[data]);
//
if (model[data] instanceof Array) {
let valueType = getValueType(formRef.value.getProps, data);
//
if (valueType === 'string') {
model[data] = model[data].join(',');
}
}
}
await saveOrUpdate(model, isUpdate.value)
.then((res) => {
if (res.success) {
createMessage.success(res.message);
emit('ok');
} else {
createMessage.warning(res.message);
}
})
.finally(() => {
confirmLoading.value = false;
});
}
defineExpose({
add,
edit,
submitForm,
});
</script>
<style lang="less" scoped>
.antd-modal-form {
padding: 14px 20px;
}
</style>

View File

@ -0,0 +1,92 @@
<template>
<j-modal
:title="title"
:maxHeight="500"
:width="800"
:visible="visible"
@ok="handleOk"
:okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
@cancel="handleCancel"
cancelText="关闭"
>
<InventoryLogForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></InventoryLogForm>
<template #footer>
<a-button @click="handleCancel"></a-button>
<a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk"> </a-button>
</template>
</j-modal>
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import InventoryLogForm from './InventoryLogForm.vue';
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
const title = ref<string>('');
const width = ref<number>(800);
const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const registerForm = ref();
const emit = defineEmits(['register', 'success']);
/**
* 新增
*/
function add() {
title.value = '新增';
visible.value = true;
nextTick(() => {
registerForm.value.add();
});
}
/**
* 编辑
* @param record
*/
function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑';
visible.value = true;
nextTick(() => {
registerForm.value.edit(record);
});
}
/**
* 确定按钮点击事件
*/
function handleOk() {
registerForm.value.submitForm();
}
/**
* form保存回调事件
*/
function submitCallback() {
handleCancel();
emit('success');
}
/**
* 取消按钮回调事件
*/
function handleCancel() {
visible.value = false;
}
defineExpose({
add,
edit,
disableSubmit,
});
</script>
<style lang="less">
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
</style>
<style lang="less" scoped></style>

View File

@ -4,7 +4,7 @@
<div class="table-title-bar"> <div class="table-title-bar">
<a-tabs defaultActiveKey="4" @change="tabChange" size="small"> <a-tabs defaultActiveKey="4" @change="tabChange" size="small">
<a-tab-pane tab="异常日志" key="4"></a-tab-pane> <a-tab-pane tab="异常日志" key="4"></a-tab-pane>
<a-tab-pane tab="登录日志" key="1"></a-tab-pane> <!-- <a-tab-pane tab="登录日志" key="1"></a-tab-pane>-->
<a-tab-pane tab="操作日志" key="2"></a-tab-pane> <a-tab-pane tab="操作日志" key="2"></a-tab-pane>
</a-tabs> </a-tabs>
<span class="export-btn" v-if="searchInfo.logType == 2"> <span class="export-btn" v-if="searchInfo.logType == 2">

View File

@ -106,7 +106,7 @@ export const asnDetailColumns: JVxeColumn[] = [
width: 150, width: 150,
async: true, // 异步搜索,默认为 true async: true, // 异步搜索,默认为 true
//查询状态为可用、启用、未删除的容器 //查询状态为可用、启用、未删除的容器
dictCode: 'base_stock where status=0 and iz_active=1 and del_flag=0,stock_code,id', dictCode: 'base_stock where iz_active=1 and del_flag=0,stock_code,id',
tipsContent: '请搜索容器', tipsContent: '请搜索容器',
validateRules: [ validateRules: [
{ {
@ -122,7 +122,7 @@ export const asnDetailColumns: JVxeColumn[] = [
width: 150, width: 150,
async: true, // 异步搜索,默认为 true async: true, // 异步搜索,默认为 true
//查询状态为可用、启用、未删除的库位 //查询状态为可用、启用、未删除的库位
dictCode: 'base_point where status=0 and iz_active=1 and del_flag=0,point_code,id', dictCode: 'base_point where iz_active=1 and del_flag=0,point_code,id',
tipsContent: '请搜索库位', tipsContent: '请搜索库位',
validateRules: [ validateRules: [
{ {