feat:汇总按sku码合集

This commit is contained in:
yangmf_0726 committed 2025-08-25 09:27:37 +08:00
1 parent 96c950084f
commit e9d765a2b3
2 files changed
+245 -401

No files matched your search

@@ -1,248 +0,0 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="设备IMEI" prop="deviceId">
<el-input v-model="formData.deviceId" placeholder="请输入设备IMEI" />
</el-form-item>
<el-form-item label="SKU条形码" prop="barCode">
<el-input v-model="formData.barCode" placeholder="请输入SKU 的条形码" />
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入商品名称" />
</el-form-item>
<!-- <el-form-item label="关键字" prop="keyword">
<el-input v-model="formData.keyword" placeholder="请输入关键字" />
</el-form-item> -->
<!-- <el-form-item label="商品简介" prop="introduction">
<el-input v-model="formData.introduction" placeholder="请输入商品简介" />
</el-form-item> -->
<!-- <el-form-item label="商品详情" prop="description">
<Editor v-model="formData.description" height="150px" />
</el-form-item> -->
<el-form-item label="商品分类编号" prop="categoryId">
<el-input v-model="formData.categoryId" placeholder="请输入商品分类编号" />
</el-form-item>
<!-- <el-form-item label="商品品牌编号" prop="brandId">
<el-input v-model="formData.brandId" placeholder="请输入商品品牌编号" />
</el-form-item> -->
<el-form-item label="商品封面图" prop="picUrl">
<el-input v-model="formData.picUrl" placeholder="请输入商品封面图" />
</el-form-item>
<!-- <el-form-item label="商品轮播图地址
数组,以逗号分隔
最多上传15张" prop="sliderPicUrls">
<el-input v-model="formData.sliderPicUrls" placeholder="请输入商品轮播图地址
数组,以逗号分隔
最多上传15张" />
</el-form-item> -->
<el-form-item label="排序字段" prop="sort">
<el-input v-model="formData.sort" placeholder="请输入排序字段" />
</el-form-item>
<el-form-item label="商品价格,单位使用:分" prop="price">
<el-input v-model="formData.price" placeholder="请输入商品价格,单位使用:分" />
</el-form-item>
<!-- <el-form-item label="市场价,单位使用:分" prop="marketPrice">
<el-input v-model="formData.marketPrice" placeholder="请输入市场价,单位使用:分" />
</el-form-item> -->
<el-form-item label="成本价,单位:分" prop="costPrice">
<el-input v-model="formData.costPrice" placeholder="请输入成本价,单位: 分" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { SummaryApi, SummaryVO } from '@/api/product/summary'
/** 商品汇总 表单 */
defineOptions({ name: 'SummaryForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
pageNo:1,
pageSize:10,
/*商品编号,自增 */
id: undefined,
/*商品名称 */
name: undefined,
/*设备IMEI */
deviceId: undefined,
/*SKU 的条形码 */
barCode: undefined,
/*关键字 */
keyword: undefined,
/*商品简介 */
introduction: undefined,
/*商品详情 */
description: undefined,
/*商品分类编号 */
categoryId: undefined,
/*商品品牌编号 */
brandId: undefined,
/*商品封面图 */
picUrl: undefined,
/*商品轮播图地址数组,以逗号分隔 最多上传15张 */
sliderPicUrls: undefined,
/*排序字段 */
sort: undefined,
/*商品价格,单位使用:分 */
price: undefined,
/*市场价,单位使用:分 */
marketPrice: undefined,
/*成本价,单位: 分 */
costPrice: undefined,
/*创建时间 */
createTime: [],
/*库存 */
stock: undefined,
/*商品销量 */
salesCount: undefined,
})
const formRules = reactive({
deviceId: [{ required: true, message: '设备IMEI不能为空', trigger: 'blur' }],
barCode: [{ required: true, message: 'SKU 的条形码不能为空', trigger: 'blur' }],
name: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
categoryId: [{ required: true, message: '商品分类编号不能为空', trigger: 'blur' }],
picUrl: [{ required: true, message: '商品封面图不能为空', trigger: 'blur' }],
sort: [{ required: true, message: '排序字段不能为空', trigger: 'blur' }],
price: [{ required: true, message: '商品价格,单位使用:分不能为空', trigger: 'blur' }],
costPrice: [{ 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 {
formData.value = await SummaryApi.getSummary(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as SummaryVO
if (formType.value === 'create') {
await SummaryApi.createSummary(data)
message.success(t('common.createSuccess'))
} else {
await SummaryApi.updateSummary(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
pageNo:1,
pageSize:10,
/*商品编号,自增 */
id: undefined,
/*商品名称 */
name: undefined,
/*设备IMEI */
deviceId: undefined,
/*SKU 的条形码 */
barCode: undefined,
/*关键字 */
keyword: undefined,
/*商品简介 */
introduction: undefined,
/*商品详情 */
description: undefined,
/*商品分类编号 */
categoryId: undefined,
/*商品品牌编号 */
brandId: undefined,
/*商品封面图 */
picUrl: undefined,
/*商品轮播图地址数组,以逗号分隔 最多上传15张 */
sliderPicUrls: undefined,
/*排序字段 */
sort: undefined,
/*商品价格,单位使用:分 */
price: undefined,
/*市场价,单位使用:分 */
marketPrice: undefined,
/*成本价,单位: 分 */
costPrice: undefined,
/*创建时间 */
createTime: [],
/*库存 */
stock: undefined,
/*商品销量 */
salesCount: undefined,
}
formRef.value?.resetFields()
}
</script>
+245 -153
View File
@@ -8,7 +8,7 @@
:inline="true"
label-width="68px"
>
<el-form-item label="设备IMEI" prop="deviceId">
<!-- <el-form-item label="设备IMEI" prop="deviceId">
<el-input
v-model="queryParams.deviceId"
placeholder="请输入设备IMEI"
@@ -16,7 +16,7 @@
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
</el-form-item> -->
<el-form-item label="条形码" prop="barCode">
<el-input
v-model="queryParams.barCode"
@@ -129,7 +129,7 @@
class="!w-240px"
/>
</el-form-item> -->
<el-form-item label="创建时间" prop="createTime">
<!-- <el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
@@ -139,7 +139,7 @@
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
</el-form-item> -->
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
@@ -151,7 +151,7 @@
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
<!-- <el-button
type="success"
plain
@click="handleExport"
@@ -159,7 +159,7 @@
v-hasPermi="['product:summary:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-button> -->
</el-form-item>
</el-form>
</ContentWrap>
@@ -167,62 +167,78 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column type="expand">
<template #default="{ row }">
<div class="expand-content">
<!-- <h4>商品来源详情:</h4> -->
<el-table :data="row.sources" size="small">
<el-table-column label="来源类型" prop="type" width="120" />
<el-table-column label="SPU名称" prop="spuName" />
<!-- <el-table-column label="设备ID" prop="deviceId" /> -->
<el-table-column label="库存" prop="stock" />
<el-table-column label="销量" prop="salesCount" />
<!-- 展开详情表格中的创建时间列 -->
<el-table-column label="创建时间" prop="createTime" width="180">
<template #default="{ row }">
<span>{{
row.createTime ? dateFormatter(null, null, new Date(row.createTime)) : ''
}}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column label="商品编号" align="center" prop="id" />
<el-table-column label="设备IMEI" align="center" prop="deviceId" />
<el-table-column label="SKU条形码" align="center" prop="barCode" />
<el-table-column label="SKU条形码" align="center" prop="barCode" width="150" />
<el-table-column label="商品名称" align="center" prop="name" />
<!-- <el-table-column label="关键字" align="center" prop="keyword" /> -->
<el-table-column label="商品来源" align="center" width="120">
<template #default="{ row }">
<el-tag :type="row.sources[0]?.type === '商城商品' ? 'primary' : 'success'" size="small">
{{ row.sources[0]?.type }}
</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="设备IMEI" align="center" prop="deviceId" /> -->
<el-table-column label="商品简介" align="center" prop="introduction" />
<!-- <el-table-column label="商品详情" align="center" prop="description" /> -->
<el-table-column label="分类编号" align="center" prop="categoryId" />
<!-- <el-table-column label="商品品牌编号" align="center" prop="brandId" /> -->
<el-table-column label="商品封面图" align="center" prop="picUrl" />
<el-table-column label="库存" align="center" prop="stock" />
<el-table-column label="商品销量" align="center" prop="salesCount" />
<!-- <el-table-column label="商品轮播图地址
数组,以逗号分隔
最多上传15张" align="center" prop="sliderPicUrls" /> -->
<!-- <el-table-column label="排序字段" align="center" prop="sort" /> -->
<!-- <el-table-column label="商品价格,单位使用:分" align="center" prop="price" /> -->
<!-- <el-table-column label="市场价,单位使用:分" align="center" prop="marketPrice" /> -->
<!-- <el-table-column label="成本价,单位: 分" align="center" prop="costPrice" /> -->
<el-table-column
<el-table-column label="商品封面图" align="center" prop="picUrl">
<template #default="{ row }">
<el-image
v-if="row.picUrl"
:src="row.picUrl"
style="width: 40px; height: 40px"
fit="cover"
preview-teleported
:preview-src-list="[row.picUrl]"
/>
</template>
</el-table-column>
<!-- <el-table-column label="总库存" align="center" prop="stock">
<template #default="{ row }">
<el-tag type="info">{{ row.stock }}</el-tag>
</template>
</el-table-column>
<el-table-column label="总销量" align="center" prop="salesCount">
<template #default="{ row }">
<el-tag type="warning">{{ row.salesCount }}</el-tag>
</template>
</el-table-column> -->
<!-- <el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<!-- <el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['product:summary:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['product:summary:delete']"
>
删除
</el-button>
</template>
</el-table-column> -->
/> -->
</el-table>
<!-- 分页 -->
<!-- <Pagination
:total="total"
v-model:page="queryParams.pageNo"
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/> -->
/>
</ContentWrap>
<!-- 商品类型选择组件 -->
@@ -240,8 +256,9 @@
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { SummaryApi, SummaryVO } from '@/api/product/summary'
import SummaryForm from './SummaryForm.vue'
import * as ProductSpuApi from '@/api/mall/product/spu'
import ProductTypeSelector from './ProductTypeSelector.vue'
import { onActivated, onMounted, reactive, ref } from 'vue'
/** 商品汇总 列表 */
defineOptions({ name: 'Summary' })
@@ -255,81 +272,145 @@ const list = ref<SummaryVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const showProductTypeDialog = ref(false) // 商品类型选择弹窗显示状态
const queryParams = reactive({
// 查询参数
const queryParams = ref({
pageNo: 1,
pageSize: 10,
/*商品编号,自增 */
id: undefined,
/*商品名称 */
name: undefined,
/*设备IMEI */
deviceId: undefined,
/*SKU 的条形码 */
barCode: undefined,
/*关键字 */
keyword: undefined,
/*商品简介 */
introduction: undefined,
/*商品详情 */
description: undefined,
/*商品分类编号 */
tabType: 0,
name: '',
categoryId: undefined,
/*商品品牌编号 */
brandId: undefined,
/*商品封面图 */
picUrl: undefined,
/*商品轮播图地址数组,以逗号分隔 最多上传15张 */
sliderPicUrls: undefined,
/*排序字段 */
sort: undefined,
/*商品价格,单位使用:分 */
price: undefined,
/*市场价,单位使用:分 */
marketPrice: undefined,
/*成本价,单位: 分 */
costPrice: undefined,
/*创建时间 */
createTime: [],
/*库存 */
stock: undefined,
/*商品销量 */
salesCount: undefined,
createTime: undefined,
types: 0,
barCode: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
/**
* 获取所有商品数据(商城 + 提货柜)
*/
const fetchAllProducts = async () => {
const [mallResponse, containerResponse] = await Promise.all([
ProductSpuApi.getSpuPage({ ...queryParams.value, types: 0 }), // 商城商品
ProductSpuApi.getSpuPage({ ...queryParams.value, types: 1 }) // 提货柜商品
])
return [
...(mallResponse.list || []).map((product) => ({ ...product, source: 'mall' })),
...(containerResponse.list || []).map((product) => ({ ...product, source: 'container' }))
]
}
/**
* 获取商品详细信息
*/
const fetchProductDetails = async (products) => {
const spuIds = products.map((item) => item.id).filter(Boolean)
if (spuIds.length === 0) return []
const detailResponse = await ProductSpuApi.getSpuDetailList(spuIds)
return (detailResponse || []).map((product) => {
const originalProduct = products.find((p) => p.id === product.id)
return { ...product, source: originalProduct?.source || 'unknown' }
})
}
/** 查询列表 - 修复分页问题 */
const getList = async () => {
loading.value = true
try {
const data = await SummaryApi.getSummaryPage(queryParams)
list.value = data.list
total.value = data.total
// 获取所有商品数据
const allProducts = await fetchAllProducts()
// 获取详细信息
const detailedProducts = await fetchProductDetails(allProducts)
// 按barCode分组合并商品
const searchBarCode = queryParams.value.barCode?.trim() || null
const allGroupedProducts = groupProductsByBarCode(detailedProducts, searchBarCode)
// 前端分页处理
const { pageNo, pageSize } = queryParams.value
const startIndex = (pageNo - 1) * pageSize
const endIndex = startIndex + pageSize
list.value = allGroupedProducts.slice(startIndex, endIndex)
total.value = allGroupedProducts.length
} catch (error) {
console.error('获取商品数据失败:', error)
message.error('获取商品数据失败')
} finally {
loading.value = false
}
}
/**
* 按barCode分组合并商品 - 优化版本
*/
const groupProductsByBarCode = (products, searchBarCode = null) => {
const groupedMap = new Map()
const addToGroup = (key, productData, skuData = null) => {
if (!groupedMap.has(key)) {
groupedMap.set(key, {
id: productData.id,
barCode: skuData?.barCode || '',
name: productData.name,
deviceId: productData.deviceId || '',
introduction: productData.introduction || '',
categoryId: productData.categoryId,
picUrl: productData.picUrl,
stock: 0,
salesCount: 0,
createTime: productData.createTime,
sources: []
})
}
const grouped = groupedMap.get(key)
const stock = skuData?.stock || productData.stock || 0
const salesCount = productData.salesCount || 0
grouped.stock += stock
grouped.salesCount += salesCount
grouped.sources.push({
type: productData.source === 'mall' ? '商城商品' : '提货柜商品',
spuName: productData.name,
deviceId: productData.deviceId || '',
stock,
salesCount,
source: productData.source,
spuId: productData.id,
createTime: productData.createTime
})
}
products.forEach((product) => {
if (product.skus?.length > 0) {
// 处理有SKU数据的商品
product.skus.forEach((sku) => {
if (!sku.barCode) return
// 如果有搜索条件,只处理匹配的barCode
if (searchBarCode && sku.barCode !== searchBarCode) return
addToGroup(sku.barCode, product, sku)
})
} else {
// 处理没有SKU数据的商品
const key = `spu_${product.id}`
addToGroup(key, product)
}
})
return Array.from(groupedMap.values())
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
queryParams.value.pageNo = 1
getList()
}
@@ -341,69 +422,80 @@ const resetQuery = () => {
/** 处理商品类型确认选择 */
const handleProductTypeConfirm = (type: 'mall' | 'pickup') => {
// 立即关闭弹窗
showProductTypeDialog.value = false
// 根据选择的类型跳转到对应的添加页面
if (type === 'mall') {
// 跳转到商城商品添加页面
push({ name: 'ProductSpuAdd' })
} else if (type === 'pickup') {
// 跳转到提货柜商品添加页面
push({ name: 'ProductPickupAdd' })
const routeMap = {
mall: 'ProductSpuAdd',
pickup: 'ProductPickupAdd'
}
const routeName = routeMap[type]
if (routeName) {
push({ name: routeName })
}
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
// const formRef = ref()
// const openForm = (type: string, id?: number) => {
// formRef.value.open(type, id)
// }
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await SummaryApi.deleteSummary(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
// const handleDelete = async (id: number) => {
// try {
// await message.delConfirm()
// await SummaryApi.deleteSummary(id)
// message.success(t('common.delSuccess'))
// await getList()
// } catch {}
// }
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await SummaryApi.exportSummary(queryParams)
download.excel(data, '商品汇总.xls')
} catch {
} finally {
exportLoading.value = false
}
}
// const handleExport = async () => {
// try {
// await message.exportConfirm()
// exportLoading.value = true
// const data = await SummaryApi.exportSummary(queryParams.value)
// download.excel(data, '商品汇总.xls')
// message.success('导出成功')
// } catch (error) {
// console.error('导出失败:', error)
// message.error('导出失败,请检查网络连接或联系管理员')
// } finally {
// exportLoading.value = false
// }
// }
const productTypeSelectorKey = ref(0) // 添加 key 控制组件重新渲染
const productTypeSelectorKey = ref(0)
/** 页面激活时清除弹窗状态 */
onActivated(() => {
// 确保从其他页面返回时弹窗是关闭的
const resetPageState = () => {
showProductTypeDialog.value = false
// 强制重新渲染组件
productTypeSelectorKey.value++
// 重新获取数据
getList()
})
}
/** 初始化 **/
onActivated(resetPageState)
onMounted(() => {
getList()
})
</script>
<style scoped>
.expand-content {
padding: 10px;
background-color: #141414;
border-radius: 4px;
}
.expand-content h4 {
margin: 0 0 15px 0;
color: #606266;
font-size: 14px;
}
.mr-1 {
margin-right: 4px;
}
</style>