文章目录

  • 前言
  • 一、仓内作业
    • 1.库存冻结
      • 1.1 页面代码
      • 1.2 接口代码
    • 2.库存调整
      • 2.1 页面代码
      • 2.2 接口代码
    • 3.库存盘点
      • 3.1 页面代码
      • 3.2 接口代码

前言

这节主要分为两个模块:

  • 库存冻结:库存冻结的作用是为了防止过度的库存积压,从而避免库存的浪费。
  • 库存调整:库存调整的作用是提高库存利用率,改善库存管理,降低库存成本,改善企业的运作效率。
  • 库存盘点:库存盘点的目的是对现有库存量进行实际核对,以确定物料的实际数量、状况和位置,并核实它们是否与账面记载的数量一致。这样可以有效地发现任何库存记录错误,以及及时补充不足的库存。

一、仓内作业

1.库存冻结

1.1 页面代码

<!-- Warehouse Freeze -->
<template><div class="container"><div><!-- Main Content --><v-card class="mt-5"><v-card-text><v-window v-model="data.activeTab"><v-window-item><div class="operateArea"><v-row no-gutters><!-- Operate Btn --><v-col cols="3" class="col"><tooltip-btnicon="mdi-lock-open-outline":tooltip-text="$t('wms.warehouseWorking.warehouseFreeze.freeze')"@click="method.add(FREEZE_JOB_FREEZE)"></tooltip-btn><tooltip-btnicon="mdi-lock-open-variant-outline":tooltip-text="$t('wms.warehouseWorking.warehouseFreeze.unfreeze')"@click="method.add(FREEZE_JOB_UNFREEZE)"></tooltip-btn><tooltip-btn icon="mdi-refresh" :tooltip-text="$t('system.page.refresh')" @click="method.refresh"></tooltip-btn><tooltip-btn icon="mdi-export-variant" :tooltip-text="$t('system.page.export')" @click="method.exportTable"> </tooltip-btn></v-col><!-- Search Input --><v-col cols="9"><v-row no-gutters @keyup.enter="method.sureSearch"><v-col cols="4"></v-col><v-col cols="4"></v-col><v-col cols="4"><v-text-fieldv-model="data.searchForm.job_code"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('wms.warehouseWorking.warehouseFreeze.job_code')"variant="solo"></v-text-field></v-col></v-row></v-col></v-row></div><!-- Table --><divclass="mt-5":style="{height: cardHeight}"><vxe-table ref="xTable" :column-config="{ minWidth: '100px' }" :data="data.tableData" :height="tableHeight" align="center"><template #empty>{{ i18n.global.t('system.page.noData') }}</template><vxe-column type="seq" width="60"></vxe-column><vxe-column type="checkbox" width="50"></vxe-column><vxe-column field="job_code" width="150px" :title="$t('wms.warehouseWorking.warehouseFreeze.job_code')"></vxe-column><vxe-column field="job_type" width="150px" :title="$t('wms.warehouseWorking.warehouseFreeze.job_type')"><template #default="{ row, column }"><span>{{ formatFreezeJobType(row[column.property]) }}</span></template></vxe-column><vxe-column field="warehouse_name" width="150px" :title="$t('wms.warehouseWorking.warehouseFreeze.warehouse')"></vxe-column><vxe-column field="location_name" width="150px" :title="$t('wms.warehouseWorking.warehouseFreeze.location_name')"></vxe-column><vxe-column field="spu_code" width="150px" :title="$t('base.commodityManagement.spu_code')"></vxe-column><vxe-column field="spu_name" width="150px" :title="$t('base.commodityManagement.spu_name')"></vxe-column><vxe-column field="sku_code" width="150px" :title="$t('base.commodityManagement.sku_code')"></vxe-column><vxe-column field="handler" width="150px" :title="$t('wms.warehouseWorking.warehouseFreeze.handler')"></vxe-column><vxe-column field="handle_time" width="170px" :title="$t('wms.warehouseWorking.warehouseFreeze.handle_time')"><template #default="{ row, column }"><span>{{ formatDate(row[column.property]) }}</span></template></vxe-column><!-- <vxe-column field="creator" :title="$t('wms.warehouseWorking.warehouseFreeze.creator')"></vxe-column><vxe-columnfield="create_time"width="170px":title="$t('wms.warehouseWorking.warehouseFreeze.create_time')"></vxe-column> --><vxe-column field="operate" :title="$t('system.page.operate')" width="100" :resizable="false" show-overflow><template #default="{ row }"><tooltip-btn:flat="true"icon="mdi-eye-outline":tooltip-text="$t('system.page.view')"@click="method.viewRow(row)"></tooltip-btn><!-- <tooltip-btn:flat="true"icon="mdi-book-open-outline":tooltip-text="$t('wms.warehouseWorking.warehouseMove.confirmMove')"@click="method.confirmMove(row)"></tooltip-btn> --><!-- <tooltip-btn:flat="true"icon="mdi-delete-outline":tooltip-text="$t('system.page.delete')":icon-color="errorColor"@click="method.deleteRow(row)"></tooltip-btn> --></template></vxe-column></vxe-table><custom-pager:current-page="data.tablePage.pageIndex":page-size="data.tablePage.pageSize"perfect:total="data.tablePage.total":page-sizes="PAGE_SIZE":layouts="PAGE_LAYOUT"@page-change="method.handlePageChange"></custom-pager></div></v-window-item></v-window></v-card-text></v-card><addOrUpdateDialog:show-dialog="data.showDialog":form="data.dialogForm":freeze-type="data.freezeType"@close="method.closeDialog"@saveSuccess="method.saveSuccess"/></div></div>
</template><script lang="ts" setup>
import { computed, ref, reactive, onActivated, watch, nextTick } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight } from '@/constant/style'
import { WarehouseFreezeVO } from '@/types/WarehouseWorking/WarehouseFreeze'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import { FREEZE_JOB_FREEZE, FREEZE_JOB_UNFREEZE } from '@/constant/warehouseWorking'
import { hookComponent } from '@/components/system'
import { formatFreezeJobType } from '@/utils/format/formatWarehouseWorking'
import { getStockFreezeList, getStockFreezeOne } from '@/api/wms/warehouseFreeze'
import { DEBOUNCE_TIME } from '@/constant/system'
import { setSearchObject } from '@/utils/common'
import { SearchObject } from '@/types/System/Form'
import { formatDate } from '@/utils/format/formatSystem'
import tooltipBtn from '@/components/tooltip-btn.vue'
import addOrUpdateDialog from './add-or-update-freeze.vue'
import i18n from '@/languages/i18n'
import customPager from '@/components/custom-pager.vue'
import { exportData } from '@/utils/exportTable'const xTable = ref()const data = reactive({showDialog: false,freezeType: FREEZE_JOB_FREEZE,timer: ref<any>(null),activeTab: null,searchForm: {job_code: ''},tableData: ref<WarehouseFreezeVO[]>([]),dialogForm: {id: 0,job_code: '',job_type: FREEZE_JOB_FREEZE,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,handler: '',handle_time: '',last_update_time: '',tenant_id: 0,warehouse_name: '',location_name: '',spu_code: '',spu_name: '',sku_code: '',creator: '',create_time: ''},tablePage: reactive({total: 0,pageIndex: 1,pageSize: DEFAULT_PAGE_SIZE,searchObjects: ref<Array<SearchObject>>([])})
})const method = reactive({// Open a dialog to addadd: (jobType: boolean) => {data.freezeType = jobTypedata.dialogForm = {id: 0,job_code: '',job_type: FREEZE_JOB_FREEZE,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,handler: '',handle_time: '',last_update_time: '',tenant_id: 0,warehouse_name: '',location_name: '',spu_code: '',spu_name: '',sku_code: '',creator: '',create_time: ''}nextTick(() => {data.showDialog = true})},// After add or update success.saveSuccess: () => {method.refresh()method.closeDialog()},// Refresh datarefresh: () => {method.getStockProcessList()},// Shut add or update dialogcloseDialog: () => {data.showDialog = false},getStockProcessList: async () => {const { data: res } = await getStockFreezeList(data.tablePage)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.tableData = res.data.rowsdata.tablePage.total = res.data.totals},viewRow: async (row: WarehouseFreezeVO) => {await method.getOne(row.id)nextTick(() => {data.showDialog = true})},getOne: async (id: number) => {const { data: res } = await getStockFreezeOne(id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.dialogForm = res.data},// deleteRow(row: WarehouseFreezeVO) {//   hookComponent.$dialog({//     content: i18n.global.t('system.tips.beforeDeleteMessage'),//     handleConfirm: async () => {//       if (row.id) {//         const { data: res } = await deleteStockFreeze(row.id)//         if (!res.isSuccess) {//           hookComponent.$message({//             type: 'error',//             content: res.errorMessage//           })//           return//         }//         hookComponent.$message({//           type: 'success',//           content: `${ i18n.global.t('system.page.delete') }${ i18n.global.t('system.tips.success') }`//         })//         method.refresh()//       }//     }//   })// },handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {data.tablePage.pageIndex = currentPagedata.tablePage.pageSize = pageSizemethod.refresh()}),exportTable: () => {const $table = xTable.valueexportData({table: $table,filename: i18n.global.t('router.sideBar.warehouseFreeze'),columnFilterMethod({ column }: any) {return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)}})},sureSearch: () => {data.tablePage.searchObjects = setSearchObject(data.searchForm)method.refresh()}
})onActivated(() => {method.refresh()
})const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false }))watch(() => data.searchForm,() => {// debounceif (data.timer) {clearTimeout(data.timer)}data.timer = setTimeout(() => {data.timer = nullmethod.sureSearch()}, DEBOUNCE_TIME)},{deep: true}
)
</script><style scoped lang="less">
.operateArea {width: 100%;min-width: 760px;display: flex;align-items: center;border-radius: 10px;padding: 0 10px;
}.col {display: flex;align-items: center;
}
</style>

<!-- Warehouse Freeze Operate Dialog -->
<template><v-dialog v-model="isShow" :width="'30%'" transition="dialog-top-transition" :persistent="true"><template #default><v-card><v-toolbar class="" color="white" :title="`${$t('router.sideBar.warehouseFreeze')}(${jobTypeComp})`"></v-toolbar><v-card-text><v-form ref="formRef"><v-text-fieldv-model="data.form.spu_code":label="$t('base.commodityManagement.spu_code')":rules="data.rules.spu_code"variant="outlined"readonlyclearable@click="method.openCommoditySelect"@click:clear="method.clearCommodity"></v-text-field><v-text-fieldv-model="data.form.spu_name":label="$t('base.commodityManagement.spu_name')":rules="data.rules.spu_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.sku_code":label="$t('base.commodityManagement.sku_code')":rules="data.rules.sku_code"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.location_name":label="$t('wms.warehouseWorking.warehouseFreeze.location_name')":rules="data.rules.location_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.warehouse_name":label="$t('wms.warehouseWorking.warehouseFreeze.warehouse')":rules="data.rules.warehouse_name"variant="outlined"disabled></v-text-field></v-form></v-card-text><v-card-actions class="justify-end"><v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn><v-btn color="primary" variant="text" :disabled="operateDisabled" @click="method.submit">{{ $t('system.page.submit') }}</v-btn></v-card-actions></v-card></template></v-dialog><commodity-select:show-dialog="data.showCommodityDialogSelect":sql-title="commoditySqlTitle"@close="method.closeCommodityDialogSelect"@sureSelect="method.sureSelectCommodity"/><!-- <location-select :show-dialog="data.showLocationDialogSelect" @close="method.closeLocationDialogSelect" @sureSelect="method.sureSelectLocation" /> -->
</template><script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { FREEZE_JOB_FREEZE, FREEZE_JOB_UNFREEZE } from '@/constant/warehouseWorking'
import { hookComponent } from '@/components/system/index'
import { addStockFreeze } from '@/api/wms/warehouseFreeze'
import { WarehouseFreezeVO } from '@/types/WarehouseWorking/WarehouseFreeze'
import { removeObjectNull } from '@/utils/common'
import commoditySelect from '@/components/select/commodity-select.vue'
import i18n from '@/languages/i18n'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const isUpdate = computed(() => props.form.id && props.form.id > 0)
const operateDisabled = computed(() => !!isUpdate.value)const props = defineProps<{showDialog: booleanform: WarehouseFreezeVOfreezeType: boolean
}>()const isShow = computed(() => props.showDialog)
const jobTypeComp = computed(() => (data.form.job_type === FREEZE_JOB_FREEZE? i18n.global.t('wms.warehouseWorking.warehouseFreeze.freeze'): i18n.global.t('wms.warehouseWorking.warehouseFreeze.unfreeze')))// Unfreeze should be filter freezed.
const commoditySqlTitle = computed(() => (data.form.job_type === FREEZE_JOB_UNFREEZE ? 'frozen' : ''))const data = reactive({showCommodityDialogSelect: false,showLocationDialogSelect: false,form: ref<WarehouseFreezeVO>({id: 0,job_code: '',job_type: FREEZE_JOB_FREEZE,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,handler: '',handle_time: '',last_update_time: '',tenant_id: 0,warehouse_name: '',location_name: '',spu_code: '',spu_name: '',sku_code: '',creator: '',create_time: ''}),rules: {warehouse_name: [],location_name: [],spu_code: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_code') }!`],spu_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_name') }!`],sku_code: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_code') }!`]}
})const method = reactive({closeDialog: () => {emit('close')},initForm: () => {data.form = props.formdata.form.job_type = props.freezeType},openCommoditySelect: () => {// Open select modal after UI rendered.setTimeout(() => {data.showCommodityDialogSelect = true}, 100)},closeCommodityDialogSelect: () => {data.showCommodityDialogSelect = false},openLocationSelect: () => {setTimeout(() => {data.showLocationDialogSelect = true}, 100)},closeLocationDialogSelect: () => {data.showLocationDialogSelect = false},sureSelectCommodity: (selectRecords: any) => {try {data.form.sku_id = selectRecords[0].sku_iddata.form.spu_code = selectRecords[0].spu_codedata.form.spu_name = selectRecords[0].spu_namedata.form.sku_code = selectRecords[0].sku_codedata.form.goods_owner_id = selectRecords[0].goods_owner_iddata.form.goods_location_id = selectRecords[0].goods_location_iddata.form.warehouse_name = selectRecords[0].warehouse_namedata.form.location_name = selectRecords[0].location_name} catch (error) {console.error(error)}},clearCommodity: () => {data.form.sku_id = 0data.form.spu_code = ''data.form.spu_name = ''data.form.sku_code = ''},clearLocation: () => {data.form.goods_location_id = 0data.form.warehouse_name = ''data.form.location_name = ''},submit: async () => {const { valid } = await formRef.value.validate()const form = method.constructFormBody()if (valid) {const { data: res } = await addStockFreeze(form)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`})emit('saveSuccess')} else {hookComponent.$message({type: 'error',content: i18n.global.t('system.checkText.checkFormFail')})}},constructFormBody: () => {let form = { ...data.form }form = removeObjectNull(form)return form}
})watch(() => isShow.value,(val) => {if (val) {method.initForm()}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}
</style>

1.2 接口代码

     [Route("stockfreeze")][ApiController][ApiExplorerSettings(GroupName = "WMS")]public class StockfreezeController : BaseController{#region Args/// <summary>/// stockfreeze Service/// </summary>private readonly IStockfreezeService _stockfreezeService;/// <summary>/// Localizer Service/// </summary>private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;#endregion#region constructor/// <summary>/// constructor/// </summary>/// <param name="stockfreezeService">stockfreeze Service</param>/// <param name="stringLocalizer">Localizer</param>public StockfreezeController(IStockfreezeService stockfreezeService, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer){this._stockfreezeService = stockfreezeService;this._stringLocalizer= stringLocalizer;}#endregion#region Api/// <summary>/// page search/// </summary>/// <param name="pageSearch">args</param>/// <returns></returns>[HttpPost("list")]public async Task<ResultModel<PageData<StockfreezeViewModel>>> PageAsync(PageSearch pageSearch){var (data, totals) = await _stockfreezeService.PageAsync(pageSearch, CurrentUser);return ResultModel<PageData<StockfreezeViewModel>>.Success(new PageData<StockfreezeViewModel>{Rows = data,Totals = totals});}/// <summary>/// get all records/// </summary>/// <returns>args</returns>[HttpGet("all")]public async Task<ResultModel<List<StockfreezeViewModel>>> GetAllAsync(){var data = await _stockfreezeService.GetAllAsync(CurrentUser);if (data.Any()){return ResultModel<List<StockfreezeViewModel>>.Success(data);}else{return ResultModel<List<StockfreezeViewModel>>.Success(new List<StockfreezeViewModel>());}}/// <summary>/// get a record by id/// </summary>/// <returns>args</returns>[HttpGet]public async Task<ResultModel<StockfreezeViewModel>> GetAsync(int id){var data = await _stockfreezeService.GetAsync(id);if (data!=null){return ResultModel<StockfreezeViewModel>.Success(data);}else{return ResultModel<StockfreezeViewModel>.Error(_stringLocalizer["not_exists_entity"]);}}/// <summary>/// add a new record/// </summary>/// <param name="viewModel">args</param>/// <returns></returns>[HttpPost]public async Task<ResultModel<int>> AddAsync(StockfreezeViewModel viewModel){var (id, msg) = await _stockfreezeService.AddAsync(viewModel,CurrentUser);if (id > 0){return ResultModel<int>.Success(id);}else{return ResultModel<int>.Error(msg);}}/// <summary>/// update a record/// </summary>/// <param name="viewModel">args</param>/// <returns></returns>[HttpPut]public async Task<ResultModel<bool>> UpdateAsync(StockfreezeViewModel viewModel){var (flag, msg) = await _stockfreezeService.UpdateAsync(viewModel);if (flag){return ResultModel<bool>.Success(flag);}else{return ResultModel<bool>.Error(msg, 400, flag);}}/// <summary>/// delete a record/// </summary>/// <param name="id">id</param>/// <returns></returns>[HttpDelete]public async Task<ResultModel<string>> DeleteAsync(int id){var (flag, msg) = await _stockfreezeService.DeleteAsync(id);if (flag){return ResultModel<string>.Success(msg);}else{return ResultModel<string>.Error(msg);}}#endregion}

2.库存调整


库存调整主要是查询,数据来源与仓内加工和盘点

2.1 页面代码

<!-- Warehouse Adjust -->
<template><div class="container"><div><!-- Main Content --><v-card class="mt-5"><v-card-text><v-window v-model="data.activeTab"><v-window-item><div class="operateArea"><v-row no-gutters><!-- Operate Btn --><v-col cols="3" class="col"><!-- <tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.add')" @click="method.add()"></tooltip-btn> --><tooltip-btn icon="mdi-refresh" :tooltip-text="$t('system.page.refresh')" @click="method.refresh"></tooltip-btn><tooltip-btn icon="mdi-export-variant" :tooltip-text="$t('system.page.export')" @click="method.exportTable"> </tooltip-btn></v-col><!-- Search Input --><v-col cols="9"><v-row no-gutters @keyup.enter="method.sureSearch"><v-col cols="4"></v-col><v-col cols="4"></v-col><v-col cols="4"><v-text-fieldv-model="data.searchForm.job_code"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('wms.warehouseWorking.warehouseAdjust.job_code')"variant="solo"></v-text-field></v-col></v-row></v-col></v-row></div><!-- Table --><divclass="mt-5":style="{height: cardHeight}"><vxe-table ref="xTable" :column-config="{ minWidth: '100px' }" :data="data.tableData" :height="tableHeight" align="center"><template #empty>{{ i18n.global.t('system.page.noData') }}</template><vxe-column type="seq" width="60"></vxe-column><vxe-column type="checkbox" width="50"></vxe-column><vxe-column field="job_code" width="150px" :title="$t('wms.warehouseWorking.warehouseAdjust.job_code')"></vxe-column><vxe-column field="job_type" width="150px" :title="$t('wms.warehouseWorking.warehouseAdjust.job_type')"><template #default="{ row, column }"><span>{{ formatAdjustJobType(row[column.property]) }}</span></template></vxe-column><vxe-column field="spu_code" width="150px" :title="$t('base.commodityManagement.spu_code')"></vxe-column><vxe-column field="spu_name" width="150px" :title="$t('base.commodityManagement.spu_name')"></vxe-column><vxe-column field="sku_code" width="150px" :title="$t('base.commodityManagement.sku_code')"></vxe-column><vxe-column field="qty" width="150px" :title="$t('wms.warehouseWorking.warehouseAdjust.qty')"></vxe-column><vxe-column field="warehouse_name" width="150px" :title="$t('wms.warehouseWorking.warehouseAdjust.warehouse')"></vxe-column><vxe-column field="location_name" width="150px" :title="$t('wms.warehouseWorking.warehouseAdjust.location_name')"></vxe-column><!-- <vxe-column field="handler" width="150px" :title="$t('wms.warehouseWorking.warehouseAdjust.handler')"></vxe-column><vxe-columnfield="handle_time"width="170px":title="$t('wms.warehouseWorking.warehouseAdjust.handle_time')"></vxe-column> --><vxe-column field="creator" :title="$t('wms.warehouseWorking.warehouseAdjust.creator')"></vxe-column><vxe-column field="create_time" width="170px" :title="$t('wms.warehouseWorking.warehouseAdjust.create_time')"></vxe-column><!-- <vxe-column field="operate" :title="$t('system.page.operate')" width="200" :resizable="false" show-overflow><template #default="{ row }"><tooltip-btn:flat="true"icon="mdi-eye-outline":tooltip-text="$t('system.page.view')"@click="method.viewRow(row)"></tooltip-btn><tooltip-btn:flat="true"icon="mdi-book-open-outline":tooltip-text="$t('wms.warehouseWorking.warehouseAdjust.confirmAdjust')":disabled="method.confirmAdjustBtnDisabled(row)"@click="method.confirmAdjust(row)"></tooltip-btn><tooltip-btn:flat="true"icon="mdi-delete-outline":tooltip-text="$t('system.page.delete')":icon-color="errorColor":disabled="method.confirmAdjustBtnDisabled(row)"@click="method.deleteRow(row)"></tooltip-btn></template></vxe-column> --></vxe-table><custom-pager:current-page="data.tablePage.pageIndex":page-size="data.tablePage.pageSize"perfect:total="data.tablePage.total":page-sizes="PAGE_SIZE":layouts="PAGE_LAYOUT"@page-change="method.handlePageChange"></custom-pager></div></v-window-item></v-window></v-card-text></v-card><addOrUpdateDialog:show-dialog="data.showDialog":form="data.dialogForm":process-type="data.processType"@close="method.closeDialog"@saveSuccess="method.saveSuccess"/></div></div>
</template><script lang="ts" setup>
import { computed, ref, reactive, watch, nextTick, onActivated } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight } from '@/constant/style'
import { WarehouseAdjustVO } from '@/types/WarehouseWorking/WarehouseAdjust'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import { hookComponent } from '@/components/system'
import { deleteStockAdjust, getStockAdjustList, getStockAdjustOne, confirmStockAdjust } from '@/api/wms/warehouseAdjust'
import { PROCESS_JOB_COMBINE } from '@/constant/warehouseWorking'
import { DEBOUNCE_TIME } from '@/constant/system'
import { setSearchObject } from '@/utils/common'
import { SearchObject } from '@/types/System/Form'
import { formatAdjustJobType } from '@/utils/format/formatWarehouseWorking'
import tooltipBtn from '@/components/tooltip-btn.vue'
import addOrUpdateDialog from './add-or-update-adjust.vue'
import i18n from '@/languages/i18n'
import customPager from '@/components/custom-pager.vue'
import { exportData } from '@/utils/exportTable'const xTable = ref()const data = reactive({showDialog: false,processType: PROCESS_JOB_COMBINE,timer: ref<any>(null),activeTab: null,searchForm: {job_code: ''},tableData: ref<WarehouseAdjustVO[]>([]),dialogForm: {id: 0,// job_type: AdjustJobType.TAKE,job_code: '',sku_id: 0,goods_owner_id: 0,goods_location_id: 0,qty: 0,is_update_stock: false,source_table_id: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: ''},tablePage: reactive({total: 0,pageIndex: 1,pageSize: DEFAULT_PAGE_SIZE,searchObjects: ref<Array<SearchObject>>([])})
})const method = reactive({// Open a dialog to addadd: () => {data.dialogForm = {id: 0,// job_type: AdjustJobType.TAKE,job_code: '',sku_id: 0,goods_owner_id: 0,goods_location_id: 0,qty: 0,is_update_stock: false,source_table_id: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: ''}nextTick(() => {data.showDialog = true})},// After add or update success.saveSuccess: () => {method.refresh()method.closeDialog()},// Refresh datarefresh: () => {method.getStockProcessList()},// Shut add or update dialogcloseDialog: () => {data.showDialog = false},getStockProcessList: async () => {const { data: res } = await getStockAdjustList(data.tablePage)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.tableData = res.data.rowsdata.tablePage.total = res.data.totals},viewRow: async (row: WarehouseAdjustVO) => {await method.getOne(row.id)nextTick(() => {data.showDialog = true})},getOne: async (id: number) => {const { data: res } = await getStockAdjustOne(id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.dialogForm = res.data},deleteRow(row: WarehouseAdjustVO) {hookComponent.$dialog({content: i18n.global.t('system.tips.beforeDeleteMessage'),handleConfirm: async () => {if (row.id) {const { data: res } = await deleteStockAdjust(row.id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('system.page.delete') }${ i18n.global.t('system.tips.success') }`})method.refresh()}}})},handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {data.tablePage.pageIndex = currentPagedata.tablePage.pageSize = pageSizemethod.refresh()}),exportTable: () => {const $table = xTable.valueexportData({table: $table,filename: i18n.global.t('router.sideBar.warehouseAdjust'),columnFilterMethod({ column }: any) {return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)}})},sureSearch: () => {data.tablePage.searchObjects = setSearchObject(data.searchForm)method.refresh()},// The btn will become disabled when the 'process_status' is false// confirmAdjustBtnDisabled: (row: WarehouseAdjustVO) => row.is_update_stock === true,confirmAdjust: async (row: WarehouseAdjustVO) => {hookComponent.$dialog({content: i18n.global.t('wms.warehouseWorking.warehouseAdjust.beforeConfirmAdjust'),handleConfirm: async () => {if (row.id) {const { data: res } = await confirmStockAdjust(row.id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('wms.warehouseWorking.warehouseAdjust.confirmAdjust') }${ i18n.global.t('system.tips.success') }`})method.refresh()}}})}
})onActivated(() => {method.refresh()
})const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false }))watch(() => data.searchForm,() => {// debounceif (data.timer) {clearTimeout(data.timer)}data.timer = setTimeout(() => {data.timer = nullmethod.sureSearch()}, DEBOUNCE_TIME)},{deep: true}
)
</script><style scoped lang="less">
.operateArea {width: 100%;min-width: 760px;display: flex;align-items: center;border-radius: 10px;padding: 0 10px;
}.col {display: flex;align-items: center;
}
</style>

<!-- Warehouse Adjust Operate Dialog -->
<template><v-dialog v-model="isShow" :width="'30%'" transition="dialog-top-transition" :persistent="true"><template #default><v-card><v-toolbar class="" color="white" :title="`${$t('router.sideBar.warehouseAdjust')}`"></v-toolbar><v-card-text><v-form ref="formRef"><v-selectv-model="data.form.job_type":items="data.combobox.job_type"item-title="label"item-value="value":rules="data.rules.job_type":label="$t('wms.warehouseWorking.warehouseAdjust.job_type')"variant="outlined"clearable:disabled="isAssociatedJobType"@update:model-value="method.changeJobType"></v-select><v-text-fieldv-model="data.form.spu_code":label="$t('base.commodityManagement.spu_code')":rules="data.rules.spu_code"variant="outlined"readonlyclearable:disabled="isAssociatedJobType"@click="method.openCommoditySelect"@click:clear="method.clearCommodity"></v-text-field><v-text-fieldv-model="data.form.spu_name":label="$t('base.commodityManagement.spu_name')":rules="data.rules.spu_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.sku_code":label="$t('base.commodityManagement.sku_code')":rules="data.rules.sku_code"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.warehouse_name":label="$t('wms.warehouseWorking.warehouseAdjust.warehouse')":rules="data.rules.warehouse_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.location_name":label="$t('wms.warehouseWorking.warehouseAdjust.location_name')":rules="data.rules.location_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.qty":label="$t('wms.warehouseWorking.warehouseAdjust.qty')":rules="data.rules.qty"variant="outlined"></v-text-field></v-form></v-card-text><v-card-actions class="justify-end"><v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn><v-btn color="primary" variant="text" :disabled="operateDisabled" @click="method.submit">{{ $t('system.page.submit') }}</v-btn></v-card-actions></v-card></template></v-dialog><commodity-select:show-dialog="data.showCommodityDialogSelect"@close="method.closeCommodityDialogSelect"@sureSelect="method.sureSelectCommodity"/>
</template><script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addStockAdjust } from '@/api/wms/warehouseAdjust'
import { WarehouseAdjustVO, AdjustJobType } from '@/types/WarehouseWorking/WarehouseAdjust'
import { removeObjectNull } from '@/utils/common'
import commoditySelect from '@/components/select/commodity-select.vue'
import { IsInteger } from '@/utils/dataVerification/formRule'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const isUpdate = computed(() => props.form.id && props.form.id > 0)
const operateDisabled = computed(() => !!isUpdate.value)const props = defineProps<{showDialog: booleanform: WarehouseAdjustVO// If 'associatedJobType' has value, the 'job_type' and 'sku' should be disabled.associatedJobType?: AdjustJobType
}>()const isShow = computed(() => props.showDialog)
const isAssociatedJobType = computed(() => !!(props.associatedJobType && props.associatedJobType > 0))const data = reactive({showCommodityDialogSelect: false,showLocationDialogSelect: false,form: ref<WarehouseAdjustVO>({id: 0,// job_type: AdjustJobType.UNKNOW,job_code: '',sku_id: 0,goods_owner_id: 0,goods_location_id: 0,qty: 0,is_update_stock: false,source_table_id: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: '',creator: '',create_time: ''}),rules: {job_type: [],qty: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseAdjust.qty') }!`,(val: number) => IsInteger(val, 'greaterThanZero') === '' || IsInteger(val, 'greaterThanZero')],warehouse_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseAdjust.warehouse') }!`],location_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseAdjust.location_name') }!`],spu_code: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_code') }!`],spu_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_name') }!`],sku_code: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_code') }!`],sku_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_name') }!`]},combobox: ref<{job_type: {label: stringvalue: AdjustJobType}[]}>({job_type: [{label: i18n.global.t('wms.warehouseWorking.warehouseAdjust.warehouseTake'),value: AdjustJobType.TAKE},{label: i18n.global.t('wms.warehouseWorking.warehouseAdjust.processCombine'),value: AdjustJobType.PROCESS_COMBINE},{label: i18n.global.t('wms.warehouseWorking.warehouseAdjust.processSplit'),value: AdjustJobType.PROCESS_SPLIT},{label: i18n.global.t('wms.warehouseWorking.warehouseAdjust.warehouseMove'),value: AdjustJobType.MOVE}]})
})const method = reactive({closeDialog: () => {emit('close')},initForm: () => {data.form = props.formif (isAssociatedJobType.value) {data.form.job_type = props.associatedJobType}},changeJobType: (value: any) => {// Find the ID corresponding valueconst jobType = data.combobox.job_type.find((item) => item.value === value)if (jobType) {data.form.job_type = jobType.value}},openCommoditySelect: () => {// Open select modal after UI rendered.setTimeout(() => {data.showCommodityDialogSelect = true}, 100)},closeCommodityDialogSelect: () => {data.showCommodityDialogSelect = false},sureSelectCommodity: (selectRecords: any) => {try {data.form.sku_id = selectRecords[0].sku_iddata.form.goods_owner_id = selectRecords[0].goods_owner_iddata.form.goods_location_id = selectRecords[0].goods_location_iddata.form.warehouse_name = selectRecords[0].warehousedata.form.location_name = selectRecords[0].location_namedata.form.spu_code = selectRecords[0].spu_codedata.form.spu_name = selectRecords[0].spu_namedata.form.sku_code = selectRecords[0].sku_code} catch (error) {// console.error(error)}},clearCommodity: () => {data.form.sku_id = 0data.form.goods_location_id = 0data.form.warehouse_name = ''data.form.location_name = ''data.form.spu_code = ''data.form.spu_name = ''data.form.sku_code = ''},submit: async () => {const { valid } = await formRef.value.validate()const form = method.constructFormBody()if (valid) {const { data: res } = await addStockAdjust(form)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`})emit('saveSuccess')} else {hookComponent.$message({type: 'error',content: i18n.global.t('system.checkText.checkFormFail')})}},constructFormBody: () => {let form = { ...data.form }form = removeObjectNull(form)return form}
})watch(() => isShow.value,(val) => {if (val) {method.initForm()}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}
</style>

2.2 接口代码

     [Route("stockadjust")][ApiController][ApiExplorerSettings(GroupName = "WMS")]public class StockadjustController : BaseController{#region Args/// <summary>/// stockadjust Service/// </summary>private readonly IStockadjustService _stockadjustService;/// <summary>/// Localizer Service/// </summary>private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;#endregion#region constructor/// <summary>/// constructor/// </summary>/// <param name="stockadjustService">stockadjust Service</param>/// <param name="stringLocalizer">Localizer</param>public StockadjustController(IStockadjustService stockadjustService, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer){this._stockadjustService = stockadjustService;this._stringLocalizer= stringLocalizer;}#endregion#region Api/// <summary>/// page search/// </summary>/// <param name="pageSearch">args</param>/// <returns></returns>[HttpPost("list")]public async Task<ResultModel<PageData<StockadjustViewModel>>> PageAsync(PageSearch pageSearch){var (data, totals) = await _stockadjustService.PageAsync(pageSearch, CurrentUser);return ResultModel<PageData<StockadjustViewModel>>.Success(new PageData<StockadjustViewModel>{Rows = data,Totals = totals});}#endregion}

3.库存盘点

3.1 页面代码

<!-- Warehouse Taking -->
<template><div class="container"><div><!-- Main Content --><v-card class="mt-5"><v-card-text><v-window v-model="data.activeTab"><v-window-item><div class="operateArea"><v-row no-gutters><!-- Operate Btn --><v-col cols="3" class="col"><tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.add')" @click="method.add()"></tooltip-btn><tooltip-btn icon="mdi-refresh" :tooltip-text="$t('system.page.refresh')" @click="method.refresh"></tooltip-btn><tooltip-btn icon="mdi-export-variant" :tooltip-text="$t('system.page.export')" @click="method.exportTable"> </tooltip-btn></v-col><!-- Search Input --><v-col cols="9"><v-row no-gutters @keyup.enter="method.sureSearch"><v-col cols="4"></v-col><v-col cols="4"></v-col><v-col cols="4"><v-text-fieldv-model="data.searchForm.job_code"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('wms.warehouseWorking.warehouseTaking.job_code')"variant="solo"></v-text-field></v-col></v-row></v-col></v-row></div><!-- Table --><divclass="mt-5":style="{height: cardHeight}"><vxe-table ref="xTable" :column-config="{ minWidth: '100px' }" :data="data.tableData" :height="tableHeight" align="center"><template #empty>{{ i18n.global.t('system.page.noData') }}</template><vxe-column type="seq" width="60"></vxe-column><vxe-column type="checkbox" width="50"></vxe-column><vxe-column field="job_code" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.job_code')"></vxe-column><vxe-column field="job_status" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.job_status')"><template #default="{ row, column }"><span>{{ formatTakingJobStatus(row[column.property]) }}</span></template></vxe-column><vxe-column field="spu_code" width="150px" :title="$t('base.commodityManagement.spu_code')"></vxe-column><vxe-column field="spu_name" width="150px" :title="$t('base.commodityManagement.spu_name')"></vxe-column><vxe-column field="sku_code" width="150px" :title="$t('base.commodityManagement.sku_code')"></vxe-column><vxe-column field="warehouse_name" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.warehouse')"></vxe-column><vxe-column field="location_name" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.location_name')"></vxe-column><vxe-column field="book_qty" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.book_qty')"></vxe-column><vxe-column field="counted_qty" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.counted_qty')"></vxe-column><vxe-column field="difference_qty" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.difference_qty')"></vxe-column><vxe-column field="handler" width="150px" :title="$t('wms.warehouseWorking.warehouseTaking.handler')"></vxe-column><vxe-column field="handle_time" width="170px" :title="$t('wms.warehouseWorking.warehouseTaking.handle_time')"><template #default="{ row, column }"><span>{{ formatDate(row[column.property]) }}</span></template></vxe-column><vxe-column field="creator" :title="$t('wms.warehouseWorking.warehouseTaking.creator')"></vxe-column><vxe-column field="create_time" width="170px" :title="$t('wms.warehouseWorking.warehouseTaking.create_time')"></vxe-column><vxe-column field="operate" :title="$t('system.page.operate')" width="250" :resizable="false" show-overflow><template #default="{ row }"><tooltip-btn:flat="true"icon="mdi-eye-outline":tooltip-text="$t('system.page.view')"@click="method.viewRow(row)"></tooltip-btn><tooltip-btn:flat="true"icon="mdi-book-check-outline":tooltip-text="$t('wms.warehouseWorking.warehouseTaking.confirmTaking')":disabled="method.isConfirmTaking(row)"@click="method.confirmTaking(row)"></tooltip-btn><tooltip-btn:flat="true"icon="mdi-book-open-outline":tooltip-text="$t('wms.warehouseWorking.warehouseProcessing.confirmAdjust')":disabled="method.confirmAdjustBtnDisabled(row)"@click="method.confirmAdjust(row)"></tooltip-btn><tooltip-btn:flat="true"icon="mdi-delete-outline":tooltip-text="$t('system.page.delete')":icon-color="errorColor":disabled="method.isConfirmTaking(row)"@click="method.deleteRow(row)"></tooltip-btn></template></vxe-column></vxe-table><custom-pager:current-page="data.tablePage.pageIndex":page-size="data.tablePage.pageSize"perfect:total="data.tablePage.total":page-sizes="PAGE_SIZE":layouts="PAGE_LAYOUT"@page-change="method.handlePageChange"></custom-pager></div></v-window-item></v-window></v-card-text></v-card><addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" /><number-input:show-dialog="data.showDialogNumberInput":form="data.dialogForm"@close="method.closeDialogNumberInput"@saveSuccess="method.saveSuccessNumberInput"/></div></div>
</template><script lang="ts" setup>
import { computed, ref, reactive, onActivated, watch, nextTick } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import { WarehouseTakingVO } from '@/types/WarehouseWorking/WarehouseTaking'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import { hookComponent } from '@/components/system'
import { TAKING_JOB_FINISH, TAKING_JOB_UNFINISH } from '@/constant/warehouseWorking'
import { deleteStockTaking, getStockTakingList, getStockTakingOne, confirmAdjustment } from '@/api/wms/warehouseTaking'
import { DEBOUNCE_TIME } from '@/constant/system'
import { setSearchObject } from '@/utils/common'
import { SearchObject } from '@/types/System/Form'
import { formatTakingJobStatus } from '@/utils/format/formatWarehouseWorking'
import { formatDate } from '@/utils/format/formatSystem'
import tooltipBtn from '@/components/tooltip-btn.vue'
import addOrUpdateDialog from './add-or-update-taking.vue'
import numberInput from './number-input.vue'
import i18n from '@/languages/i18n'
import customPager from '@/components/custom-pager.vue'
import { exportData } from '@/utils/exportTable'const xTable = ref()const data = reactive({showDialog: false,showDialogNumberInput: false,timer: ref<any>(null),activeTab: null,tableData: ref<WarehouseTakingVO[]>([]),dialogForm: ref<WarehouseTakingVO>({id: 0,job_code: '',job_status: TAKING_JOB_FINISH,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,book_qty: 0,counted_qty: 0,difference_qty: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: '',handler: '',handle_time: '',adjust_status: false,creator: '',create_time: ''}),searchForm: {job_code: ''},tablePage: reactive({total: 0,pageIndex: 1,pageSize: DEFAULT_PAGE_SIZE,searchObjects: ref<Array<SearchObject>>([])})
})const method = reactive({// Open a dialog to addadd: () => {data.dialogForm = {id: 0,job_code: '',job_status: TAKING_JOB_FINISH,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,book_qty: 0,counted_qty: 0,difference_qty: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: '',handler: '',handle_time: '',adjust_status: false,creator: '',create_time: ''}nextTick(() => {data.showDialog = true})},// After add or update success.saveSuccess: () => {method.refresh()method.closeDialog()},// After confirm taking.saveSuccessNumberInput: () => {method.refresh()method.closeDialogNumberInput()},// Refresh datarefresh: () => {method.getStockProcessList()},// Shut add or update dialogcloseDialog: () => {data.showDialog = false},// Shut number input dialogcloseDialogNumberInput: () => {data.showDialogNumberInput = false},getStockProcessList: async () => {const { data: res } = await getStockTakingList(data.tablePage)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.tableData = res.data.rowsdata.tablePage.total = res.data.totals},viewRow: async (row: WarehouseTakingVO) => {await method.getOne(row.id)nextTick(() => {data.showDialog = true})},getOne: async (id: number) => {const { data: res } = await getStockTakingOne(id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.dialogForm = res.data},deleteRow(row: WarehouseTakingVO) {hookComponent.$dialog({content: i18n.global.t('system.tips.beforeDeleteMessage'),handleConfirm: async () => {if (row.id) {const { data: res } = await deleteStockTaking(row.id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('system.page.delete') }${ i18n.global.t('system.tips.success') }`})method.refresh()}}})},handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {data.tablePage.pageIndex = currentPagedata.tablePage.pageSize = pageSizemethod.refresh()}),exportTable: () => {const $table = xTable.valueexportData({table: $table,filename: i18n.global.t('router.sideBar.warehouseTaking'),columnFilterMethod({ column }: any) {return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)}})},sureSearch: () => {data.tablePage.searchObjects = setSearchObject(data.searchForm)method.refresh()},// The btn will become disabled when the 'job_status' is falseconfirmAdjustBtnDisabled: (row: WarehouseTakingVO) => row.job_status === TAKING_JOB_UNFINISH || !!row.adjust_status,// The btn will become disabled when the 'job_status' is trueisConfirmTaking: (row: WarehouseTakingVO) => row.job_status === TAKING_JOB_FINISH,confirmTaking: async (row: WarehouseTakingVO) => {data.dialogForm = rownextTick(() => {data.showDialogNumberInput = true})},confirmAdjust: async (row: WarehouseTakingVO) => {hookComponent.$dialog({content: i18n.global.t('wms.warehouseWorking.warehouseTaking.beforeConfirmAdjust'),handleConfirm: async () => {if (row.id) {const { data: res } = await confirmAdjustment(row.id)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('wms.warehouseWorking.warehouseTaking.confirmAdjust') }${ i18n.global.t('system.tips.success') }`})method.refresh()}}})}
})onActivated(() => {method.refresh()
})const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false }))watch(() => data.searchForm,() => {// debounceif (data.timer) {clearTimeout(data.timer)}data.timer = setTimeout(() => {data.timer = nullmethod.sureSearch()}, DEBOUNCE_TIME)},{deep: true}
)
</script><style scoped lang="less">
.operateArea {width: 100%;min-width: 760px;display: flex;align-items: center;border-radius: 10px;padding: 0 10px;
}.col {display: flex;align-items: center;
}
</style>

<!-- Warehouse Taking Operate Dialog -->
<template><v-dialog v-model="isShow" :width="'30%'" transition="dialog-top-transition" :persistent="true"><template #default><v-card><v-toolbar color="white" :title="`${$t('router.sideBar.warehouseTaking')}`"></v-toolbar><v-card-text><div class="header"><div class="headerBtn"><tooltip-btnsize="x-small"icon="mdi-home-plus-outline":tooltip-text="$t('wms.warehouseWorking.warehouseTaking.addFromStock')"@click="method.openCommoditySelect()"></tooltip-btn><tooltip-btnsize="x-small"icon="mdi-store-plus-outline":tooltip-text="$t('wms.warehouseWorking.warehouseTaking.addFromCommodity')":disabled="isFromStock"@click="method.openSkuSelect()"></tooltip-btn></div><div class="headerTips"><v-tooltip :text="$t('wms.warehouseWorking.warehouseTaking.addTips')"><template #activator="{ props }"><v-btn v-bind="props" size="x-small" variant="text" icon="mdi-help-circle" color="blue-lighten-2"></v-btn></template></v-tooltip></div></div><v-form ref="formRef"><v-text-fieldv-model="data.form.spu_code":label="$t('base.commodityManagement.spu_code')":rules="data.rules.spu_code"variant="outlined"readonlyclearable@click:clear="method.clearCommodity"></v-text-field><v-text-fieldv-model="data.form.spu_name":label="$t('base.commodityManagement.spu_name')":rules="data.rules.spu_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.sku_code":label="$t('base.commodityManagement.sku_code')":rules="data.rules.sku_code"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.warehouse_name":label="$t('wms.warehouseWorking.warehouseTaking.warehouse')":rules="data.rules.warehouse_name"variant="outlined"disabled></v-text-field><v-text-fieldv-model="data.form.location_name":label="$t('wms.warehouseWorking.warehouseTaking.location_name')":rules="data.rules.location_name"variant="outlined":disabled="isFromStock"@click="method.openLocationSelect"@click:clear="method.clearLocation"></v-text-field><v-text-fieldv-model="data.form.book_qty":label="$t('wms.warehouseWorking.warehouseTaking.book_qty')":rules="data.rules.book_qty"variant="outlined"disabled></v-text-field></v-form></v-card-text><v-card-actions class="justify-end"><v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn><v-btn color="primary" variant="text" :disabled="operateDisabled" @click="method.submit">{{ $t('system.page.submit') }}</v-btn></v-card-actions></v-card></template></v-dialog><commodity-select:show-dialog="data.showCommodityDialogSelect"@close="method.closeCommodityDialogSelect"@sureSelect="method.sureSelectCommodity"/><sku-select :show-dialog="data.showSkuDialogSelect" @close="method.closeDialogSelectSku" @sureSelect="method.sureSelectSku" /><location-select :show-dialog="data.showLocationDialogSelect" @close="method.closeDialogSelectLocation" @sureSelect="method.sureSelectLocation" />
</template><script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { hookComponent } from '@/components/system/index'
import { addStockTaking } from '@/api/wms/warehouseTaking'
import { WarehouseTakingVO } from '@/types/WarehouseWorking/WarehouseTaking'
import { removeObjectNull } from '@/utils/common'
import { TAKING_JOB_FINISH } from '@/constant/warehouseWorking'
import i18n from '@/languages/i18n'
import commoditySelect from '@/components/select/commodity-select.vue'
import tooltipBtn from '@/components/tooltip-btn.vue'
import locationSelect from '@/components/select/location-select.vue'
import skuSelect from '@/components/select/sku-select.vue'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const isUpdate = computed(() => props.form.id && props.form.id > 0)
const operateDisabled = computed(() => !!isUpdate.value)
const isFromStock = computed(() => data.curStockID > 0)const props = defineProps<{showDialog: booleanform: WarehouseTakingVO
}>()const isShow = computed(() => props.showDialog)const data = reactive({showCommodityDialogSelect: false,showLocationDialogSelect: false,showSkuDialogSelect: false,// There has value when choose from stock.curStockID: 0,form: ref<WarehouseTakingVO>({id: 0,job_code: '',job_status: TAKING_JOB_FINISH,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,book_qty: 0,counted_qty: 0,difference_qty: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: '',handler: '',handle_time: '',adjust_status: false,creator: '',create_time: ''}),rules: {job_type: [],book_qty: [],warehouse_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseTaking.warehouse') }!`],location_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseTaking.location_name') }!`],spu_code: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_code') }!`],spu_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_name') }!`],sku_code: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_code') }!`],sku_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_name') }!`]}
})const method = reactive({closeDialog: () => {emit('close')},initForm: () => {data.form = props.form},openCommoditySelect: () => {data.showCommodityDialogSelect = true},closeCommodityDialogSelect: () => {data.showCommodityDialogSelect = false},sureSelectCommodity: (selectRecords: any) => {try {data.curStockID = selectRecords[0].iddata.form.sku_id = selectRecords[0].sku_iddata.form.goods_owner_id = selectRecords[0].goods_owner_iddata.form.goods_location_id = selectRecords[0].goods_location_iddata.form.warehouse_name = selectRecords[0].warehouse_namedata.form.location_name = selectRecords[0].location_namedata.form.spu_code = selectRecords[0].spu_codedata.form.spu_name = selectRecords[0].spu_namedata.form.sku_code = selectRecords[0].sku_codedata.form.book_qty = selectRecords[0].qty_available} catch (error) {// console.error(error)}},clearCommodity: () => {data.curStockID = 0data.form.sku_id = 0data.form.goods_location_id = 0data.form.warehouse_name = ''data.form.location_name = ''data.form.spu_code = ''data.form.spu_name = ''data.form.sku_code = ''data.form.book_qty = 0},openSkuSelect: () => {data.showSkuDialogSelect = true},closeDialogSelectSku: () => {data.showSkuDialogSelect = false},sureSelectSku: (selectRecords: any) => {if (selectRecords.length > 0) {data.form.sku_id = selectRecords[0].sku_iddata.form.goods_owner_id = selectRecords[0].goods_owner_iddata.form.goods_location_id = selectRecords[0].goods_location_id// The book qty should zero when the data from commodity.data.form.book_qty = 0data.form.spu_code = selectRecords[0].spu_codedata.form.spu_name = selectRecords[0].spu_namedata.form.sku_code = selectRecords[0].sku_code}},openLocationSelect: () => {data.showLocationDialogSelect = true},closeDialogSelectLocation: () => {data.showLocationDialogSelect = false},sureSelectLocation: (selectRecords: any) => {if (selectRecords.length > 0) {data.form.goods_location_id = selectRecords[0].iddata.form.warehouse_name = selectRecords[0].warehouse_namedata.form.location_name = selectRecords[0].location_name}},clearLocation: () => {data.form.goods_location_id = 0data.form.warehouse_name = ''data.form.location_name = ''},submit: async () => {const { valid } = await formRef.value.validate()const form = method.constructFormBody()if (valid) {const { data: res } = await addStockTaking(form)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`})emit('saveSuccess')} else {hookComponent.$message({type: 'error',content: i18n.global.t('system.checkText.checkFormFail')})}},constructFormBody: () => {let form = { ...data.form }form = removeObjectNull(form)return form}
})watch(() => isShow.value,(val) => {if (val) {method.initForm()}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}.header {display: flex;margin-bottom: 15px;justify-content: space-between;
}.headerBtn {}
.headerTips {display: flex;align-items: center;
}
</style>

<!-- Warehouse Taking Number Input -->
<template><v-dialog v-model="isShow" :width="'20%'" transition="dialog-top-transition" :persistent="true"><template #default><v-card><v-toolbar color="white" :title="`${$t('router.sideBar.warehouseTaking')}`"></v-toolbar><v-card-text><p class="mb-4" style="color: #999999">{{ $t('wms.warehouseWorking.warehouseTaking.book_qty') }}:{{ data.form.book_qty }}</p><v-form ref="formRef"><v-text-fieldv-model="data.form.counted_qty":label="$t('wms.warehouseWorking.warehouseTaking.counted_qty')":rules="data.rules.counted_qty"variant="outlined"clearable></v-text-field></v-form></v-card-text><v-card-actions class="justify-end"><v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn><v-btn color="primary" variant="text" @click="method.submit">{{ $t('system.page.submit') }}</v-btn></v-card-actions></v-card></template></v-dialog>
</template><script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { hookComponent } from '@/components/system/index'
import { confirmStockTaking } from '@/api/wms/warehouseTaking'
import { WarehouseTakingVO } from '@/types/WarehouseWorking/WarehouseTaking'
import { removeObjectNull } from '@/utils/common'
import { TAKING_JOB_FINISH } from '@/constant/warehouseWorking'
import i18n from '@/languages/i18n'
import { IsInteger } from '@/utils/dataVerification/formRule'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])const props = defineProps<{showDialog: booleanform: WarehouseTakingVO
}>()const isShow = computed(() => props.showDialog)const data = reactive({showCommodityDialogSelect: false,showLocationDialogSelect: false,form: ref<WarehouseTakingVO>({id: 0,job_code: '',job_status: TAKING_JOB_FINISH,sku_id: 0,goods_owner_id: 0,goods_location_id: 0,book_qty: 0,counted_qty: 0,difference_qty: 0,spu_code: '',spu_name: '',sku_code: '',warehouse_name: '',location_name: '',adjust_status: false,handler: '',handle_time: ''}),rules: {book_qty: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseTaking.book_qty') }!`],counted_qty: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('wms.warehouseWorking.warehouseTaking.counted_qty') }!`,(val: number) => IsInteger(val, 'nonNegative') === '' || IsInteger(val, 'nonNegative')]}
})const method = reactive({closeDialog: () => {emit('close')},initForm: () => {data.form = props.form},submit: async () => {const { valid } = await formRef.value.validate()const form = method.constructFormBody()if (valid) {const { data: res } = await confirmStockTaking(form)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}hookComponent.$message({type: 'success',content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`})emit('saveSuccess')} else {hookComponent.$message({type: 'error',content: i18n.global.t('system.checkText.checkFormFail')})}},constructFormBody: () => {let form = { ...data.form }form = removeObjectNull(form)return form}
})watch(() => isShow.value,(val) => {if (val) {method.initForm()}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}
</style>

3.2 接口代码

     [Route("stocktaking")][ApiController][ApiExplorerSettings(GroupName = "WMS")]public class StocktakingController : BaseController{#region Args/// <summary>/// stocktaking Service/// </summary>private readonly IStocktakingService _stocktakingService;/// <summary>/// Localizer Service/// </summary>private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;#endregion#region constructor/// <summary>/// constructor/// </summary>/// <param name="stocktakingService">stocktaking Service</param>/// <param name="stringLocalizer">Localizer</param>public StocktakingController(IStocktakingService stocktakingService, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer){this._stocktakingService = stocktakingService;this._stringLocalizer= stringLocalizer;}#endregion#region Api/// <summary>/// page search/// </summary>/// <param name="pageSearch">args</param>/// <returns></returns>[HttpPost("list")]public async Task<ResultModel<PageData<StocktakingViewModel>>> PageAsync(PageSearch pageSearch){var (data, totals) = await _stocktakingService.PageAsync(pageSearch, CurrentUser);return ResultModel<PageData<StocktakingViewModel>>.Success(new PageData<StocktakingViewModel>{Rows = data,Totals = totals});}/// <summary>/// get a record by id/// </summary>/// <returns>args</returns>[HttpGet]public async Task<ResultModel<StocktakingViewModel>> GetAsync(int id){var data = await _stocktakingService.GetAsync(id);if (data!=null){return ResultModel<StocktakingViewModel>.Success(data);}else{return ResultModel<StocktakingViewModel>.Error(_stringLocalizer["not_exists_entity"]);}}/// <summary>/// add a new record/// </summary>/// <param name="viewModel">args</param>/// <returns></returns>[HttpPost]public async Task<ResultModel<int>> AddAsync(StocktakingBasicViewModel viewModel){ var (id, msg) = await _stocktakingService.AddAsync(viewModel,CurrentUser);if (id > 0){return ResultModel<int>.Success(id);}else{return ResultModel<int>.Error(msg);}}/// <summary>/// Confirm a record/// </summary>/// <param name="viewModel">args</param>/// <returns></returns>[HttpPut]public async Task<ResultModel<bool>> PutAsync(StocktakingConfirmViewModel viewModel){var (flag, msg) = await _stocktakingService.PutAsync(viewModel, CurrentUser);if (flag){return ResultModel<bool>.Success(flag);}else{return ResultModel<bool>.Error(msg, 400, flag);}}/// <summary>/// confrim a record and change stock and add to stockadjust/// </summary>/// <param name="id">id</param>/// <returns></returns>[HttpPut("adjustment-confirm")]public async Task<ResultModel<bool>> ConfirmAsync(int id){var (flag, msg) = await _stocktakingService.ConfirmAsync(id, CurrentUser);if (flag){return ResultModel<bool>.Success(flag);}else{return ResultModel<bool>.Error(msg, 400, flag);}}/// <summary>/// delete a record/// </summary>/// <param name="id">id</param>/// <returns></returns>[HttpDelete]public async Task<ResultModel<string>> DeleteAsync(int id){var (flag, msg) = await _stocktakingService.DeleteAsync(id);if (flag){return ResultModel<string>.Success(msg);}else{return ResultModel<string>.Error(msg);}}#endregion}

【愚公系列】2023年02月 WMS智能仓储系统-017.仓内作业(库存冻结、库存调整、库存盘点)相关推荐

  1. 【愚公系列】2023年02月 WMS智能仓储系统-011.数据库文档设计

    文章目录 前言 一.数据库文档设计 1.chm版本 2.PDF版本 3.html版本 二.数据库表目录 表结构 __EFMigrationsHistory asn asnsort category c ...

  2. 【愚公系列】2023年02月 WMS智能仓储系统-018.收获管理和发货管理(完结篇)

    文章目录 前言 一.收获管理和发货管理 1.收获管理 1.1 页面代码 1.2 接口代码 2.发货管理 1.1 页面代码 1.2 接口代码 前言 WMS智能仓储收货管理和发货管理是非常重要的两个功能 ...

  3. 【愚公系列】2023年02月 WMS智能仓储系统-016.库存管理和仓内作业(库存管理、仓内加工、库存移动)

    文章目录 前言 一.库存管理 1.1 页面代码 1.2 接口代码 二.仓内作业 1.仓内加工 1.1 页面代码 1.2 接口代码 2.库存移动 2.1 页面代码 2.2 接口代码 前言 这节主要分为两 ...

  4. 【愚公系列】2023年02月 WMS智能仓储系统-007.Swagger接口文档的配置

    文章目录 前言 一.Swagger接口文档的配置 1.安装包 2.注入 2.1 Swagger服务的注入 2.2 appsetting.json的配置 2.3 Swagger服务的封装 2.3.1 S ...

  5. 【愚公系列】2023年02月 WMS智能仓储系统-010.全局过滤、中间件、格式化配置

    文章目录 前言 一.全局过滤 1.全局行为过滤的概念 2.全局行为过滤的注册方式 2.1 方法注册 2.2 控制器注册 2.3 全局注册 2.4 TypeFilter 和 ServiceFilter ...

  6. 【愚公系列】2023年02月 WMS智能仓储系统-001.NLog日志的使用

    文章目录 前言 一.NLog日志的使用 1.安装包 2.配置文件nlog.config 3.使用 前言 系统日志是记录系统中硬件.软件和系统问题的信息,同时还可以监视系统中发生的事件.用户可以通过它来 ...

  7. 【愚公系列】2023年02月 WMS智能仓储系统-012.登录功能的实现

    文章目录 前言 1.业务流程说明 2.登录业务的相关技术点 3.登录-token原理分析 4.前端框架设计 一.登录功能的实现 1.登录页面设计 2.登录逻辑功能实现 2.1 登录逻辑页面 2.2 接 ...

  8. 【愚公系列】2023年02月 WMS智能仓储系统-015.基础设置(货主信息、运费设置、客户信息)

    文章目录 前言 一.基础设置 1.货主信息 1.1 页面代码 1.2 接口代码 2.运费设置 2.1 页面代码 2.2 接口代码 3.客户信息 3.1 页面代码 3.2 接口代码 前言 基础设置主要分 ...

  9. 【愚公系列】2023年02月 WMS智能仓储系统-006.租户功能的配置

    文章目录 前言 一.租户功能的配置 1.注入租户类 2.相关类说明 前言 多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何 ...

最新文章

  1. java中文件操作的工具类
  2. 前端面试经历(持续更新)
  3. MySQL高级 - 锁 - 锁的概述及分类
  4. Hadoop2异常分析(二):Sqoop导出数据错误
  5. jquery详细基础教程
  6. Java中FilterInputStream和FilterOutputStream的用法
  7. 等高线生成地形_等高线一键变地形模型
  8. golang 图片处理,剪切,base64数据转换,文件存储
  9. 管理感悟:软件的特性
  10. jodconverter+ pdfbox 实现 office文档转pdf(openoffice实现),并提取pdf为缩略图、预览图
  11. 37 个 Python Web 开发框架总结
  12. Python中filter筛选函数匿名参数问题
  13. win10安装免安装版的mysql5.7
  14. 靶机渗透之Typhoon实战
  15. 星期五五–大数据,Doppio和假Linus Torvalds
  16. 地图SDK全面升级 – 数十项新功能及优化等你来体验
  17. Xtend调用Scilab
  18. ProGrade Digital宣布推出SDXC UHS-II V90存储卡
  19. iOS 应用上架流程(提交到AppStore)
  20. Ps中的钢笔工具和快速选择工具

热门文章

  1. 计算机毕业设计题目参考以及如何选题附项目源码
  2. gitkraken汉化
  3. html图片遮住字了,css 内容为何被遮挡住了?
  4. 4.3 概率判别式模型(PRML读书笔记)
  5. 笔记本电脑的选购之一(2011年10月)
  6. 老婆最近陷入苦恼,连续几个晚上都没睡好
  7. Java中BorderLayout布局管理器的两种排列实现方式
  8. php攻克技术难点,攻克的七大技术难题
  9. 投稿时论文Highlights的写法
  10. Centos8 linux安装oracle 19c rpm