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


前言

这节主要分为两个模块:

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

在这里插入图片描述

一、仓内作业

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-btn
                      icon="mdi-lock-open-outline"
                      :tooltip-text="$t('wms.warehouseWorking.warehouseFreeze.freeze')"
                      @click="method.add(FREEZE_JOB_FREEZE)"
                    ></tooltip-btn>
                    <tooltip-btn
                      icon="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-field
                          v-model="data.searchForm.job_code"
                          clearable
                          hide-details
                          density="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 -->
              <div
                class="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-column
                    field="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 add
  add: (jobType: boolean) => {
    data.freezeType = jobType
    data.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 data
  refresh: () => {
    method.getStockProcessList()
  },

  // Shut add or update dialog
  closeDialog: () => {
    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.rows
    data.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 = currentPage
    data.tablePage.pageSize = pageSize
    method.refresh()
  }),

  exportTable: () => {
    const $table = xTable.value
    exportData({
      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,
  () => {
    // debounce
    if (data.timer) {
      clearTimeout(data.timer)
    }
    data.timer = setTimeout(() => {
      data.timer = null
      method.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-field
              v-model="data.form.spu_code"
              :label="$t('base.commodityManagement.spu_code')"
              :rules="data.rules.spu_code"
              variant="outlined"
              readonly
              clearable
              @click="method.openCommoditySelect"
              @click:clear="method.clearCommodity"
            ></v-text-field>
            <v-text-field
              v-model="data.form.spu_name"
              :label="$t('base.commodityManagement.spu_name')"
              :rules="data.rules.spu_name"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-model="data.form.sku_code"
              :label="$t('base.commodityManagement.sku_code')"
              :rules="data.rules.sku_code"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-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-field
              v-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: boolean
  form: WarehouseFreezeVO
  freezeType: 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.form
    data.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_id
      data.form.spu_code = selectRecords[0].spu_code
      data.form.spu_name = selectRecords[0].spu_name
      data.form.sku_code = selectRecords[0].sku_code

      data.form.goods_owner_id = selectRecords[0].goods_owner_id
      data.form.goods_location_id = selectRecords[0].goods_location_id
      data.form.warehouse_name = selectRecords[0].warehouse_name
      data.form.location_name = selectRecords[0].location_name
    } catch (error) {
      console.error(error)
    }
  },

  clearCommodity: () => {
    data.form.sku_id = 0
    data.form.spu_code = ''
    data.form.spu_name = ''
    data.form.sku_code = ''
  },

  clearLocation: () => {
    data.form.goods_location_id = 0
    data.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-field
                          v-model="data.searchForm.job_code"
                          clearable
                          hide-details
                          density="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 -->
              <div
                class="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-column
                    field="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 add
  add: () => {
    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 data
  refresh: () => {
    method.getStockProcessList()
  },

  // Shut add or update dialog
  closeDialog: () => {
    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.rows
    data.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 = currentPage
    data.tablePage.pageSize = pageSize
    method.refresh()
  }),

  exportTable: () => {
    const $table = xTable.value
    exportData({
      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,
  () => {
    // debounce
    if (data.timer) {
      clearTimeout(data.timer)
    }
    data.timer = setTimeout(() => {
      data.timer = null
      method.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-select
              v-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-field
              v-model="data.form.spu_code"
              :label="$t('base.commodityManagement.spu_code')"
              :rules="data.rules.spu_code"
              variant="outlined"
              readonly
              clearable
              :disabled="isAssociatedJobType"
              @click="method.openCommoditySelect"
              @click:clear="method.clearCommodity"
            ></v-text-field>
            <v-text-field
              v-model="data.form.spu_name"
              :label="$t('base.commodityManagement.spu_name')"
              :rules="data.rules.spu_name"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-model="data.form.sku_code"
              :label="$t('base.commodityManagement.sku_code')"
              :rules="data.rules.sku_code"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-model="data.form.warehouse_name"
              :label="$t('wms.warehouseWorking.warehouseAdjust.warehouse')"
              :rules="data.rules.warehouse_name"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-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-field
              v-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: boolean
  form: 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: string
      value: 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.form

    if (isAssociatedJobType.value) {
      data.form.job_type = props.associatedJobType
    }
  },

  changeJobType: (value: any) => {
    // Find the ID corresponding value
    const 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_id
      data.form.goods_owner_id = selectRecords[0].goods_owner_id
      data.form.goods_location_id = selectRecords[0].goods_location_id
      data.form.warehouse_name = selectRecords[0].warehouse
      data.form.location_name = selectRecords[0].location_name
      data.form.spu_code = selectRecords[0].spu_code
      data.form.spu_name = selectRecords[0].spu_name
      data.form.sku_code = selectRecords[0].sku_code
    } catch (error) {
      // console.error(error)
    }
  },

  clearCommodity: () => {
    data.form.sku_id = 0
    data.form.goods_location_id = 0
    data.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-field
                          v-model="data.searchForm.job_code"
                          clearable
                          hide-details
                          density="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 -->
              <div
                class="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 add
  add: () => {
    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 data
  refresh: () => {
    method.getStockProcessList()
  },

  // Shut add or update dialog
  closeDialog: () => {
    data.showDialog = false
  },

  // Shut number input dialog
  closeDialogNumberInput: () => {
    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.rows
    data.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 = currentPage
    data.tablePage.pageSize = pageSize
    method.refresh()
  }),

  exportTable: () => {
    const $table = xTable.value
    exportData({
      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 false
  confirmAdjustBtnDisabled: (row: WarehouseTakingVO) => row.job_status === TAKING_JOB_UNFINISH || !!row.adjust_status,

  // The btn will become disabled when the 'job_status' is true
  isConfirmTaking: (row: WarehouseTakingVO) => row.job_status === TAKING_JOB_FINISH,

  confirmTaking: async (row: WarehouseTakingVO) => {
    data.dialogForm = row
    nextTick(() => {
      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,
  () => {
    // debounce
    if (data.timer) {
      clearTimeout(data.timer)
    }
    data.timer = setTimeout(() => {
      data.timer = null
      method.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-btn
                size="x-small"
                icon="mdi-home-plus-outline"
                :tooltip-text="$t('wms.warehouseWorking.warehouseTaking.addFromStock')"
                @click="method.openCommoditySelect()"
              ></tooltip-btn>
              <tooltip-btn
                size="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-field
              v-model="data.form.spu_code"
              :label="$t('base.commodityManagement.spu_code')"
              :rules="data.rules.spu_code"
              variant="outlined"
              readonly
              clearable
              @click:clear="method.clearCommodity"
            ></v-text-field>
            <v-text-field
              v-model="data.form.spu_name"
              :label="$t('base.commodityManagement.spu_name')"
              :rules="data.rules.spu_name"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-model="data.form.sku_code"
              :label="$t('base.commodityManagement.sku_code')"
              :rules="data.rules.sku_code"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-model="data.form.warehouse_name"
              :label="$t('wms.warehouseWorking.warehouseTaking.warehouse')"
              :rules="data.rules.warehouse_name"
              variant="outlined"
              disabled
            ></v-text-field>
            <v-text-field
              v-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-field
              v-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: boolean
  form: 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].id
      data.form.sku_id = selectRecords[0].sku_id
      data.form.goods_owner_id = selectRecords[0].goods_owner_id
      data.form.goods_location_id = selectRecords[0].goods_location_id
      data.form.warehouse_name = selectRecords[0].warehouse_name
      data.form.location_name = selectRecords[0].location_name
      data.form.spu_code = selectRecords[0].spu_code
      data.form.spu_name = selectRecords[0].spu_name
      data.form.sku_code = selectRecords[0].sku_code
      data.form.book_qty = selectRecords[0].qty_available
    } catch (error) {
      // console.error(error)
    }
  },

  clearCommodity: () => {
    data.curStockID = 0
    data.form.sku_id = 0
    data.form.goods_location_id = 0
    data.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_id
      data.form.goods_owner_id = selectRecords[0].goods_owner_id
      data.form.goods_location_id = selectRecords[0].goods_location_id
      // The book qty should zero when the data from commodity.
      data.form.book_qty = 0
      data.form.spu_code = selectRecords[0].spu_code
      data.form.spu_name = selectRecords[0].spu_name
      data.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].id
      data.form.warehouse_name = selectRecords[0].warehouse_name
      data.form.location_name = selectRecords[0].location_name
    }
  },

  clearLocation: () => {
    data.form.goods_location_id = 0
    data.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-field
              v-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: boolean
  form: 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
 
     }

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚公搬代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值