feat(wms):增加商品信息、SKU 信息

wms
YunaiV 2026-05-10 21:33:42 +08:00
parent 30e4fef7bb
commit d890781149
13 changed files with 1002 additions and 5 deletions

View File

@ -0,0 +1,55 @@
import request from '@/config/axios'
import { ItemSkuVO } from './sku'
// WMS 商品 VO
export interface ItemVO {
id?: number
code?: string
name?: string
categoryId?: number
categoryName?: string
unit?: string
brandId?: number
brandName?: string
remark?: string
skus?: ItemSkuVO[]
createTime?: Date
}
// WMS 商品 API
export const ItemApi = {
// 查询商品分页
getItemPage: async (params: any) => {
return await request.get({ url: '/wms/item/page', params })
},
// 查询商品精简列表
getItemSimpleList: async (params?: any) => {
return await request.get({ url: '/wms/item/simple-list', params })
},
// 查询商品详情
getItem: async (id: number) => {
return await request.get({ url: '/wms/item/get?id=' + id })
},
// 新增商品
createItem: async (data: ItemVO) => {
return await request.post({ url: '/wms/item/create', data })
},
// 修改商品
updateItem: async (data: ItemVO) => {
return await request.put({ url: '/wms/item/update', data })
},
// 删除商品
deleteItem: async (id: number) => {
return await request.delete({ url: '/wms/item/delete?id=' + id })
},
// 导出商品
exportItem: async (params: any) => {
return await request.download({ url: '/wms/item/export-excel', params })
}
}

View File

@ -0,0 +1,23 @@
// WMS 商品 SKU VO
export interface ItemSkuVO {
id?: number
name?: string
itemId?: number
itemCode?: string
itemName?: string
categoryId?: number
categoryName?: string
unit?: string
brandId?: number
brandName?: string
barCode?: string
code?: string
length?: number
width?: number
height?: number
grossWeight?: number
netWeight?: number
costPrice?: number
sellingPrice?: number
createTime?: Date
}

View File

@ -0,0 +1,291 @@
<!-- WMS 商品表单 -->
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1200px">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="88px"
>
<!-- 基础信息 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="商品名称" prop="name">
<el-input v-model="formData.name" maxlength="60" placeholder="请输入商品名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品分类" prop="categoryId">
<ItemCategorySelect v-model="formData.categoryId" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品编号" prop="code">
<el-input v-model="formData.code" maxlength="20" placeholder="请输入商品编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品单位" prop="unit">
<el-input v-model="formData.unit" maxlength="20" placeholder="请输入单位" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品品牌" prop="brandId">
<ItemBrandSelect v-model="formData.brandId" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" maxlength="255" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
<!-- 规格信息 -->
<div class="mb-12px flex items-center justify-between">
<span class="text-14px font-bold">规格</span>
<el-button plain type="primary" @click="handleAddSku">
<Icon class="mr-5px" icon="ep:plus" />
新增规格
</el-button>
</div>
<el-table :data="formData.skus" border>
<el-table-column label="规格名称" min-width="150">
<template #default="scope">
<el-form-item
:prop="'skus.' + scope.$index + '.name'"
:rules="skuNameRules"
class="!mb-0 !w-1/1"
label-width="0"
>
<el-input
v-model="scope.row.name"
maxlength="255"
class="!w-1/1"
placeholder="请输入规格名称"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="编号/条码" width="220">
<template #default="scope">
<el-input v-model="scope.row.code" maxlength="64" class="!w-1/1" placeholder="编号" />
<el-input
v-model="scope.row.barCode"
maxlength="64"
class="!w-1/1 mt-5px"
placeholder="条码为空时,提交后自动生成"
/>
</template>
</el-table-column>
<el-table-column label="长/宽/高(cm)" width="210">
<template #default="scope">
<div class="flex w-1/1 gap-5px">
<el-input-number
v-model="scope.row.length"
:controls="false"
:min="0"
:precision="DIMENSION_PRECISION"
class="!w-1/3"
placeholder="长"
/>
<el-input-number
v-model="scope.row.width"
:controls="false"
:min="0"
:precision="DIMENSION_PRECISION"
class="!w-1/3"
placeholder="宽"
/>
<el-input-number
v-model="scope.row.height"
:controls="false"
:min="0"
:precision="DIMENSION_PRECISION"
class="!w-1/3"
placeholder="高"
/>
</div>
</template>
</el-table-column>
<el-table-column label="净重/毛重(kg)" width="180">
<template #default="scope">
<el-input-number
v-model="scope.row.netWeight"
:controls="false"
:min="0"
:precision="WEIGHT_PRECISION"
class="!w-1/1"
placeholder="净重"
/>
<el-input-number
v-model="scope.row.grossWeight"
:controls="false"
:min="0"
:precision="WEIGHT_PRECISION"
class="!w-1/1 mt-5px"
placeholder="毛重"
/>
</template>
</el-table-column>
<el-table-column label="成本价/销售价" width="180">
<template #default="scope">
<el-input-number
v-model="scope.row.costPrice"
:controls="false"
:min="0"
:precision="PRICE_PRECISION"
class="!w-1/1"
placeholder="成本价"
/>
<el-input-number
v-model="scope.row.sellingPrice"
:controls="false"
:min="0"
:precision="PRICE_PRECISION"
class="!w-1/1 mt-5px"
placeholder="销售价"
/>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="80">
<template #default="scope">
<el-button link type="danger" @click="handleDeleteSku(scope.$index)"></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { FormRules } from 'element-plus'
import { ItemApi, ItemVO } from '@/api/wms/md/item'
import { ItemSkuVO } from '@/api/wms/md/item/sku'
import { DIMENSION_PRECISION, PRICE_PRECISION, WEIGHT_PRECISION } from '@/views/wms/utils/format'
import ItemBrandSelect from './brand/components/ItemBrandSelect.vue'
import ItemCategorySelect from './category/components/ItemCategorySelect.vue'
/** WMS 商品表单 */
defineOptions({ name: 'WmsItemForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) //
const formType = ref('') // create - update -
const formData = ref<ItemVO>({
id: undefined,
code: undefined,
name: undefined,
categoryId: undefined,
unit: undefined,
brandId: undefined,
remark: undefined,
skus: []
})
const formRules = reactive<FormRules>({
name: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
categoryId: [{ required: true, message: '商品分类不能为空', trigger: 'change' }],
skus: [{ required: true, message: '至少包含一个商品规格', trigger: 'change' }]
})
const skuNameRules = [{ required: true, message: '规格名称不能为空', trigger: 'blur' }]
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
const item = await ItemApi.getItem(id)
formData.value = {
...item,
skus: item.skus?.length ? item.skus : [buildEmptySku()]
}
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 构建空规格 */
const buildEmptySku = (): ItemSkuVO => ({
id: undefined,
name: undefined,
barCode: undefined,
code: undefined,
length: undefined,
width: undefined,
height: undefined,
grossWeight: undefined,
netWeight: undefined,
costPrice: undefined,
sellingPrice: undefined
})
/** 添加规格 */
const handleAddSku = () => {
formData.value.skus?.push(buildEmptySku())
}
/** 删除规格 */
const handleDeleteSku = (index: number) => {
if (!formData.value.skus || formData.value.skus.length <= 1) {
message.error('至少包含一个商品规格')
return
}
formData.value.skus.splice(index, 1)
}
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as ItemVO
if (formType.value === 'create') {
await ItemApi.createItem(data)
message.success(t('common.createSuccess'))
} else {
await ItemApi.updateItem(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
code: undefined,
name: undefined,
categoryId: undefined,
unit: undefined,
brandId: undefined,
remark: undefined,
skus: [buildEmptySku()]
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,81 @@
<!-- WMS 商品品牌选择器 -->
<template>
<el-select
v-bind="$attrs"
v-model="selectValue"
:clearable="clearable"
:disabled="disabled"
:filter-method="handleFilter"
:placeholder="placeholder"
class="w-1/1"
filterable
@change="handleChange"
>
<el-option
v-for="brand in filteredList"
:key="brand.id!"
:label="brand.name"
:value="brand.id!"
/>
</el-select>
</template>
<script lang="ts" setup>
import { ItemBrandApi, ItemBrandVO } from '@/api/wms/md/item/brand'
/** WMS 商品品牌选择器 */
defineOptions({ name: 'WmsItemBrandSelect', inheritAttrs: false })
const props = withDefaults(
defineProps<{
modelValue?: number
disabled?: boolean
clearable?: boolean
placeholder?: string
}>(),
{
disabled: false,
clearable: true,
placeholder: '请选择商品品牌'
}
)
const emit = defineEmits<{
'update:modelValue': [value: number | undefined]
change: [brand: ItemBrandVO | undefined]
}>()
const brandList = ref<ItemBrandVO[]>([]) //
const filteredList = ref<ItemBrandVO[]>([]) //
const selectValue = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
/** 前端过滤 */
const handleFilter = (query: string) => {
if (!query) {
filteredList.value = brandList.value
return
}
const keyword = query.toLowerCase()
filteredList.value = brandList.value.filter((brand) =>
brand.name?.toLowerCase().includes(keyword)
)
}
/** 选中变化 */
const handleChange = (value: number | undefined) => {
emit(
'change',
brandList.value.find((brand) => brand.id === value)
)
}
/** 初始化 */
onMounted(async () => {
brandList.value = await ItemBrandApi.getItemBrandSimpleList()
filteredList.value = brandList.value
})
</script>

View File

@ -23,7 +23,12 @@
<el-input v-model="formData.name" placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="显示排序" prop="sort">
<el-input-number v-model="formData.sort" :min="0" class="!w-1/1" controls-position="right" />
<el-input-number
v-model="formData.sort"
:min="0"
class="!w-1/1"
controls-position="right"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="formData.status" clearable placeholder="请选择状态">

View File

@ -0,0 +1,57 @@
<!-- WMS 商品分类选择器 -->
<template>
<el-tree-select
v-bind="$attrs"
v-model="selectValue"
:data="categoryTree"
:disabled="disabled"
:props="defaultProps"
check-strictly
class="w-1/1"
default-expand-all
:placeholder="placeholder"
value-key="id"
/>
</template>
<script lang="ts" setup>
import { defaultProps, handleTree } from '@/utils/tree'
import { ItemCategoryApi, ItemCategoryVO } from '@/api/wms/md/item/category'
/** WMS 商品分类选择器 */
defineOptions({ name: 'WmsItemCategorySelect', inheritAttrs: false })
const props = withDefaults(
defineProps<{
modelValue?: number
disabled?: boolean
placeholder?: string
}>(),
{
disabled: false,
placeholder: '请选择商品分类'
}
)
const emit = defineEmits<{
'update:modelValue': [value: number | undefined]
}>()
const categoryTree = ref<ItemCategoryVO[]>([]) //
const selectValue = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
/** 获得分类树 */
const getCategoryTree = async () => {
const data = await ItemCategoryApi.getItemCategorySimpleList()
categoryTree.value = handleTree(data, 'id', 'parentId')
}
/** 初始化 */
onMounted(async () => {
await getCategoryTree()
})
</script>

View File

@ -0,0 +1,100 @@
<!-- WMS 商品分类树组件 -->
<template>
<div class="h-full">
<el-input v-model="filterText" class="p-[15px]" clearable :placeholder="filterPlaceholder">
<template #prefix>
<Icon icon="ep:search" />
</template>
</el-input>
<el-scrollbar class="!h-[calc(100%-32px-30px)]">
<el-tree
ref="treeRef"
:data="categoryList"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:props="defaultProps"
default-expand-all
highlight-current
node-key="id"
@node-click="handleNodeClick"
/>
</el-scrollbar>
</div>
</template>
<script lang="ts" setup>
import { ElTree } from 'element-plus'
import { defaultProps, handleTree } from '@/utils/tree'
import { ItemCategoryApi, ItemCategoryVO } from '@/api/wms/md/item/category'
/** WMS 商品分类树组件 */
defineOptions({ name: 'WmsItemCategoryTree' })
withDefaults(
defineProps<{
filterPlaceholder?: string //
}>(),
{
filterPlaceholder: '请输入分类名称'
}
)
const emit = defineEmits<{
'node-click': [categoryId: number | undefined]
}>()
const filterText = ref('') //
const categoryList = ref<ItemCategoryVO[]>([]) //
const treeRef = ref<InstanceType<typeof ElTree>>() // Ref
let currentNodeId: number | null = null // ID
/** 加载分类树 */
const loadTree = async () => {
const data = await ItemCategoryApi.getItemCategorySimpleList()
categoryList.value = handleTree(data, 'id', 'parentId')
}
/** 基于名字过滤 */
const filterNode = (name: string, data: ItemCategoryVO) => {
if (!name) {
return true
}
return !!data.name?.includes(name)
}
/** 监听过滤关键字 */
watch(filterText, (val) => {
treeRef.value?.filter(val)
})
/** 处理节点点击:支持点击同一节点取消选中 */
const handleNodeClick = (row: ItemCategoryVO) => {
if (currentNodeId === row.id) {
treeRef.value?.setCurrentKey(undefined)
currentNodeId = null
emit('node-click', undefined)
} else {
currentNodeId = row.id!
emit('node-click', row.id)
}
}
/** 清空选中状态 */
const reset = () => {
currentNodeId = null
filterText.value = ''
treeRef.value?.setCurrentKey(undefined)
}
/** 设置当前选中分类 */
const setCurrent = (categoryId: number) => {
treeRef.value?.setCurrentKey(categoryId)
}
defineExpose({ reset, setCurrent })
/** 初始化 */
onMounted(async () => {
await loadTree()
})
</script>

View File

@ -0,0 +1,312 @@
<!-- WMS 商品管理 -->
<template>
<el-row :gutter="20">
<el-col :span="4" :xs="24">
<ContentWrap class="h-1/1">
<ItemCategoryTree ref="categoryTreeRef" @node-click="handleCategoryNodeClick" />
</ContentWrap>
</el-col>
<el-col :span="20" :xs="24">
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="商品编号" prop="code">
<el-input
v-model="queryParams.code"
class="!w-240px"
clearable
placeholder="请输入商品编号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input
v-model="queryParams.name"
class="!w-240px"
clearable
placeholder="请输入商品名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<ItemBrandSelect v-model="queryParams.brandId" class="!w-240px" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button
v-hasPermi="['wms:item:create']"
plain
type="primary"
@click="openForm('create')"
>
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
<el-button
v-hasPermi="['wms:item:export']"
:loading="exportLoading"
plain
type="success"
@click="handleExport"
>
<Icon class="mr-5px" icon="ep:download" />
导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 商品列表 -->
<ContentWrap>
<el-table
v-loading="loading"
:data="list"
:show-overflow-tooltip="true"
:span-method="spanMethod"
>
<el-table-column label="商品信息" min-width="220">
<template #default="scope">
<div class="text-14px">{{ scope.row.itemName }}</div>
<div v-if="scope.row.itemCode" class="text-12px text-gray-500">
{{ scope.row.itemCode }}
</div>
<div v-if="scope.row.brandName" class="text-12px text-gray-500">
品牌{{ scope.row.brandName }}
</div>
<div v-if="scope.row.categoryName" class="text-12px text-gray-500">
分类{{ scope.row.categoryName }}
</div>
</template>
</el-table-column>
<el-table-column label="规格信息" min-width="180">
<template #default="scope">
<div class="text-14px">{{ scope.row.name }}</div>
<div v-if="scope.row.code" class="text-12px text-gray-500"
>编号{{ scope.row.code }}</div
>
<div v-if="scope.row.barCode" class="text-12px text-gray-500">
条码{{ scope.row.barCode }}
</div>
</template>
</el-table-column>
<el-table-column label="金额(元)" min-width="140">
<template #default="scope">
<div v-if="hasValue(scope.row.costPrice)">
成本价{{ formatPrice(scope.row.costPrice) }}
</div>
<div v-if="hasValue(scope.row.sellingPrice)">
销售价{{ formatPrice(scope.row.sellingPrice) }}
</div>
</template>
</el-table-column>
<el-table-column label="重量(kg)" min-width="140">
<template #default="scope">
<div v-if="hasValue(scope.row.netWeight)">
净重{{ formatWeight(scope.row.netWeight) }}
</div>
<div v-if="hasValue(scope.row.grossWeight)">
毛重{{ formatWeight(scope.row.grossWeight) }}
</div>
</template>
</el-table-column>
<el-table-column align="right" label="长宽高(cm)" min-width="180">
<template #default="scope">
{{ formatDimensionText(scope.row.length, scope.row.width, scope.row.height) }}
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="120">
<template #default="scope">
<el-button
v-hasPermi="['wms:item:update']"
link
type="primary"
@click="openForm('update', scope.row.itemId)"
>
修改
</el-button>
<el-button
v-hasPermi="['wms:item:delete']"
link
type="danger"
@click="handleDelete(scope.row.itemId, scope.row.itemName)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
</el-col>
</el-row>
<!-- 表单弹窗添加/修改 -->
<ItemForm ref="formRef" @success="getList" />
</template>
<script lang="ts" setup>
import download from '@/utils/download'
import { ItemApi, ItemVO } from '@/api/wms/md/item'
import { ItemSkuVO } from '@/api/wms/md/item/sku'
import ItemForm from './ItemForm.vue'
import ItemCategoryTree from './category/components/ItemCategoryTree.vue'
import ItemBrandSelect from './brand/components/ItemBrandSelect.vue'
import { isNullOrUnDef } from '@/utils/is'
import { formatDimensionText, formatPrice, formatWeight } from '@/views/wms/utils/format'
/** WMS 商品管理 */
defineOptions({ name: 'WmsItem' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<ItemSkuVO[]>([]) //
const total = ref(0) //
const queryParams = reactive<{
pageNo: number
pageSize: number
code?: string
name?: string
categoryId?: number
brandId?: number
}>({
pageNo: 1,
pageSize: 10,
code: undefined,
name: undefined,
categoryId: undefined,
brandId: undefined
})
const queryFormRef = ref() //
const categoryTreeRef = ref() //
const exportLoading = ref(false) //
const hasValue = (value?: number | string | null) => !isNullOrUnDef(value)
/** 查询商品列表 */
const getList = async () => {
loading.value = true
try {
const data = await ItemApi.getItemPage(queryParams)
list.value = buildItemSkuRows(data.list || [])
total.value = data.total
} finally {
loading.value = false
}
}
/** 展开商品 SKU 列表 */
const buildItemSkuRows = (items: ItemVO[]) => {
return items.flatMap((item) => {
const skus = item.skus?.length ? item.skus : [{}]
return skus.map((sku) => ({
...sku,
itemId: item.id,
itemCode: item.code,
itemName: item.name,
categoryId: item.categoryId,
categoryName: item.categoryName,
unit: item.unit,
brandId: item.brandId,
brandName: item.brandName
}))
})
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
categoryTreeRef.value?.reset()
queryParams.categoryId = undefined
handleQuery()
}
/** 分类树点击 */
const handleCategoryNodeClick = (categoryId: number | undefined) => {
queryParams.categoryId = categoryId
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除商品 */
const handleDelete = async (id: number, name?: string) => {
try {
//
await message.confirm('确认删除商品【' + name + '】吗?')
//
await ItemApi.deleteItem(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await ItemApi.exportItem(queryParams)
download.excel(data, '商品.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 合并商品维度的重复单元格 */
const spanMethod = ({ rowIndex, columnIndex }: { rowIndex: number; columnIndex: number }) => {
if (![0, 5].includes(columnIndex)) {
return { rowspan: 1, colspan: 1 }
}
const row = list.value[rowIndex]
if (rowIndex > 0 && list.value[rowIndex - 1]?.itemId === row.itemId) {
return { rowspan: 0, colspan: 0 }
}
let rowspan = 1
for (let index = rowIndex + 1; index < list.value.length; index++) {
if (list.value[index]?.itemId !== row.itemId) {
break
}
rowspan++
}
return { rowspan, colspan: 1 }
}
/** 初始化 */
onMounted(async () => {
await getList()
})
</script>

View File

@ -15,7 +15,12 @@
<el-input v-model="formData.code" maxlength="20" placeholder="请输入仓库编号" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort" :min="0" class="!w-1/1" controls-position="right" />
<el-input-number
v-model="formData.sort"
:min="0"
class="!w-1/1"
controls-position="right"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input

View File

@ -56,7 +56,9 @@ const formLoading = ref(false) // 表单的加载中1修改时的数据加
const formType = ref('') // create - update -
const warehouseList = ref<WarehouseVO[]>([]) //
const selectableWarehouseList = computed(() =>
warehouseList.value.filter((warehouse): warehouse is WarehouseVO & { id: number } => !!warehouse.id)
warehouseList.value.filter(
(warehouse): warehouse is WarehouseVO & { id: number } => !!warehouse.id
)
)
const formData = ref<WarehouseAreaVO>({
id: undefined,

View File

@ -154,7 +154,9 @@ const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) //
const warehouseList = ref<WarehouseVO[]>([]) //
const selectableWarehouseList = computed(() =>
warehouseList.value.filter((warehouse): warehouse is WarehouseVO & { id: number } => !!warehouse.id)
warehouseList.value.filter(
(warehouse): warehouse is WarehouseVO & { id: number } => !!warehouse.id
)
)
/** 查询库区列表 */

View File

@ -79,7 +79,9 @@
v-if="AREA_ENABLE"
:to="{ name: 'WmsWarehouseArea', params: { warehouseId: scope.row.id } }"
>
<el-button v-hasPermi="['wms:warehouse-area:query']" link type="primary"></el-button>
<el-button v-hasPermi="['wms:warehouse-area:query']" link type="primary">
库区
</el-button>
</router-link>
<el-button
v-hasPermi="['wms:warehouse:update']"

View File

@ -0,0 +1,62 @@
/**
* WMS
*/
import { isNullOrUnDef } from '@/utils/is'
/** 金额小数位 */
export const PRICE_PRECISION = 2
/** 重量小数位 */
export const WEIGHT_PRECISION = 3
/** 长宽高小数位 */
export const DIMENSION_PRECISION = 1
export const formatNumber = (
value?: number | string | null,
digit: number = PRICE_PRECISION
) => {
if (isNullOrUnDef(value)) {
return ''
}
if (typeof value === 'string' && value.trim() === '') {
return ''
}
const numberValue = typeof value === 'string' ? Number(value) : value
if (!Number.isFinite(numberValue)) {
return ''
}
return numberValue.toFixed(digit)
}
/** 格式化金额,保留 2 位小数 */
export const formatPrice = (value?: number | string | null) => {
return formatNumber(value, PRICE_PRECISION)
}
/** 格式化重量,保留 3 位小数 */
export const formatWeight = (value?: number | string | null) => {
return formatNumber(value, WEIGHT_PRECISION)
}
/** 格式化长宽高,保留 1 位小数 */
export const formatDimension = (value?: number | string | null) => {
return formatNumber(value, DIMENSION_PRECISION)
}
/** 格式化长宽高组合,保留 1 位小数 */
export const formatDimensionText = (
length?: number | string | null,
width?: number | string | null,
height?: number | string | null
) => {
if (!isNullOrUnDef(length) && !isNullOrUnDef(width) && !isNullOrUnDef(height)) {
return [formatDimension(length), formatDimension(width), formatDimension(height)].join(' * ')
}
return [
!isNullOrUnDef(length) ? `长:${formatDimension(length)}` : undefined,
!isNullOrUnDef(width) ? `宽:${formatDimension(width)}` : undefined,
!isNullOrUnDef(height) ? `高:${formatDimension(height)}` : undefined
]
.filter(Boolean)
.join(' ')
}