前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【愚公系列】2023年02月 WMS智能仓储系统-015.基础设置(商品管理、供应商信息、仓库设置)

【愚公系列】2023年02月 WMS智能仓储系统-015.基础设置(商品管理、供应商信息、仓库设置)

作者头像
愚公搬代码
发布2023-03-16 17:19:53
1.2K0
发布2023-03-16 17:19:53
举报
文章被收录于专栏:历史专栏

文章目录


前言

基础设置主要分为以下几个模块:

  • 首页
  • 公司信息
  • 角色设置
  • 菜单设置
  • 用户管理
  • 商品类别设置
  • 商品管理
  • 供应商信息
  • 仓库设置
  • 货主信息
  • 运费设置
  • 客户信息
在这里插入图片描述
在这里插入图片描述

对于商品就要说下SPU和 SKU

  • SPU:SPU(Standard Product Unit):标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。比如:iPhone XS
  • SKU:SKU(Stock Keeping Unit)库存量单位,即库存进出计量的单位, 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。比如:iPhone XS 金色 64g

一、基础设置

1.商品管理

在这里插入图片描述
在这里插入图片描述

1.1 页面代码

1、主页面代码

代码语言:javascript
复制
<template>
  <div class="container">
    <div>
      <!-- Main Content -->
      <v-card class="mt-5">
        <v-card-text>
          <div class="operateArea">
            <v-row no-gutters>
              <!-- Operate Btn -->
              <v-col cols="12" sm="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="12" sm="9">
                <v-row no-gutters @keyup.enter="method.sureSearch">
                  <v-col cols="4">
                    <v-text-field
                      v-model="data.searchForm.spu_code"
                      clearable
                      hide-details
                      density="comfortable"
                      class="searchInput ml-5 mt-1"
                      :label="$t('base.commodityManagement.spu_code')"
                      variant="solo"
                    >
                    </v-text-field>
                  </v-col>
                  <v-col cols="4">
                    <v-text-field
                      v-model="data.searchForm.spu_name"
                      clearable
                      hide-details
                      density="comfortable"
                      class="searchInput ml-5 mt-1"
                      :label="$t('base.commodityManagement.spu_name')"
                      variant="solo"
                    >
                    </v-text-field>
                  </v-col>
                  <v-col cols="4">
                    <v-text-field
                      v-model="data.searchForm.category_name"
                      clearable
                      hide-details
                      density="comfortable"
                      class="searchInput ml-5 mt-1"
                      :label="$t('base.commodityManagement.category_name')"
                      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"
              :data="data.tableData"
              :column-config="{
                minWidth: '100px'
              }"
              :height="tableHeight"
              align="center"
              :tree-config="data.tableTreeConfig"
            >
              <template #empty>
                {{ i18n.global.t('system.page.noData') }}
              </template>
              <vxe-column type="seq" width="60"></vxe-column>
              <vxe-column field="spu_code" width="150px" :title="$t('base.commodityManagement.spu_code')" tree-node>
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ row.sku_code }}</span>
                  <span v-else>{{ row.spu_code }}</span>
                </template>
              </vxe-column>
              <vxe-column field="spu_name" :title="$t('base.commodityManagement.spu_name')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ row.sku_name }}</span>
                  <span v-else>{{ row.spu_name }}</span>
                </template>
              </vxe-column>
              <vxe-column field="category_name" :title="$t('base.commodityManagement.category_name')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ row.unit }}</span>
                  <span v-else>{{ row.category_name }}</span>
                </template>
              </vxe-column>
              <vxe-column field="spu_description" :title="$t('base.commodityManagement.spu_description')"> </vxe-column>
              <vxe-column field="bar_code" :title="$t('base.commodityManagement.bar_code')"> </vxe-column>
              <vxe-column field="supplier_name" :title="$t('base.commodityManagement.supplier_name')"> </vxe-column>
              <vxe-column field="brand" :title="$t('base.commodityManagement.brand')"> </vxe-column>

              <vxe-column field="weight" :title="$t('base.commodityManagement.weight')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ `${row.weight} ${GetUnit('weight', row.weight_unit)}` }}</span>
                </template>
              </vxe-column>
              <vxe-column field="lenght" :title="$t('base.commodityManagement.lenght')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ `${row.lenght} ${GetUnit('length', row.length_unit)}` }}</span>
                </template>
              </vxe-column>
              <vxe-column field="width" :title="$t('base.commodityManagement.width')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ `${row.width} ${GetUnit('length', row.length_unit)}` }}</span>
                </template>
              </vxe-column>
              <vxe-column field="height" :title="$t('base.commodityManagement.height')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ `${row.height} ${GetUnit('length', row.length_unit)}` }}</span>
                </template>
              </vxe-column>
              <vxe-column field="volume" :title="$t('base.commodityManagement.volume')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ `${row.volume} ${GetUnit('volume', row.volume_unit)}` }}</span>
                </template>
              </vxe-column>

              <vxe-column field="cost" :title="$t('base.commodityManagement.cost')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ row.cost }}</span>
                </template>
              </vxe-column>
              <vxe-column field="price" :title="$t('base.commodityManagement.price')">
                <template #default="{ row }">
                  <span v-if="row.parent_id > 0">{{ row.price }}</span>
                </template>
              </vxe-column>
              <!-- <vxe-column field="sku_name" :title="$t('base.commodityManagement.sku_name')"></vxe-column> -->
              <!-- <vxe-column field="supplier_name" :title="$t('base.commodityManagement.supplier_name')"></vxe-column>
              <vxe-column field="brand" :title="$t('base.commodityManagement.brand')"></vxe-column>
              <vxe-column field="unit" :title="$t('base.commodityManagement.unit')"></vxe-column>
              <vxe-column field="cost" :title="$t('base.commodityManagement.cost')"></vxe-column> -->
              <vxe-column field="operate" :title="$t('system.page.operate')" width="160" :resizable="false" show-overflow>
                <template #default="{ row }">
                  <div v-if="!row.parent_id || row.parent_id <= 0">
                    <tooltip-btn
                      :flat="true"
                      icon="mdi-pencil-outline"
                      :tooltip-text="$t('system.page.edit')"
                      @click="method.editRow(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>
                  </div>
                </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-card-text>
      </v-card>
    </div>
    <!-- Add or modify data mode window -->
    <addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" />
  </div>
</template>

<script lang="ts" setup>
import { computed, reactive, onMounted, ref, watch } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { CommodityVO, DataProps } from '@/types/Base/CommodityManagement'
import { getSpuList, deleteSpu } from '@/api/base/commodityManagementSetting'
import { hookComponent } from '@/components/system'
import addOrUpdateDialog from './add-or-update-commodity.vue'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import i18n from '@/languages/i18n'
import { GetUnit } from '@/constant/commodityManagement'
import customPager from '@/components/custom-pager.vue'
import { setSearchObject } from '@/utils/common'
import { exportData } from '@/utils/exportTable'
import { DEBOUNCE_TIME } from '@/constant/system'

const xTable = ref()

const data: DataProps = reactive({
  searchForm: {
    spu_code: '',
    spu_name: '',
    category_name: ''
  },
  timer: null,
  tableData: [],
  tablePage: {
    total: 0,
    pageIndex: 1,
    pageSize: DEFAULT_PAGE_SIZE
  },
  tableTreeConfig: {
    transform: true,
    rowField: 'tree_id',
    parentField: 'parent_id'
  },
  // Dialog info
  showDialog: false,
  dialogForm: {
    id: 0,
    spu_code: '',
    spu_name: '',
    category_id: 0,
    category_name: '',
    spu_description: '',
    bar_code: '',
    supplier_id: 0,
    supplier_name: '',
    brand: '',
    origin: '',
    length_unit: 1,
    volume_unit: 0,
    weight_unit: 1,
    detailList: []
  }
})

const method = reactive({
  sureSearch: () => {
    data.tablePage.searchObjects = setSearchObject(data.searchForm)
    method.getCompanyList()
  },
  // Find Data by Pagination
  getCompanyList: async () => {
    const { data: res } = await getSpuList(data.tablePage)
    if (!res.isSuccess) {
      hookComponent.$message({
        type: 'error',
        content: res.errorMessage
      })
      return
    }
    data.tableData = []
    for (const item of res.data.rows) {
      item.tree_id = item.id
      data.tableData.push(item)
      for (const ditem of item.detailList) {
        ditem.tree_id = 0
        ditem.parent_id = item.id
        ditem.length_unit = item.length_unit
        ditem.volume_unit = item.volume_unit
        ditem.weight_unit = item.weight_unit
        data.tableData.push(ditem)
      }
    }
    data.tablePage.total = res.data.totals
  },
  // Add user
  add: () => {
    data.dialogForm = {
      id: 0,
      spu_code: '',
      spu_name: '',
      spu_description: '',
      bar_code: '',
      brand: '',
      length_unit: 1,
      volume_unit: 0,
      weight_unit: 1,
      detailList: []
    }
    data.showDialog = true
  },
  // Shut add or update dialog
  closeDialog: () => {
    data.showDialog = false
  },
  // after Add or update success.
  saveSuccess: () => {
    method.refresh()
    method.closeDialog()
  },
  // Refresh data
  refresh: () => {
    method.getCompanyList()
  },
  editRow(row: CommodityVO) {
    data.dialogForm = JSON.parse(JSON.stringify(row))
    data.showDialog = true
  },
  deleteRow(row: CommodityVO) {
    hookComponent.$dialog({
      content: i18n.global.t('system.tips.beforeDeleteMessage'),
      handleConfirm: async () => {
        if (row.id) {
          const { data: res } = await deleteSpu(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()
        }
      }
    })
  },
  // Export table
  exportTable: () => {
    const $table = xTable.value
    exportData({
      table: $table,
      filename: i18n.global.t('router.sideBar.commodityManagement'),
      columnFilterMethod({ column }: any) {
        return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)
      }
    })
  },
  // When change paging
  handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {
    data.tablePage.pageIndex = currentPage
    data.tablePage.pageSize = pageSize
    method.refresh()
  })
})

onMounted(async () => {
  await method.getCompanyList()
})

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>
在这里插入图片描述
在这里插入图片描述

2、窗体代码

代码语言:javascript
复制
<template>
  <v-dialog v-model="isShow" width="70%" transition="dialog-top-transition" :persistent="true">
    <template #default>
      <v-card>
        <v-toolbar color="white" :title="`${$t('router.sideBar.commodityManagement')}`"></v-toolbar>
        <v-card-text>
          <v-row>
            <v-col cols="3">
              <div class="mainForm" :style="{ height: mainFormHeight }">
                <v-form ref="formRef">
                  <v-text-field
                    v-model="data.form.spu_code"
                    :rules="data.rules.spu_code"
                    :label="$t('base.commodityManagement.spu_code')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-text-field
                    v-model="data.form.spu_name"
                    :rules="data.rules.spu_name"
                    :label="$t('base.commodityManagement.spu_name')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-select
                    v-model="data.form.category_name"
                    :items="data.combobox.category_name"
                    item-title="label"
                    item-value="label"
                    :rules="data.rules.category_name"
                    :label="$t('base.commodityManagement.category_name')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                    @update:model-value="method.categoryNameChange"
                  ></v-select>
                  <v-text-field
                    v-model="data.form.spu_description"
                    :rules="data.rules.spu_description"
                    :label="$t('base.commodityManagement.spu_description')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-text-field
                    v-model="data.form.bar_code"
                    :rules="data.rules.bar_code"
                    :label="$t('base.commodityManagement.bar_code')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-select
                    v-model="data.form.supplier_name"
                    :items="data.combobox.supplier_name"
                    :rules="data.rules.supplier_name"
                    item-title="label"
                    item-value="label"
                    :label="$t('base.commodityManagement.supplier_name')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                    @update:model-value="method.supplierNameChange"
                  ></v-select>
                  <v-text-field
                    v-model="data.form.brand"
                    :rules="data.rules.brand"
                    :label="$t('base.commodityManagement.brand')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-select
                    v-model="data.form.length_unit"
                    :items="data.combobox.length_unit"
                    item-title="label"
                    item-value="value"
                    :rules="data.rules.length_unit"
                    :label="$t('base.commodityManagement.length_unit')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                  ></v-select>
                  <v-select
                    v-model="data.form.volume_unit"
                    :items="data.combobox.volume_unit"
                    item-title="label"
                    item-value="value"
                    :rules="data.rules.volume_unit"
                    :label="$t('base.commodityManagement.volume_unit')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                  ></v-select>
                  <v-select
                    v-model="data.form.weight_unit"
                    :items="data.combobox.weight_unit"
                    item-title="label"
                    item-value="value"
                    :rules="data.rules.weight_unit"
                    :label="$t('base.commodityManagement.weight_unit')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                  ></v-select>
                </v-form>
              </div>
            </v-col>
            <v-col cols="9">
              <div class="dataTable">
                <div class="toolbar">
                  <tooltip-btn
                    icon="mdi-plus"
                    :tooltip-text="$t('system.page.insertOneRow')"
                    size="x-small"
                    @click="method.insertOneRow"
                  ></tooltip-btn>
                  <tooltip-btn
                    icon="mdi-export-variant"
                    :tooltip-text="$t('system.page.export')"
                    size="x-small"
                    @click="method.exportTable"
                  ></tooltip-btn>
                </div>
                <vxe-table
                  ref="xTable"
                  keep-source
                  :column-config="{ minWidth: '100px' }"
                  :data="data.form.detailList"
                  :height="SYSTEM_HEIGHT.SELECT_TABLE"
                  align="center"
                  :edit-rules="data.validRules"
                  :edit-config="{ trigger: 'click', mode: 'cell' }"
                  :mouse-config="{ selected: true }"
                  :keyboard-config="{ isArrow: true, isDel: true, isEnter: true, isTab: true, isEdit: true, isChecked: true }"
                >
                  <template #empty>
                    {{ i18n.global.t('system.page.noData') }}
                  </template>
                  <vxe-column type="seq" width="60"></vxe-column>
                  <vxe-column field="sku_code" :title="$t('base.commodityManagement.sku_code')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.sku_code" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="sku_name" :title="$t('base.commodityManagement.sku_name')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.sku_name" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <!-- <vxe-column field="supplier_name" :title="$t('base.commodityManagement.supplier_name')"></vxe-column> -->
                  <!-- <vxe-column field="brand" :title="$t('base.commodityManagement.brand')"></vxe-column> -->
                  <vxe-column field="unit" :title="$t('base.commodityManagement.unit')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.unit" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="weight" :title="$t('base.commodityManagement.weight')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.weight" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="lenght" :title="$t('base.commodityManagement.lenght')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.lenght" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="width" :title="$t('base.commodityManagement.width')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.width" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="height" :title="$t('base.commodityManagement.height')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.height" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="volume" :title="$t('base.commodityManagement.volume')"> </vxe-column>
                  <vxe-column field="cost" :title="$t('base.commodityManagement.cost')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.cost" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="price" :title="$t('base.commodityManagement.price')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.price" type="text"></vxe-input>
                    </template>
                  </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-pencil-outline"
                        :tooltip-text="$t('system.page.edit')"
                        @click="method.editRow(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>
              </div>
            </v-col>
          </v-row>
        </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 { CommodityVO, CommodityDetailVO } from '@/types/Base/CommodityManagement'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addSpu, updateSpu } from '@/api/base/commodityManagementSetting'
import { getCategoryAll } from '@/api/base/commodityCategorySetting'
import { getSupplierAll } from '@/api/base/supplier'
import { CategoryVO } from '@/types/Base/CommodityCategorySetting'
import { SupplierVO } from '@/types/Base/Supplier'
import { computedSelectTableSearchHeight, SYSTEM_HEIGHT, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { removeArrayNull } from '@/utils/common'
import { StringLength } from '@/utils/dataVerification/formRule'
import { isDecimal } from '@/utils/dataVerification/tableRule'
import { exportData } from '@/utils/exportTable'

const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const xTable = ref()

const props = defineProps<{
  showDialog: boolean
  form: CommodityVO
}>()

const isShow = computed(() => props.showDialog)

const dialogTitle = computed(() => {
  if (props.form.id && props.form.id > 0) {
    return 'update'
  }
  return 'add'
})

const data = reactive({
  form: ref<CommodityVO>({
    id: 0,
    spu_code: '',
    spu_name: '',
    category_id: 0,
    category_name: '',
    spu_description: '',
    bar_code: '',
    supplier_id: 0,
    supplier_name: '',
    brand: '',
    origin: '',
    length_unit: 1,
    volume_unit: 0,
    weight_unit: 1,
    detailList: []
  }),
  tableData: [],
  rules: {
    spu_code: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_code') }!`,
      (val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
    ],
    spu_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_name') }!`,
      (val: string) => StringLength(val, 0, 200) === '' || StringLength(val, 0, 200)
    ],
    spu_description: [(val: string) => StringLength(val, 0, 1000) === '' || StringLength(val, 0, 1000)],
    bar_code: [(val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)],
    brand: [(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)],
    category_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.category_name') }!`
    ],
    length_unit: [
      (val: number) => [0, 1, 2, 3].includes(val) || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.length_unit') }!`
    ],
    volume_unit: [
      (val: number) => [0, 1, 2].includes(val) || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.volume_unit') }!`
    ],
    weight_unit: [
      (val: number) => [0, 1, 2].includes(val) || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.weight_unit') }!`
    ],
    supplier_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.supplier_name') }!`
    ]
  },
  validRules: ref<any>({
    sku_code: [
      { required: true, message: `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_code') }` },
      {
        type: 'string',
        min: 0,
        max: 32,
        message: `${ i18n.global.t('system.checkText.lengthValid') }${ 0 }-${ 32 }`,
        trigger: 'change'
      }
    ],
    sku_name: [
      { required: true, message: `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_name') }` },
      {
        type: 'string',
        min: 0,
        max: 200,
        message: `${ i18n.global.t('system.checkText.lengthValid') }${ 0 }-${ 200 }`,
        trigger: 'change'
      }
    ],
    unit: [
      { required: true, message: `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.unit') }` },
      {
        type: 'string',
        min: 0,
        max: 5,
        message: `${ i18n.global.t('system.checkText.lengthValid') }${ 0 }-${ 5 }`,
        trigger: 'change'
      }
    ],
    weight: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    lenght: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    width: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    height: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    volume: [
      // {
      //   validator: isDecimal,
      //   validNumerical: 'nonNegative',
      //   length: 8,
      //   decimalLength: 3,
      //   trigger: 'change'
      // }
    ],
    cost: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 10,
        decimalLength: 2,
        trigger: 'change'
      }
    ],
    price: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 10,
        decimalLength: 2,
        trigger: 'change'
      }
    ]
  }),
  combobox: ref<{
    length_unit: {
      label: string
      value: number
    }[]
    volume_unit: {
      label: string
      value: number
    }[]
    weight_unit: {
      label: string
      value: number
    }[]
    category_name: {
      label: string
      value: number
    }[]
    supplier_name: {
      label: string
      value: number
    }[]
  }>({
    length_unit: [
      {
        label: 'mm',
        value: 0
      },
      {
        label: 'cm',
        value: 1
      },
      {
        label: 'dm',
        value: 2
      },
      {
        label: 'm',
        value: 3
      }
    ],
    volume_unit: [
      {
        label: 'cm³',
        value: 0
      },
      {
        label: 'dm³',
        value: 1
      },
      {
        label: 'm³',
        value: 2
      }
    ],
    weight_unit: [
      {
        label: 'mg',
        value: 0
      },
      {
        label: 'g',
        value: 1
      },
      {
        label: 'kg',
        value: 2
      }
    ],
    category_name: [],
    supplier_name: []
  })
})

const method = reactive({
  // When the commodity type changes, it is mainly used to assign the ID
  categoryNameChange: (val: string) => {
    if (!val) {
      data.form.category_id = 0
    } else {
      data.form.category_id = data.combobox.category_name.filter((item) => item.label === val)[0].value
    }
  },
  supplierNameChange: (val: string) => {
    if (!val) {
      data.form.supplier_id = 0
    } else {
      data.form.supplier_id = data.combobox.supplier_name.filter((item) => item.label === val)[0].value
    }
  },
  // Get the options required by the drop-down box
  getCombobox: async () => {
    data.combobox.category_name = []
    data.combobox.supplier_name = []
    const { data: res } = await getCategoryAll()
    if (!res.isSuccess) {
      hookComponent.$message({
        type: 'error',
        content: res.errorMessage
      })
      return
    }
    data.combobox.category_name = res.data
      .filter((item: CategoryVO) => item.is_valid)
      .map((item: CategoryVO) => ({ value: item.id, label: item.category_name }))
    // Get supplier information
    const { data: supplierRes } = await getSupplierAll()
    if (!supplierRes.isSuccess) {
      hookComponent.$message({
        type: 'error',
        content: supplierRes.errorMessage
      })
      return
    }
    data.combobox.supplier_name = supplierRes.data
      .filter((item: SupplierVO) => item.is_valid)
      .map((item: SupplierVO) => ({ value: item.id, label: item.supplier_name }))
  },
  closeDialog: () => {
    emit('close')
  },
  insertOneRow: () => {
    xTable.value.insertAt(-1)
  },
  // Export table
  exportTable: () => {
    const $table = xTable.value
    exportData({
      table: $table,
      filename: i18n.global.t('router.sideBar.commodityManagement'),
      columnFilterMethod({ column }: any) {
        return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)
      }
    })
  },
  submit: async () => {
    const $table = xTable.value
    const errMap = await $table.validate(true)
    const { valid } = await formRef.value.validate()
    if (valid && !errMap) {
      if ($table.getTableData().fullData.length === 0) {
        hookComponent.$message({
          type: 'error',
          content: i18n.global.t('system.tips.detailLengthIsZero')
        })
        return
      }
      const form = { ...data.form }
      const insertRecords = $table.getInsertRecords()
      form.detailList = []
      // Processing detailed data
      if (dialogTitle.value === 'add') {
        form.detailList = [...insertRecords]
      } else {
        const updateRecords = $table.getUpdateRecords()
        const removeRecords = $table.getRemoveRecords()
        form.detailList = [...insertRecords, ...updateRecords]
        for (const item of removeRecords) {
          item.id = item.id > 0 ? 0 - item.id : item.id
          form.detailList.push(item)
        }
      }
      form.detailList = removeArrayNull(form.detailList)
      const { data: res } = dialogTitle.value === 'add' ? await addSpu(form) : await updateSpu(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')
      })
    }
  },
  editRow: (row: CommodityDetailVO) => {
    const $table = xTable.value
    $table.setEditRow(row)
  },
  deleteRow: (row: CommodityDetailVO) => {
    const $table = xTable.value
    hookComponent.$dialog({
      content: i18n.global.t('system.tips.beforeDeleteDetailMessage'),
      handleConfirm: async () => {
        $table.remove(row)
      }
    })
  }
})

const mainFormHeight = computed(() => computedSelectTableSearchHeight({ hasPager: false, hasToolBar: true }))

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      method.getCombobox()
      data.form = props.form
    }
  }
)
</script>

<style scoped lang="less">
.mainForm {
  background-color: #f9f9f9;
  border-radius: 5px;
  padding: 20px;
  box-sizing: border-box;
  overflow: auto;
}

.toolbar {
  height: 40px;
}
</style>
在这里插入图片描述
在这里插入图片描述

1.2 接口代码

代码语言:javascript
复制
<template>
  <v-dialog v-model="isShow" width="70%" transition="dialog-top-transition" :persistent="true">
    <template #default>
      <v-card>
        <v-toolbar color="white" :title="`${$t('router.sideBar.commodityManagement')}`"></v-toolbar>
        <v-card-text>
          <v-row>
            <v-col cols="3">
              <div class="mainForm" :style="{ height: mainFormHeight }">
                <v-form ref="formRef">
                  <v-text-field
                    v-model="data.form.spu_code"
                    :rules="data.rules.spu_code"
                    :label="$t('base.commodityManagement.spu_code')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-text-field
                    v-model="data.form.spu_name"
                    :rules="data.rules.spu_name"
                    :label="$t('base.commodityManagement.spu_name')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-select
                    v-model="data.form.category_name"
                    :items="data.combobox.category_name"
                    item-title="label"
                    item-value="label"
                    :rules="data.rules.category_name"
                    :label="$t('base.commodityManagement.category_name')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                    @update:model-value="method.categoryNameChange"
                  ></v-select>
                  <v-text-field
                    v-model="data.form.spu_description"
                    :rules="data.rules.spu_description"
                    :label="$t('base.commodityManagement.spu_description')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-text-field
                    v-model="data.form.bar_code"
                    :rules="data.rules.bar_code"
                    :label="$t('base.commodityManagement.bar_code')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-select
                    v-model="data.form.supplier_name"
                    :items="data.combobox.supplier_name"
                    :rules="data.rules.supplier_name"
                    item-title="label"
                    item-value="label"
                    :label="$t('base.commodityManagement.supplier_name')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                    @update:model-value="method.supplierNameChange"
                  ></v-select>
                  <v-text-field
                    v-model="data.form.brand"
                    :rules="data.rules.brand"
                    :label="$t('base.commodityManagement.brand')"
                    variant="outlined"
                    density="compact"
                    clearable
                    class="mb-4"
                  ></v-text-field>
                  <v-select
                    v-model="data.form.length_unit"
                    :items="data.combobox.length_unit"
                    item-title="label"
                    item-value="value"
                    :rules="data.rules.length_unit"
                    :label="$t('base.commodityManagement.length_unit')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                  ></v-select>
                  <v-select
                    v-model="data.form.volume_unit"
                    :items="data.combobox.volume_unit"
                    item-title="label"
                    item-value="value"
                    :rules="data.rules.volume_unit"
                    :label="$t('base.commodityManagement.volume_unit')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                  ></v-select>
                  <v-select
                    v-model="data.form.weight_unit"
                    :items="data.combobox.weight_unit"
                    item-title="label"
                    item-value="value"
                    :rules="data.rules.weight_unit"
                    :label="$t('base.commodityManagement.weight_unit')"
                    variant="outlined"
                    density="compact"
                    class="mb-4"
                    clearable
                  ></v-select>
                </v-form>
              </div>
            </v-col>
            <v-col cols="9">
              <div class="dataTable">
                <div class="toolbar">
                  <tooltip-btn
                    icon="mdi-plus"
                    :tooltip-text="$t('system.page.insertOneRow')"
                    size="x-small"
                    @click="method.insertOneRow"
                  ></tooltip-btn>
                  <tooltip-btn
                    icon="mdi-export-variant"
                    :tooltip-text="$t('system.page.export')"
                    size="x-small"
                    @click="method.exportTable"
                  ></tooltip-btn>
                </div>
                <vxe-table
                  ref="xTable"
                  keep-source
                  :column-config="{ minWidth: '100px' }"
                  :data="data.form.detailList"
                  :height="SYSTEM_HEIGHT.SELECT_TABLE"
                  align="center"
                  :edit-rules="data.validRules"
                  :edit-config="{ trigger: 'click', mode: 'cell' }"
                  :mouse-config="{ selected: true }"
                  :keyboard-config="{ isArrow: true, isDel: true, isEnter: true, isTab: true, isEdit: true, isChecked: true }"
                >
                  <template #empty>
                    {{ i18n.global.t('system.page.noData') }}
                  </template>
                  <vxe-column type="seq" width="60"></vxe-column>
                  <vxe-column field="sku_code" :title="$t('base.commodityManagement.sku_code')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.sku_code" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="sku_name" :title="$t('base.commodityManagement.sku_name')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.sku_name" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <!-- <vxe-column field="supplier_name" :title="$t('base.commodityManagement.supplier_name')"></vxe-column> -->
                  <!-- <vxe-column field="brand" :title="$t('base.commodityManagement.brand')"></vxe-column> -->
                  <vxe-column field="unit" :title="$t('base.commodityManagement.unit')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.unit" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="weight" :title="$t('base.commodityManagement.weight')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.weight" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="lenght" :title="$t('base.commodityManagement.lenght')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.lenght" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="width" :title="$t('base.commodityManagement.width')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.width" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="height" :title="$t('base.commodityManagement.height')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.height" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="volume" :title="$t('base.commodityManagement.volume')"> </vxe-column>
                  <vxe-column field="cost" :title="$t('base.commodityManagement.cost')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.cost" type="text"></vxe-input>
                    </template>
                  </vxe-column>
                  <vxe-column field="price" :title="$t('base.commodityManagement.price')" :edit-render="{ autofocus: '.vxe-input--inner' }">
                    <template #edit="{ row }">
                      <vxe-input v-model="row.price" type="text"></vxe-input>
                    </template>
                  </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-pencil-outline"
                        :tooltip-text="$t('system.page.edit')"
                        @click="method.editRow(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>
              </div>
            </v-col>
          </v-row>
        </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 { CommodityVO, CommodityDetailVO } from '@/types/Base/CommodityManagement'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addSpu, updateSpu } from '@/api/base/commodityManagementSetting'
import { getCategoryAll } from '@/api/base/commodityCategorySetting'
import { getSupplierAll } from '@/api/base/supplier'
import { CategoryVO } from '@/types/Base/CommodityCategorySetting'
import { SupplierVO } from '@/types/Base/Supplier'
import { computedSelectTableSearchHeight, SYSTEM_HEIGHT, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { removeArrayNull } from '@/utils/common'
import { StringLength } from '@/utils/dataVerification/formRule'
import { isDecimal } from '@/utils/dataVerification/tableRule'
import { exportData } from '@/utils/exportTable'

const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const xTable = ref()

const props = defineProps<{
  showDialog: boolean
  form: CommodityVO
}>()

const isShow = computed(() => props.showDialog)

const dialogTitle = computed(() => {
  if (props.form.id && props.form.id > 0) {
    return 'update'
  }
  return 'add'
})

const data = reactive({
  form: ref<CommodityVO>({
    id: 0,
    spu_code: '',
    spu_name: '',
    category_id: 0,
    category_name: '',
    spu_description: '',
    bar_code: '',
    supplier_id: 0,
    supplier_name: '',
    brand: '',
    origin: '',
    length_unit: 1,
    volume_unit: 0,
    weight_unit: 1,
    detailList: []
  }),
  tableData: [],
  rules: {
    spu_code: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_code') }!`,
      (val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
    ],
    spu_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.spu_name') }!`,
      (val: string) => StringLength(val, 0, 200) === '' || StringLength(val, 0, 200)
    ],
    spu_description: [(val: string) => StringLength(val, 0, 1000) === '' || StringLength(val, 0, 1000)],
    bar_code: [(val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)],
    brand: [(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)],
    category_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.category_name') }!`
    ],
    length_unit: [
      (val: number) => [0, 1, 2, 3].includes(val) || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.length_unit') }!`
    ],
    volume_unit: [
      (val: number) => [0, 1, 2].includes(val) || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.volume_unit') }!`
    ],
    weight_unit: [
      (val: number) => [0, 1, 2].includes(val) || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.weight_unit') }!`
    ],
    supplier_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.supplier_name') }!`
    ]
  },
  validRules: ref<any>({
    sku_code: [
      { required: true, message: `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_code') }` },
      {
        type: 'string',
        min: 0,
        max: 32,
        message: `${ i18n.global.t('system.checkText.lengthValid') }${ 0 }-${ 32 }`,
        trigger: 'change'
      }
    ],
    sku_name: [
      { required: true, message: `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.sku_name') }` },
      {
        type: 'string',
        min: 0,
        max: 200,
        message: `${ i18n.global.t('system.checkText.lengthValid') }${ 0 }-${ 200 }`,
        trigger: 'change'
      }
    ],
    unit: [
      { required: true, message: `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityManagement.unit') }` },
      {
        type: 'string',
        min: 0,
        max: 5,
        message: `${ i18n.global.t('system.checkText.lengthValid') }${ 0 }-${ 5 }`,
        trigger: 'change'
      }
    ],
    weight: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    lenght: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    width: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    height: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 8,
        decimalLength: 3,
        trigger: 'change'
      }
    ],
    volume: [
      // {
      //   validator: isDecimal,
      //   validNumerical: 'nonNegative',
      //   length: 8,
      //   decimalLength: 3,
      //   trigger: 'change'
      // }
    ],
    cost: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 10,
        decimalLength: 2,
        trigger: 'change'
      }
    ],
    price: [
      {
        validator: isDecimal,
        validNumerical: 'nonNegative',
        length: 10,
        decimalLength: 2,
        trigger: 'change'
      }
    ]
  }),
  combobox: ref<{
    length_unit: {
      label: string
      value: number
    }[]
    volume_unit: {
      label: string
      value: number
    }[]
    weight_unit: {
      label: string
      value: number
    }[]
    category_name: {
      label: string
      value: number
    }[]
    supplier_name: {
      label: string
      value: number
    }[]
  }>({
    length_unit: [
      {
        label: 'mm',
        value: 0
      },
      {
        label: 'cm',
        value: 1
      },
      {
        label: 'dm',
        value: 2
      },
      {
        label: 'm',
        value: 3
      }
    ],
    volume_unit: [
      {
        label: 'cm³',
        value: 0
      },
      {
        label: 'dm³',
        value: 1
      },
      {
        label: 'm³',
        value: 2
      }
    ],
    weight_unit: [
      {
        label: 'mg',
        value: 0
      },
      {
        label: 'g',
        value: 1
      },
      {
        label: 'kg',
        value: 2
      }
    ],
    category_name: [],
    supplier_name: []
  })
})

const method = reactive({
  // When the commodity type changes, it is mainly used to assign the ID
  categoryNameChange: (val: string) => {
    if (!val) {
      data.form.category_id = 0
    } else {
      data.form.category_id = data.combobox.category_name.filter((item) => item.label === val)[0].value
    }
  },
  supplierNameChange: (val: string) => {
    if (!val) {
      data.form.supplier_id = 0
    } else {
      data.form.supplier_id = data.combobox.supplier_name.filter((item) => item.label === val)[0].value
    }
  },
  // Get the options required by the drop-down box
  getCombobox: async () => {
    data.combobox.category_name = []
    data.combobox.supplier_name = []
    const { data: res } = await getCategoryAll()
    if (!res.isSuccess) {
      hookComponent.$message({
        type: 'error',
        content: res.errorMessage
      })
      return
    }
    data.combobox.category_name = res.data
      .filter((item: CategoryVO) => item.is_valid)
      .map((item: CategoryVO) => ({ value: item.id, label: item.category_name }))
    // Get supplier information
    const { data: supplierRes } = await getSupplierAll()
    if (!supplierRes.isSuccess) {
      hookComponent.$message({
        type: 'error',
        content: supplierRes.errorMessage
      })
      return
    }
    data.combobox.supplier_name = supplierRes.data
      .filter((item: SupplierVO) => item.is_valid)
      .map((item: SupplierVO) => ({ value: item.id, label: item.supplier_name }))
  },
  closeDialog: () => {
    emit('close')
  },
  insertOneRow: () => {
    xTable.value.insertAt(-1)
  },
  // Export table
  exportTable: () => {
    const $table = xTable.value
    exportData({
      table: $table,
      filename: i18n.global.t('router.sideBar.commodityManagement'),
      columnFilterMethod({ column }: any) {
        return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)
      }
    })
  },
  submit: async () => {
    const $table = xTable.value
    const errMap = await $table.validate(true)
    const { valid } = await formRef.value.validate()
    if (valid && !errMap) {
      if ($table.getTableData().fullData.length === 0) {
        hookComponent.$message({
          type: 'error',
          content: i18n.global.t('system.tips.detailLengthIsZero')
        })
        return
      }
      const form = { ...data.form }
      const insertRecords = $table.getInsertRecords()
      form.detailList = []
      // Processing detailed data
      if (dialogTitle.value === 'add') {
        form.detailList = [...insertRecords]
      } else {
        const updateRecords = $table.getUpdateRecords()
        const removeRecords = $table.getRemoveRecords()
        form.detailList = [...insertRecords, ...updateRecords]
        for (const item of removeRecords) {
          item.id = item.id > 0 ? 0 - item.id : item.id
          form.detailList.push(item)
        }
      }
      form.detailList = removeArrayNull(form.detailList)
      const { data: res } = dialogTitle.value === 'add' ? await addSpu(form) : await updateSpu(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')
      })
    }
  },
  editRow: (row: CommodityDetailVO) => {
    const $table = xTable.value
    $table.setEditRow(row)
  },
  deleteRow: (row: CommodityDetailVO) => {
    const $table = xTable.value
    hookComponent.$dialog({
      content: i18n.global.t('system.tips.beforeDeleteDetailMessage'),
      handleConfirm: async () => {
        $table.remove(row)
      }
    })
  }
})

const mainFormHeight = computed(() => computedSelectTableSearchHeight({ hasPager: false, hasToolBar: true }))

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      method.getCombobox()
      data.form = props.form
    }
  }
)
</script>

<style scoped lang="less">
.mainForm {
  background-color: #f9f9f9;
  border-radius: 5px;
  padding: 20px;
  box-sizing: border-box;
  overflow: auto;
}

.toolbar {
  height: 40px;
}
</style>
在这里插入图片描述
在这里插入图片描述

2.供应商信息

在这里插入图片描述
在这里插入图片描述

2.1 页面代码

1、主页面代码

代码语言:javascript
复制
<!-- supplier Setting -->
<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="12" sm="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-database-import-outline" :tooltip-text="$t('system.page.import')" @click="method.openDialogImport">
                </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="12" sm="9">
                <v-row no-gutters @keyup.enter="method.sureSearch">
                  <v-col cols="12" sm="4"></v-col>
                  <v-col cols="12" sm="4"></v-col>
                  <v-col cols="12" sm="4">
                    <v-text-field
                      v-model="data.searchForm.supplier_name"
                      clearable
                      hide-details
                      density="comfortable"
                      class="searchInput ml-5 mt-1"
                      :label="$t('base.supplier.supplier_name')"
                      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" :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="supplier_name" :title="$t('base.supplier.supplier_name')"></vxe-column>
              <vxe-column field="city" :title="$t('base.supplier.city')"></vxe-column>
              <vxe-column field="address" :title="$t('base.supplier.address')"></vxe-column>
              <vxe-column field="manager" :title="$t('base.supplier.manager')"></vxe-column>
              <vxe-column field="email" :title="$t('base.supplier.email')"></vxe-column>
              <vxe-column field="contact_tel" :title="$t('base.supplier.contact_tel')"></vxe-column>
              <vxe-column field="creator" :title="$t('base.supplier.creator')"></vxe-column>
              <vxe-column field="create_time" :title="$t('base.supplier.create_time')">
                <template #default="{ row, column }">
                  <span>{{ formatDate(row[column.property]) }}</span>
                </template>
              </vxe-column>
              <vxe-column field="last_update_time" :title="$t('base.supplier.last_update_time')">
                <template #default="{ row, column }">
                  <span>{{ formatDate(row[column.property]) }}</span>
                </template>
              </vxe-column>
              <vxe-column field="operate" :title="$t('system.page.operate')" width="160" :resizable="false" show-overflow>
                <template #default="{ row }">
                  <tooltip-btn
                    :flat="true"
                    icon="mdi-pencil-outline"
                    :tooltip-text="$t('system.page.edit')"
                    @click="method.editRow(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>
    </div>
  </div>
  <addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" />
  <importSupplierTable :show-dialog="data.showDialogImport" @close="method.closeDialogImport" @saveSuccess="method.saveSuccessImport" />
</template>

<script lang="tsx" setup>
import { computed, ref, reactive, onMounted, watch } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import { SupplierVO, DataProps } from '@/types/Base/Supplier'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import tooltipBtn from '@/components/tooltip-btn.vue'
import addOrUpdateDialog from './add-or-update-supplier.vue'
import { hookComponent } from '@/components/system'
import { DEBOUNCE_TIME } from '@/constant/system'
import { setSearchObject } from '@/utils/common'
import { SearchObject } from '@/types/System/Form'
import i18n from '@/languages/i18n'
import { getSupplierList, deleteSupplier } from '@/api/base/supplier'
import importSupplierTable from './import-supplier-table.vue'
import { formatDate } from '@/utils/format/formatSystem'
import customPager from '@/components/custom-pager.vue'
import { exportData } from '@/utils/exportTable'

const xTable = ref()

const data = reactive({
  searchForm: {
    supplier_name: ''
  },
  tableData: [],
  // activeTab: null,
  showDialog: false,
  showDialogImport: false,
  dialogForm: {
    id: 0,
    supplier_name: '',
    city: '',
    address: '',
    manager: '',
    email: '',
    contact_tel: '',
    is_valid: true
  },
  tablePage: {
    total: 0,
    pageIndex: 1,
    pageSize: DEFAULT_PAGE_SIZE,
    searchObjects: ref<Array<SearchObject>>([])
  },
  timer: ref<any>(null)
})

const method = reactive({
  // Import Dialog
  openDialogImport: () => {
    data.showDialogImport = true
  },
  closeDialogImport: () => {
    data.showDialogImport = false
  },
  saveSuccessImport: () => {
    method.refresh()
    method.closeDialog()
  },
  sureSearch: () => {
    data.tablePage.searchObjects = setSearchObject(data.searchForm)
    method.getData()
  },
  // Add user
  add: () => {
    data.dialogForm = {
      id: 0,
      supplier_name: '',
      city: '',
      address: '',
      manager: '',
      email: '',
      contact_tel: '',
      is_valid: true
    }
    data.showDialog = true
  },
  // Shut add or update dialog
  closeDialog: () => {
    data.showDialog = false
  },
  // after Add or update success.
  saveSuccess: () => {
    method.refresh()
    method.closeDialog()
  },
  // Refresh data
  refresh: () => {
    method.getData()
  },
  editRow(row: SupplierVO) {
    data.dialogForm = JSON.parse(JSON.stringify(row))
    data.showDialog = true
  },
  deleteRow(row: SupplierVO) {
    hookComponent.$dialog({
      content: i18n.global.t('system.tips.beforeDeleteMessage'),
      handleConfirm: async () => {
        if (row.id) {
          const { data: res } = await deleteSupplier(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.getData()
  }),
  exportTable: () => {
    const $table = xTable.value
    exportData({
      table: $table,
      filename: i18n.global.t('router.sideBar.supplier'),
      columnFilterMethod({ column }: any) {
        return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)
      }
    })
  },
  getData: async () => {
    const { data: res } = await getSupplierList(data.tablePage)
    if (!res.isSuccess) {
      hookComponent.$message({
        type: 'error',
        content: res.errorMessage
      })
      return
    }
    data.tableData = res.data.rows
    data.tablePage.total = res.data.totals
  }
})
onMounted(() => {
  method.getData()
})

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>
在这里插入图片描述
在这里插入图片描述

2、窗体代码

代码语言:javascript
复制
<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.supplier')}`"></v-toolbar>
        <v-card-text>
          <v-form ref="formRef">
            <v-text-field
              v-model="data.form.supplier_name"
              :label="$t('base.supplier.supplier_name')"
              :rules="data.rules.supplier_name"
              variant="outlined"
            ></v-text-field>
            <v-text-field v-model="data.form.city" :label="$t('base.supplier.city')" :rules="data.rules.city" variant="outlined"></v-text-field>
            <v-text-field v-model="data.form.email" :label="$t('base.supplier.email')" :rules="data.rules.email" variant="outlined"></v-text-field>
            <v-text-field
              v-model="data.form.address"
              :label="$t('base.supplier.address')"
              :rules="data.rules.address"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.manager"
              :label="$t('base.supplier.manager')"
              :rules="data.rules.manager"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.contact_tel"
              :label="$t('base.supplier.contact_tel')"
              :rules="data.rules.contact_tel"
              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" @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 { SupplierVO } from '@/types/Base/Supplier'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addSupplier, updateSupplier } from '@/api/base/supplier'

const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])

const props = defineProps<{
  showDialog: boolean
  form: SupplierVO
}>()

const isShow = computed(() => props.showDialog)

const dialogTitle = computed(() => {
  if (props.form.id && props.form.id > 0) {
    return 'update'
  }
  return 'add'
})

const data = reactive({
  form: ref<SupplierVO>({
    id: 0,
    supplier_name: '',
    city: '',
    address: '',
    manager: '',
    email: '',
    contact_tel: '',
    is_valid: true
  }),
  rules: {
    supplier_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.supplier.supplier_name') }!`],
    city: [],
    address: [],
    manager: [],
    email: [],
    contact_tel: [],
    is_valid: []
  }
})

const method = reactive({
  closeDialog: () => {
    emit('close')
  },
  submit: async () => {
    const { valid } = await formRef.value.validate()
    if (valid) {
      const { data: res } = dialogTitle.value === 'add' ? await addSupplier(data.form) : await updateSupplier(data.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')
      })
    }
  }
})

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      data.form = props.form
    }
  }
)
</script>

<style scoped lang="less">
.v-form {
  div {
    margin-bottom: 7px;
  }
}
</style>
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
<!-- Supplier Setting Import Dialog -->
<template>
  <v-dialog v-model="isShow" width="70%" transition="dialog-top-transition" :persistent="true">
    <template #default>
      <v-card>
        <v-toolbar color="white" :title="`${$t('system.page.import')}`"></v-toolbar>
        <v-card-text>
          <div class="mb-4">
            <tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.chooseFile')" size="x-small" @click="method.chooseFile"></tooltip-btn>
            <tooltip-btn
              icon="mdi-export-variant"
              :tooltip-text="$t('system.page.exportTemplate')"
              size="x-small"
              @click="method.exportTemplate"
            ></tooltip-btn>
            <input v-show="false" id="open" ref="uploadExcel" type="file" accept=".xls, .xlsx" @change="method.readExcel" />
          </div>
          <vxe-table
            ref="xTable"
            :column-config="{ minWidth: '100px' }"
            :data="data.importData"
            :height="SYSTEM_HEIGHT.SELECT_TABLE"
            :edit-config="{ trigger: 'click', mode: 'cell' }"
            :edit-rules="data.validRules"
            :export-config="{}"
            align="center"
          >
            <template #empty>
              {{ i18n.global.t('system.page.noData') }}
            </template>
            <vxe-column type="seq" width="60"></vxe-column>
            <vxe-column field="operate" width="60" :title="$t('system.page.operate')" :resizable="false">
              <template #default="{ row }">
                <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-column field="supplier_name" :title="$t('base.supplier.supplier_name')"></vxe-column>
            <vxe-column field="city" :title="$t('base.supplier.city')"></vxe-column>
            <vxe-column field="address" :title="$t('base.supplier.address')"></vxe-column>
            <vxe-column field="manager" :title="$t('base.supplier.manager')"></vxe-column>
            <vxe-column field="email" :title="$t('base.supplier.email')"></vxe-column>
            <vxe-column field="contact_tel" :title="$t('base.supplier.contact_tel')"></vxe-column>
          </vxe-table>
        </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 { VxeTablePropTypes } from 'vxe-table'
import * as XLSX from 'xlsx'
import i18n from '@/languages/i18n'
import { excelImport } from '@/api/base/supplier'
import { hookComponent } from '@/components/system/index'
import { SYSTEM_HEIGHT, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { SupplierExcelVO } from '@/types/Base/Supplier'
import { exportData } from '@/utils/exportTable'

const emit = defineEmits(['close', 'saveSuccess'])
const uploadExcel = ref()
const xTable = ref()

const props = defineProps<{
  showDialog: boolean
}>()

const isShow = computed(() => props.showDialog)

const data = reactive({
  importData: ref<Array<SupplierExcelVO>>([]),
  validRules: ref<VxeTablePropTypes.EditRules>({})
})

const method = reactive({
  initForm: () => {
    data.importData = []
  },
  closeDialog: () => {
    emit('close')
  },
  submit: async () => {
    const isValid = method.valid()
    if (!isValid) {
      return
    }

    const $table = xTable.value
    // It must be use 'getTableData()' to get all datas with table because it will delete row sometimes.
    const importData = $table.getTableData().fullData

    const { data: res } = await excelImport(importData)
    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')
  },

  valid: () => {
    const $table = xTable.value
    const importData = $table.getTableData().fullData

    if (importData.length <= 0) {
      hookComponent.$message({
        type: 'error',
        content: `${ i18n.global.t('system.tips.detailLengthIsZero') }`
      })
      return false
    }
    return true
  },

  chooseFile: async () => {
    uploadExcel.value.value = ''
    uploadExcel.value.click()
  },

  readExcel: async (evnt: any) => {
    const files = evnt.target.files
    if (files.length <= 0) {
      return false
    }

    const file = files[0]
    const fileReader = new FileReader()

    fileReader.onload = (ev: ProgressEvent<FileReader>) => {
      const fileData = ev?.target?.result
      if (fileData) {
        const workbook = XLSX.read(fileData, { type: 'binary' })
        const wsname = workbook.SheetNames[0]

        let ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname])
        let str = JSON.stringify(ws)
        str = str.replace(/(/g, '(')
        str = str.replace(/)/g, ')')
        ws = JSON.parse(str)

        data.importData = []
        ws.forEach((value: any, index: number, ws: any) => {
          data.importData.push({
            supplier_name: ws[index][i18n.global.t('base.supplier.supplier_name')] ? ws[index][i18n.global.t('base.supplier.supplier_name')] + '' : '',
            city: ws[index][i18n.global.t('base.supplier.city')] ? ws[index][i18n.global.t('base.supplier.city')] + '' : '',
            address: ws[index][i18n.global.t('base.supplier.address')] ? ws[index][i18n.global.t('base.supplier.address')] + '' : '',
            manager: ws[index][i18n.global.t('base.supplier.manager')] ? ws[index][i18n.global.t('base.supplier.manager')] + '' : '',
            email: ws[index][i18n.global.t('base.supplier.email')] ? ws[index][i18n.global.t('base.supplier.email')] + '' : '',
            contact_tel: ws[index][i18n.global.t('base.supplier.contact_tel')] ? ws[index][i18n.global.t('base.supplier.contact_tel')] + '' : ''
          })
        })
      }
    }
    fileReader.readAsBinaryString(file)
  },

  exportTemplate: () => {
    const $table = xTable.value
    exportData({
      table: $table,
      filename: i18n.global.t('router.sideBar.supplier'),
      mode: 'header',
      columnFilterMethod({ column }: any) {
        return !['checkbox', 'seq'].includes(column?.type) && !['operate'].includes(column?.field)
      }
    })
  },

  deleteRow: (row: SupplierExcelVO) => {
    hookComponent.$dialog({
      content: i18n.global.t('system.tips.beforeDeleteDetailMessage'),
      handleConfirm: async () => {
        if (row) {
          const $table = xTable.value
          $table.remove(row)
        }
      }
    })
  }
})

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      method.initForm()
    }
  }
)
</script>

<style scoped lang="less">
.v-form {
  div {
    margin-bottom: 7px;
  }
}
</style>
在这里插入图片描述
在这里插入图片描述

2.2 接口代码

代码语言:javascript
复制
    /// <summary>
    /// supplier controller
    /// </summary>
     [Route("supplier")]
     [ApiController]
     [ApiExplorerSettings(GroupName = "Base")]
     public class SupplierController : BaseController
     {
         #region Args
 
         /// <summary>
         /// supplier Service
         /// </summary>
         private readonly ISupplierService _supplierService;
 
         /// <summary>
         /// Localizer Service
         /// </summary>
         private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
         #endregion
 
         #region constructor
         /// <summary>
         /// constructor
         /// </summary>
         /// <param name="supplierService">supplier Service</param>
        /// <param name="stringLocalizer">Localizer</param>
         public SupplierController(
             ISupplierService supplierService
           , IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
             )
         {
             this._supplierService = supplierService;
            this._stringLocalizer= stringLocalizer;
         }
         #endregion
 
         #region Api
         /// <summary>
         /// page search
         /// </summary>
         /// <param name="pageSearch">args</param>
         /// <returns></returns>
         [HttpPost("list")]
         public async Task<ResultModel<PageData<SupplierViewModel>>> PageAsync(PageSearch pageSearch)
         {
             var (data, totals) = await _supplierService.PageAsync(pageSearch, CurrentUser);
              
             return ResultModel<PageData<SupplierViewModel>>.Success(new PageData<SupplierViewModel>
             {
                 Rows = data,
                 Totals = totals
             });
         }
 
         /// <summary>
         /// get all records
         /// </summary>
         /// <returns>args</returns>
        [HttpGet("all")]
         public async Task<ResultModel<List<SupplierViewModel>>> GetAllAsync()
         {
             var data = await _supplierService.GetAllAsync(CurrentUser);
             if (data.Any())
             {
                 return ResultModel<List<SupplierViewModel>>.Success(data);
             }
             else
             {
                 return ResultModel<List<SupplierViewModel>>.Success(new List<SupplierViewModel>());
             }
         }
 
         /// <summary>
         /// get a record by id
         /// </summary>
         /// <returns>args</returns>
         [HttpGet]
         public async Task<ResultModel<SupplierViewModel>> GetAsync(int id)
         {
             var data = await _supplierService.GetAsync(id);
             if (data!=null)
             {
                 return ResultModel<SupplierViewModel>.Success(data);
             }
             else
             {
                 return ResultModel<SupplierViewModel>.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(SupplierViewModel viewModel)
         {
             var (id, msg) = await _supplierService.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(SupplierViewModel viewModel)
         {
             var (flag, msg) = await _supplierService.UpdateAsync(viewModel, 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 _supplierService.DeleteAsync(id);
             if (flag)
             {
                 return ResultModel<string>.Success(msg);
             }
             else
             {
                 return ResultModel<string>.Error(msg);
             }
         }

        /// <summary>
        /// import suppliers by excel
        /// </summary>
        /// <param name="excel_datas">excel datas</param>
        /// <returns></returns>
        [HttpPost("excel")]
        public async Task<ResultModel<string>> ExcelAsync(List<SupplierExcelImportViewModel> excel_datas)
        {
            var (flag, msg) = await _supplierService.ExcelAsync(excel_datas, CurrentUser);
            if (flag)
            {
                return ResultModel<string>.Success(msg);
            }
            else
            {
                return ResultModel<string>.Error(msg);
            }
        }
        #endregion

    }
在这里插入图片描述
在这里插入图片描述

3.仓库设置

1、仓库设置

在这里插入图片描述
在这里插入图片描述

2、库区设置

在这里插入图片描述
在这里插入图片描述

2、库位设置

在这里插入图片描述
在这里插入图片描述

3.1 页面代码

1、主页面代码

代码语言:javascript
复制
<!-- Warehouse Setting -->
<template>
  <div class="container">
    <div>
      <v-tabs v-model="data.activeTab" stacked @update:model-value="method.changeTabs">
        <v-tab v-for="(item, index) of tabsConfig" :key="index" :value="item.value">
          <v-icon>{{ item.icon }}</v-icon>
          <p class="tabItemTitle">{{ item.tabName }}</p>
        </v-tab>
      </v-tabs>

      <!-- Main Content -->
      <v-card class="mt-5">
        <v-card-text>
          <v-window v-model="data.activeTab">
            <v-window-item value="tabWarehouse">
              <tab-warehouse ref="tabWarehouseRef" />
            </v-window-item>
            <v-window-item value="tabReservoir">
              <tab-reservoir ref="tabReservoirRef" />
            </v-window-item>
            <v-window-item value="tabLocation">
              <tab-location ref="tabLocationRef" />
            </v-window-item>
          </v-window>
        </v-card-text>
      </v-card>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, onMounted, watch, nextTick } from 'vue'
import i18n from '@/languages/i18n'
import tabWarehouse from './tab-warehouse.vue'
import tabReservoir from './tab-reservoir.vue'
import tabLocation from './tab-location.vue'

const tabWarehouseRef = ref()
const tabReservoirRef = ref()
const tabLocationRef = ref()

const tabsConfig = [
  {
    value: 'tabWarehouse',
    icon: 'mdi-warehouse',
    tabName: i18n.global.t('base.warehouseSetting.warehouseSetting')
  },
  {
    value: 'tabReservoir',
    icon: 'mdi-texture-box',
    tabName: i18n.global.t('base.warehouseSetting.reservoirSetting')
  },
  {
    value: 'tabLocation',
    icon: 'mdi-library-shelves ',
    tabName: i18n.global.t('base.warehouseSetting.locationSetting')
  }
]

const data = reactive({
  activeTab: '',
  isLoadWarehouseData: false,
  isLoadReservoirData: false,
  isLoadLocationData: false
})

const method = reactive({
  changeTabs: (e: any): void => {
    nextTick(() => {
      switch (e) {
        case 'tabWarehouse':
          // Tips:Must be write the nextTick so that can get DOM!!
          if (tabWarehouseRef?.value?.getWarehouseList) {
            tabWarehouseRef.value.getWarehouseList()
          }
          break
        case 'tabReservoir':
          if (tabReservoirRef?.value?.getWarehouseAreaList) {
            tabReservoirRef.value.getWarehouseAreaList()
          }
          break
        case 'tabLocation':
          if (tabLocationRef?.value?.getGoodsLocationList) {
            tabLocationRef.value.getGoodsLocationList()
          }
          break
      }
    })
  }
})

onMounted(() => {})
</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>
在这里插入图片描述
在这里插入图片描述

2、窗体代码

代码语言:javascript
复制
<!-- Warehouse Setting 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.warehouseSetting')}`"></v-toolbar>
        <v-card-text>
          <v-form ref="formRef">
            <v-text-field
              v-model="data.form.warehouse_name"
              :label="$t('base.warehouseSetting.warehouse_name')"
              :rules="data.rules.warehouse_name"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.city"
              :label="$t('base.warehouseSetting.city')"
              :rules="data.rules.city"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.address"
              :label="$t('base.warehouseSetting.address')"
              :rules="data.rules.address"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.contact_tel"
              :label="$t('base.warehouseSetting.contact_tel')"
              :rules="data.rules.contact_tel"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.email"
              :label="$t('base.warehouseSetting.email')"
              :rules="data.rules.email"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.manager"
              :label="$t('base.warehouseSetting.manager')"
              :rules="data.rules.manager"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-switch
              v-model="data.form.is_valid"
              color="primary"
              :label="$t('base.warehouseSetting.is_valid')"
              :rules="data.rules.is_valid"
            ></v-switch>
          </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 i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addWarehouse, updateWarehouse } from '@/api/base/warehouseSetting'
import { WarehouseVO } from '@/types/Base/Warehouse'
import { StringLength } from '@/utils/dataVerification/formRule'

const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])

const props = defineProps<{
  showDialog: boolean
  form: WarehouseVO
}>()

const isShow = computed(() => props.showDialog)

const dialogTitle = computed(() => {
  if (props.form.id && props.form.id > 0) {
    return 'update'
  }
  return 'add'
})

const data = reactive({
  form: ref<WarehouseVO>({
    id: 0,
    warehouse_name: '',
    city: '',
    address: '',
    contact_tel: '',
    email: '',
    manager: '',
    is_valid: true
  }),
  rules: {
    warehouse_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.warehouse_name') }!`,
      (val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
    ],
    city: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.city') }!`,
      (val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)
    ],
    address: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.address') }!`,
      (val: string) => StringLength(val, 0, 256) === '' || StringLength(val, 0, 256)
    ],
    contact_tel: [(val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)],
    email: [(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)],
    manager: [(val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)],
    is_valid: []
  }
})

const method = reactive({
  closeDialog: () => {
    emit('close')
  },
  initForm: () => {
    data.form = props.form
  },
  submit: async () => {
    const { valid } = await formRef.value.validate()
    if (valid) {
      const { data: res } = dialogTitle.value === 'add' ? await addWarehouse(data.form) : await updateWarehouse(data.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')
      })
    }
  }
})

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      method.initForm()
    }
  }
)
</script>

<style scoped lang="less">
.v-form {
  div {
    margin-bottom: 7px;
  }
}
</style>
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
<!-- Reservoir Setting 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('base.warehouseSetting.reservoirSetting')}`"></v-toolbar>
        <v-card-text>
          <v-form ref="formRef">
            <v-select
              v-model="data.form.warehouse_id"
              :items="data.combobox.warehouse_name"
              item-title="label"
              item-value="value"
              :rules="data.rules.warehouse_name"
              :label="$t('base.warehouseSetting.warehouse_name')"
              variant="outlined"
              clearable
              @update:model-value="method.changeWarehouse"
            ></v-select>
            <v-text-field
              v-model="data.form.area_name"
              :label="$t('base.warehouseSetting.area_name')"
              :rules="data.rules.area_name"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-select
              v-model="data.form.area_property"
              :items="data.combobox.area_property"
              item-title="label"
              item-value="value"
              :rules="data.rules.area_property"
              :label="$t('base.warehouseSetting.area_property')"
              variant="outlined"
              clearable
            ></v-select>
            <v-switch
              v-model="data.form.is_valid"
              color="primary"
              :label="$t('base.warehouseSetting.is_valid')"
              :rules="data.rules.is_valid"
            ></v-switch>
          </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 i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addWarehouseArea, updateWarehouseArea, getWarehouseSelect } from '@/api/base/warehouseSetting'
import { WarehouseAreaVO, AreaProperty } from '@/types/Base/Warehouse'
import { StringLength } from '@/utils/dataVerification/formRule'

const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])

const props = defineProps<{
  showDialog: boolean
  form: WarehouseAreaVO
}>()

const isShow = computed(() => props.showDialog)

const dialogTitle = computed(() => {
  if (props.form.id && props.form.id > 0) {
    return 'update'
  }
  return 'add'
})

const data = reactive({
  form: ref<WarehouseAreaVO>({
    id: 0,
    warehouse_id: 0,
    parent_id: 0,
    warehouse_name: '',
    area_name: '',
    area_property: AreaProperty.picking_area,
    is_valid: true
  }),
  rules: {
    warehouse_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.warehouse_name') }!`
    ],
    area_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.area_name') }!`,
      (val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
    ],
    area_property: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.area_property') }!`
    ],
    is_valid: []
  },
  combobox: ref<{
    warehouse_name: {
      label: string
      value: number
    }[]
    area_property: {
      label: string
      value: AreaProperty
    }[]
  }>({
    warehouse_name: [],
    area_property: [
      {
        label: i18n.global.t('base.warehouseSetting.picking_area'),
        value: AreaProperty.picking_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.stocking_area'),
        value: AreaProperty.stocking_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.receiving_area'),
        value: AreaProperty.receiving_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.return_area'),
        value: AreaProperty.return_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.defective_area'),
        value: AreaProperty.defective_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.inventory_area'),
        value: AreaProperty.inventory_area
      }
    ]
  })
})

const method = reactive({
  getCombobox: async () => {
    data.combobox.warehouse_name = []
    const { data: res } = await getWarehouseSelect()
    if (!res.isSuccess) {
      return
    }
    for (const item of res.data) {
      data.combobox.warehouse_name.push({
        label: item.name,
        // WarehouseID is a numberic type
        value: Number(item.value)
      })
    }
  },
  closeDialog: () => {
    emit('close')
  },
  initForm: () => {
    data.form = props.form
  },
  changeWarehouse: (warehouseID: any) => {
    // Find the ID corresponding value
    const warehouse = data.combobox.warehouse_name.find((item) => item.value === warehouseID)
    if (warehouse) {
      data.form.warehouse_name = warehouse.label
      data.form.warehouse_id = warehouseID
    }
  },
  submit: async () => {
    const { valid } = await formRef.value.validate()
    if (valid) {
      const { data: res } = dialogTitle.value === 'add' ? await addWarehouseArea(data.form) : await updateWarehouseArea(data.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')
      })
    }
  }
})

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      method.initForm()
      method.getCombobox()
    }
  }
)
</script>

<style scoped lang="less">
.v-form {
  div {
    margin-bottom: 7px;
  }
}
</style>
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
<!-- Location Setting 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('base.warehouseSetting.locationSetting')}`"></v-toolbar>
        <v-card-text>
          <v-form ref="formRef">
            <v-select
              v-model="data.form.warehouse_id"
              :items="data.combobox.warehouse_name"
              item-title="label"
              item-value="value"
              :rules="data.rules.warehouse_name"
              :label="$t('base.warehouseSetting.warehouse_name')"
              variant="outlined"
              clearable
              @update:model-value="method.changeWarehouse"
            ></v-select>
            <v-select
              v-model="data.form.warehouse_area_id"
              :items="data.combobox.warehouse_area_name"
              item-title="label"
              item-value="value"
              :rules="data.rules.warehouse_area_name"
              :label="$t('base.warehouseSetting.area_name')"
              variant="outlined"
              clearable
              @update:model-value="method.changeWarehouseArea"
            ></v-select>
            <v-select
              v-model="data.form.warehouse_area_property"
              :items="data.combobox.area_property"
              item-title="label"
              item-value="value"
              :rules="data.rules.warehouse_area_property"
              :label="$t('base.warehouseSetting.area_property')"
              variant="outlined"
              disabled
              clearable
            ></v-select>
            <v-text-field
              v-model="data.form.location_name"
              :label="$t('base.warehouseSetting.location_name')"
              :rules="data.rules.location_name"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.location_length"
              :label="$t('base.warehouseSetting.location_length')"
              :rules="data.rules.location_length"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.location_width"
              :label="$t('base.warehouseSetting.location_width')"
              :rules="data.rules.location_width"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.location_heigth"
              :label="$t('base.warehouseSetting.location_heigth')"
              :rules="data.rules.location_heigth"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.location_volume"
              :label="$t('base.warehouseSetting.location_volume')"
              :rules="data.rules.location_volume"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.location_load"
              :label="$t('base.warehouseSetting.location_load')"
              :rules="data.rules.location_load"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.roadway_number"
              :label="$t('base.warehouseSetting.roadway_number')"
              :rules="data.rules.roadway_number"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.shelf_number"
              :label="$t('base.warehouseSetting.shelf_number')"
              :rules="data.rules.shelf_number"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.layer_number"
              :label="$t('base.warehouseSetting.layer_number')"
              :rules="data.rules.layer_number"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-text-field
              v-model="data.form.tag_number"
              :label="$t('base.warehouseSetting.tag_number')"
              :rules="data.rules.tag_number"
              variant="outlined"
              clearable
            ></v-text-field>
            <v-switch
              v-model="data.form.is_valid"
              color="primary"
              :label="$t('base.warehouseSetting.is_valid')"
              :rules="data.rules.is_valid"
            ></v-switch>
          </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 { addGoodsLocation, updateGoodsLocation, getWarehouseSelect, getWarehouseAreaSelect } from '@/api/base/warehouseSetting'
import { GoodsLocationVO, AreaProperty } from '@/types/Base/Warehouse'
import i18n from '@/languages/i18n'
import { StringLength, IsDecimal } from '@/utils/dataVerification/formRule'

const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])

const props = defineProps<{
  showDialog: boolean
  form: GoodsLocationVO
}>()

const isShow = computed(() => props.showDialog)

const dialogTitle = computed(() => {
  if (props.form.id && props.form.id > 0) {
    return 'update'
  }
  return 'add'
})

const data = reactive({
  form: ref<GoodsLocationVO>({
    id: 0,
    warehouse_name: '',
    warehouse_area_name: '',
    location_name: '',
    location_length: 0,
    location_width: 0,
    location_heigth: 0,
    location_volume: 0,
    location_load: 0,
    roadway_number: '',
    shelf_number: '',
    layer_number: '',
    tag_number: '',
    is_valid: true
  }),
  rules: {
    warehouse_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.warehouse_name') }!`,
      (val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
    ],
    warehouse_area_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.area_name') }!`,
      (val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
    ],
    warehouse_area_property: [],
    location_name: [
      (val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.warehouseSetting.location_name') }!`,
      (val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)
    ],
    location_length: [(val: number) => IsDecimal(val, 'nonNegative', 5, 2) === '' || IsDecimal(val, 'nonNegative', 5, 2)],
    location_width: [(val: number) => IsDecimal(val, 'nonNegative', 5, 2) === '' || IsDecimal(val, 'nonNegative', 5, 2)],
    location_heigth: [(val: number) => IsDecimal(val, 'nonNegative', 5, 2) === '' || IsDecimal(val, 'nonNegative', 5, 2)],
    location_volume: [(val: number) => IsDecimal(val, 'nonNegative', 12, 2) === '' || IsDecimal(val, 'nonNegative', 12, 2)],
    location_load: [(val: number) => IsDecimal(val, 'nonNegative', 8, 2) === '' || IsDecimal(val, 'nonNegative', 8, 2)],
    roadway_number: [(val: string) => StringLength(val, 0, 10) === '' || StringLength(val, 0, 10)],
    shelf_number: [(val: string) => StringLength(val, 0, 10) === '' || StringLength(val, 0, 10)],
    layer_number: [(val: string) => StringLength(val, 0, 10) === '' || StringLength(val, 0, 10)],
    tag_number: [(val: string) => StringLength(val, 0, 10) === '' || StringLength(val, 0, 10)],
    is_valid: []
  },
  combobox: ref<{
    warehouse_name: {
      label: string
      value: number
    }[]
    warehouse_area_name: {
      label: string
      value: number
      meta: any
    }[]
    area_property: {
      label: string
      value: AreaProperty
    }[]
  }>({
    warehouse_name: [],
    warehouse_area_name: [],
    area_property: [
      {
        label: i18n.global.t('base.warehouseSetting.picking_area'),
        value: AreaProperty.picking_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.stocking_area'),
        value: AreaProperty.stocking_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.receiving_area'),
        value: AreaProperty.receiving_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.return_area'),
        value: AreaProperty.return_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.defective_area'),
        value: AreaProperty.defective_area
      },
      {
        label: i18n.global.t('base.warehouseSetting.inventory_area'),
        value: AreaProperty.inventory_area
      }
    ]
  })
})

const method = reactive({
  changeWarehouse: (warehouseID: any) => {
    data.combobox.warehouse_area_name = []

    if (!warehouseID) {
      method.initWarehouseArea()
      return
    }

    // Find the ID corresponding value
    const warehouse = data.combobox.warehouse_name.find((item) => item.value === warehouseID)
    if (warehouse) {
      data.form.warehouse_name = warehouse.label
      data.form.warehouse_id = warehouseID

      // Clear messages of warehouse area when warehouse changed.
      method.initWarehouseArea()
      method.getWarehouseAreaSelect(warehouseID)
    }
  },
  changeWarehouseArea: (warehouseAreaID: any) => {
    if (!warehouseAreaID) {
      method.initWarehouseArea()
      return
    }

    // Find the ID corresponding value
    const warehouse = data.combobox.warehouse_area_name.find((item) => item.value === warehouseAreaID)
    if (warehouse) {
      data.form.warehouse_area_name = warehouse.label
      data.form.warehouse_area_id = warehouseAreaID
      data.form.warehouse_area_property = warehouse.meta
    }
  },
  // Clear messages of warehouse area when warehouse changed.
  initWarehouseArea() {
    delete data.form.warehouse_area_id
    data.form.warehouse_area_name = ''
    delete data.form.warehouse_area_property
  },
  getWarehouseSelect: async () => {
    data.combobox.warehouse_name = []
    const { data: res } = await getWarehouseSelect()
    if (!res.isSuccess) {
      return
    }
    for (const item of res.data) {
      data.combobox.warehouse_name.push({
        label: item.name,
        // WarehouseID is a numeric type
        value: Number(item.value)
      })
    }
  },
  // Get warehouse area select when warehouse changed.
  getWarehouseAreaSelect: async (warehouseID: number) => {
    data.combobox.warehouse_area_name = []
    const { data: res } = await getWarehouseAreaSelect(warehouseID)
    if (!res.isSuccess) {
      return
    }
    for (const item of res.data) {
      data.combobox.warehouse_area_name.push({
        label: item.area_name,
        // ID: WarehouseAreaID
        value: item.id,
        // Custom Property
        meta: item.area_property
      })
    }
  },
  closeDialog: () => {
    emit('close')
  },
  initForm: () => {
    data.form = props.form
  },
  submit: async () => {
    const { valid } = await formRef.value.validate()
    if (valid) {
      const { data: res } = dialogTitle.value === 'add' ? await addGoodsLocation(data.form) : await updateGoodsLocation(data.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')
      })
    }
  }
})

watch(
  () => isShow.value,
  (val) => {
    if (val) {
      method.initForm()
      method.getWarehouseSelect()
    }
  }
)
</script>

<style scoped lang="less">
.v-form {
  div {
    margin-bottom: 7px;
  }
}
</style>
在这里插入图片描述
在这里插入图片描述

3.2 接口代码

代码语言:javascript
复制
/// <summary>
/// warehouse controller
/// </summary>
 [Route("warehouse")]
 [ApiController]
 [ApiExplorerSettings(GroupName = "Base")]
 public class WarehouseController : BaseController
 {
     #region Args

     /// <summary>
     /// warehouse Service
     /// </summary>
     private readonly IWarehouseService _warehouseService;

     /// <summary>
     /// Localizer Service
     /// </summary>
     private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
     #endregion

     #region constructor
     /// <summary>
     /// constructor
     /// </summary>
     /// <param name="warehouseService">warehouse Service</param>
    /// <param name="stringLocalizer">Localizer</param>
     public WarehouseController(
         IWarehouseService warehouseService
       , IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
         )
     {
         this._warehouseService = warehouseService;
        this._stringLocalizer= stringLocalizer;
     }
    #endregion

    #region Api
    /// <summary>
    /// get select items
    /// </summary>
    /// <returns></returns>
    [HttpGet("select-item")]
    public async Task<ResultModel<List<FormSelectItem>>> GetSelectItemsAsnyc()
    {
        var datas = await _warehouseService.GetSelectItemsAsnyc(CurrentUser);
        return ResultModel<List<FormSelectItem>>.Success(datas);
    }

    /// <summary>
    /// page search
    /// </summary>
    /// <param name="pageSearch">args</param>
    /// <returns></returns>
    [HttpPost("list")]
     public async Task<ResultModel<PageData<WarehouseViewModel>>> PageAsync(PageSearch pageSearch)
     {
         var (data, totals) = await _warehouseService.PageAsync(pageSearch, CurrentUser);
          
         return ResultModel<PageData<WarehouseViewModel>>.Success(new PageData<WarehouseViewModel>
         {
             Rows = data,
             Totals = totals
         });
     }

     /// <summary>
     /// get all records
     /// </summary>
     /// <returns>args</returns>
    [HttpGet("all")]
     public async Task<ResultModel<List<WarehouseViewModel>>> GetAllAsync()
     {
         var data = await _warehouseService.GetAllAsync(CurrentUser);
         if (data.Any())
         {
             return ResultModel<List<WarehouseViewModel>>.Success(data);
         }
         else
         {
             return ResultModel<List<WarehouseViewModel>>.Success(new List<WarehouseViewModel>());
         }
     }

     /// <summary>
     /// get a record by id
     /// </summary>
     /// <returns>args</returns>
     [HttpGet]
     public async Task<ResultModel<WarehouseViewModel>> GetAsync(int id)
     {
         var data = await _warehouseService.GetAsync(id);
         if (data!=null)
         {
             return ResultModel<WarehouseViewModel>.Success(data);
         }
         else
         {
             return ResultModel<WarehouseViewModel>.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(WarehouseViewModel viewModel)
     {
         var (id, msg) = await _warehouseService.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(WarehouseViewModel viewModel)
     {
         var (flag, msg) = await _warehouseService.UpdateAsync(viewModel,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 _warehouseService.DeleteAsync(id);
         if (flag)
         {
             return ResultModel<string>.Success(msg);
         }
         else
         {
             return ResultModel<string>.Error(msg);
         }
     }

    /// <summary>
    /// import warehouses by excel
    /// </summary>
    /// <param name="excel_datas">excel datas</param>
    /// <returns></returns>
    [HttpPost("excel")]
    public async Task<ResultModel<string>> ExcelAsync(List<WarehouseExcelImportViewModel> excel_datas)
    {
        var (flag, msg) = await _warehouseService.ExcelAsync(excel_datas, CurrentUser);
        if (flag)
        {
            return ResultModel<string>.Success(msg);
        }
        else
        {
            return ResultModel<string>.Error(msg);
        }
    }
    #endregion

}
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
/// <summary>
/// warehousearea controller
/// </summary>
 [Route("warehousearea")]
 [ApiController]
 [ApiExplorerSettings(GroupName = "Base")]
 public class WarehouseareaController : BaseController
 {
     #region Args

     /// <summary>
     /// warehousearea Service
     /// </summary>
     private readonly IWarehouseareaService _warehouseareaService;

     /// <summary>
     /// Localizer Service
     /// </summary>
     private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
     #endregion

     #region constructor
     /// <summary>
     /// constructor
     /// </summary>
     /// <param name="warehouseareaService">warehousearea Service</param>
    /// <param name="stringLocalizer">Localizer</param>
     public WarehouseareaController(
         IWarehouseareaService warehouseareaService
       , IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
         )
     {
         this._warehouseareaService = warehouseareaService;
        this._stringLocalizer= stringLocalizer;
     }
    #endregion

    #region Api
    /// <summary>
    /// get warehousearea  select items by warehouse_id
    /// </summary>
    /// <returns></returns>
    [HttpGet("areas-by-warehouse_id")]
    public async Task<ResultModel<List<FormSelectItem>>> GetSelectItemsAsnyc(int warehouse_id)
    {
        var datas = await _warehouseareaService.GetWarehouseareaByWarehouse_id(warehouse_id,CurrentUser);
        return ResultModel<List<FormSelectItem>>.Success(datas);
    }

    /// <summary>
    /// page search
    /// </summary>
    /// <param name="pageSearch">args</param>
    /// <returns></returns>
    [HttpPost("list")]
     public async Task<ResultModel<PageData<WarehouseareaViewModel>>> PageAsync(PageSearch pageSearch)
     {
         var (data, totals) = await _warehouseareaService.PageAsync(pageSearch, CurrentUser);
          
         return ResultModel<PageData<WarehouseareaViewModel>>.Success(new PageData<WarehouseareaViewModel>
         {
             Rows = data,
             Totals = totals
         });
     }

     /// <summary>
     /// get all records
     /// </summary>
     /// <returns>args</returns>
    [HttpGet("all")]
     public async Task<ResultModel<List<WarehouseareaViewModel>>> GetAllAsync(int warehouse_id)
     {
         var data = await _warehouseareaService.GetAllAsync( warehouse_id, CurrentUser);
         if (data.Any())
         {
             return ResultModel<List<WarehouseareaViewModel>>.Success(data);
         }
         else
         {
             return ResultModel<List<WarehouseareaViewModel>>.Success(new List<WarehouseareaViewModel>());
         }
     }

     /// <summary>
     /// get a record by id
     /// </summary>
     /// <returns>args</returns>
     [HttpGet]
     public async Task<ResultModel<WarehouseareaViewModel>> GetAsync(int id)
     {
         var data = await _warehouseareaService.GetAsync(id);
         if (data!=null)
         {
             return ResultModel<WarehouseareaViewModel>.Success(data);
         }
         else
         {
             return ResultModel<WarehouseareaViewModel>.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(WarehouseareaViewModel viewModel)
     {
         var (id, msg) = await _warehouseareaService.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(WarehouseareaViewModel viewModel)
     {
         var (flag, msg) = await _warehouseareaService.UpdateAsync(viewModel,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 _warehouseareaService.DeleteAsync(id);
         if (flag)
         {
             return ResultModel<string>.Success(msg);
         }
         else
         {
             return ResultModel<string>.Error(msg);
         }
     }
     #endregion

 }
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
/// <summary>
/// goodslocation controller
/// </summary>
 [Route("goodslocation")]
 [ApiController]
 [ApiExplorerSettings(GroupName = "Base")]
 public class GoodslocationController : BaseController
 {
     #region Args

     /// <summary>
     /// goodslocation Service
     /// </summary>
     private readonly IGoodslocationService _goodslocationService;

     /// <summary>
     /// Localizer Service
     /// </summary>
     private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
     #endregion

     #region constructor
     /// <summary>
     /// constructor
     /// </summary>
     /// <param name="goodslocationService">goodslocation Service</param>
    /// <param name="stringLocalizer">Localizer</param>
     public GoodslocationController(
         IGoodslocationService goodslocationService
       , IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
         )
     {
         this._goodslocationService = goodslocationService;
        this._stringLocalizer= stringLocalizer;
     }
    #endregion

    #region Api
    /// <summary>
    /// get select items
    /// </summary>
    /// <returns></returns>
    [HttpGet("location-by-warehouseare_id")]
    public async Task<ResultModel<List<FormSelectItem>>> GetSelectItemsAsnyc(int warehousearea_id)
    {
        var datas = await _goodslocationService.GetGoodslocationByWarehouse_area_id(warehousearea_id, CurrentUser);
        return ResultModel<List<FormSelectItem>>.Success(datas);
    }

    /// <summary>
    /// page search
    /// </summary>
    /// <param name="pageSearch">args</param>
    /// <returns></returns>
    [HttpPost("list")]
     public async Task<ResultModel<PageData<GoodslocationViewModel>>> PageAsync(PageSearch pageSearch)
     {
         var (data, totals) = await _goodslocationService.PageAsync(pageSearch, CurrentUser);
          
         return ResultModel<PageData<GoodslocationViewModel>>.Success(new PageData<GoodslocationViewModel>
         {
             Rows = data,
             Totals = totals
         });
     }

     /// <summary>
     /// get all records
     /// </summary>
     /// <returns>args</returns>
    [HttpGet("all")]
     public async Task<ResultModel<List<GoodslocationViewModel>>> GetAllAsync()
     {
         var data = await _goodslocationService.GetAllAsync(CurrentUser);
         if (data.Any())
         {
             return ResultModel<List<GoodslocationViewModel>>.Success(data);
         }
         else
         {
             return ResultModel<List<GoodslocationViewModel>>.Success(new List<GoodslocationViewModel>());
         }
     }

     /// <summary>
     /// get a record by id
     /// </summary>
     /// <returns>args</returns>
     [HttpGet]
     public async Task<ResultModel<GoodslocationViewModel>> GetAsync(int id)
     {
         var data = await _goodslocationService.GetAsync(id);
         if (data!=null)
         {
             return ResultModel<GoodslocationViewModel>.Success(data);
         }
         else
         {
             return ResultModel<GoodslocationViewModel>.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(GoodslocationViewModel viewModel)
     {
         var (id, msg) = await _goodslocationService.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(GoodslocationViewModel viewModel)
     {
         var (flag, msg) = await _goodslocationService.UpdateAsync(viewModel, 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 _goodslocationService.DeleteAsync(id);
         if (flag)
         {
             return ResultModel<string>.Success(msg);
         }
         else
         {
             return ResultModel<string>.Error(msg);
         }
     }
     #endregion

 }
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-02-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、基础设置
    • 1.商品管理
      • 1.1 页面代码
      • 1.2 接口代码
    • 2.供应商信息
      • 2.1 页面代码
      • 2.2 接口代码
    • 3.仓库设置
      • 3.1 页面代码
      • 3.2 接口代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档