update:新增商品导入管理

This commit is contained in:
lihaoyuan 2025-12-30 11:58:09 +08:00
parent 4e1c70862c
commit 2b2f0c0ed5
5 changed files with 776 additions and 1 deletions

View File

@ -45,6 +45,28 @@ export async function downloadTempGoods() {
responseType: 'blob',
})
}
//商品导入 下载三个模板
export async function downloadBrandTemplate() {
return request({
url: '/admin/shop/shop-sync-import/brandTemplate',
method: 'get',
responseType: 'blob',
})
}
export async function downloadCategoryTemplate() {
return request({
url: '/admin/shop/shop-sync-import/categoryTemplate',
method: 'get',
responseType: 'blob',
})
}
export async function downloadGoodsTemplate() {
return request({
url: '/admin/shop/shop-sync-import/shopTemplate',
method: 'get',
responseType: 'blob',
})
}
export async function exportUncheckShopData(storeId) {
return request({
url: '/admin/shop/shop-sync-productMapper/exportUncheckShopData',
@ -128,6 +150,37 @@ export async function importGoodsData(data) {
data,
})
}
//商品导入的三个导入
export async function categoryImportData(data) {
return request({
url: '/admin/shop/shop-sync-import/categoryImportData',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data,
})
}
export async function brandImportData(data) {
return request({
url: '/admin/shop/shop-sync-import/brandImportData',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data,
})
}
export async function shopImportData(data) {
return request({
url: '/admin/shop/shop-sync-import/shopImportData',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data,
})
}
export async function syncShopImages(data) {
data = stringify(data)
@ -169,5 +222,11 @@ export default {
importGoodsData,
syncShopImages,
getImgList,
saveBatchBarcode
saveBatchBarcode,
downloadBrandTemplate,
downloadCategoryTemplate,
downloadGoodsTemplate,
categoryImportData,
brandImportData,
shopImportData
}

View File

@ -0,0 +1,199 @@
<template>
<el-dialog
title="品牌数据导入"
width="50%"
:visible.sync="visible"
height="300px"
>
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<el-select
v-model="filter.storeId"
clearable
filterable
placeholder="选择店铺"
style="width: 200px;"
>
<el-option
v-for="item in shopList"
:key="item.store_id"
:label="item.store_name"
:value="item.store_id"
/>
</el-select>
<div style="margin-left: 10px; position: relative;">
<input
type="file"
ref="fileInput"
accept=".xlsx,.xls"
style="display: none;"
@change="handleFileChange"
/>
<el-button
type="default"
size="small"
@click="openFileSelector"
>
选择文件
</el-button>
<span v-if="fileName" style="margin-left: 10px; font-size: 12px;">
{{ fileName }}
</span>
</div>
</div>
<div v-if="uploadMessage" :style="{ color: uploadMessageType === 'success' ? 'green' : 'red', fontSize: '12px', marginTop: '10px' }">
{{ uploadMessage }}
</div>
<template #footer>
<span>
<el-button @click="close">取消</el-button>
<el-button
type="primary"
size="small"
@click="handlerImportCategory"
:disabled="!filter.storeId || !selectedFile"
>
导入
</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
export default {
name: 'importCategory',
data() {
return {
filter: {
productName: '',
storeId: '',
},
visible: false,
shopList: [],
selectedFile: null,
fileName: '',
uploadMessage: '',
uploadMessageType: '' // success/error
}
},
mounted() {
this.getShopList()
},
methods: {
open() {
this.visible = true
this.selectedFile = null
this.fileName = ''
this.uploadMessage = ''
this.uploadMessageType = ''
},
async getShopList() {
try {
let res = await GoodsToolApi.getShopList()
this.shopList = res.data.items || []
} catch (error) {
console.error('获取店铺列表失败:', error)
}
},
//
openFileSelector() {
this.$refs.fileInput.click()
},
//
handleFileChange(e) {
const file = e.target.files[0]
if (file) {
//
const fileExt = file.name.split('.').pop().toLowerCase()
if (fileExt !== 'xlsx' && fileExt !== 'xls') {
this.uploadMessage = '请选择Excel文件(.xlsx/.xls)'
this.uploadMessageType = 'error'
this.selectedFile = null
this.fileName = ''
// input便
this.$refs.fileInput.value = ''
return
}
//
const maxSize = 5 * 1024 * 1024 // 5MB
if (file.size > maxSize) {
this.uploadMessage = '文件大小不能超过5MB'
this.uploadMessageType = 'error'
this.selectedFile = null
this.fileName = ''
this.$refs.fileInput.value = ''
return
}
this.selectedFile = file
this.fileName = file.name
this.uploadMessage = '' //
}
},
async handlerImportCategory() {
if (!this.filter.storeId) {
this.uploadMessage = '请先选择店铺'
this.uploadMessageType = 'error'
return
}
if (!this.selectedFile) {
this.uploadMessage = '请先选择要上传的Excel文件'
this.uploadMessageType = 'error'
return
}
try {
const formData = new FormData()
formData.append('file', this.selectedFile)
formData.append('storeId', this.filter.storeId)
this.uploadMessage = '正在上传文件,请稍候...'
this.uploadMessageType = 'success'
const res = await GoodsToolApi.brandImportData(formData)
if (res.status == 200 || res.success) {
this.uploadMessage = '文件上传成功!'
this.uploadMessageType = 'success'
//
this.selectedFile = null
this.fileName = ''
this.$refs.fileInput.value = ''
this.$message.success('导入品牌成功!')
this.visible=false
} else {
this.uploadMessage = res.msg || '文件上传失败,请重试'
this.uploadMessageType = 'error'
}
} catch (error) {
this.uploadMessage = '文件上传失败:' + (error.message || '网络错误')
this.uploadMessageType = 'error'
}
},
close() {
this.visible = false
this.selectedFile = null
this.fileName = ''
this.uploadMessage = ''
this.filter.storeId = ''
if (this.$refs.fileInput) {
this.$refs.fileInput.value = ''
}
}
}
}
</script>
<style scoped>
.el-dialog {
height: 300px !important;
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<el-dialog
title="商品分类数据导入"
width="50%"
:visible.sync="visible"
height="300px"
>
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<el-select
v-model="filter.storeId"
clearable
filterable
placeholder="选择店铺"
style="width: 200px;"
>
<el-option
v-for="item in shopList"
:key="item.store_id"
:label="item.store_name"
:value="item.store_id"
/>
</el-select>
<div style="margin-left: 10px; position: relative;">
<input
type="file"
ref="fileInput"
accept=".xlsx,.xls"
style="display: none;"
@change="handleFileChange"
/>
<el-button
type="default"
size="small"
@click="openFileSelector"
>
选择文件
</el-button>
<span v-if="fileName" style="margin-left: 10px; font-size: 12px;">
{{ fileName }}
</span>
</div>
</div>
<div v-if="uploadMessage" :style="{ color: uploadMessageType === 'success' ? 'green' : 'red', fontSize: '12px', marginTop: '10px' }">
{{ uploadMessage }}
</div>
<template #footer>
<span>
<el-button @click="close">取消</el-button>
<el-button
type="primary"
size="small"
@click="handlerImportCategory"
:disabled="!filter.storeId || !selectedFile"
>
导入
</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
export default {
name: 'importCategory',
data() {
return {
filter: {
productName: '',
storeId: '',
},
visible: false,
shopList: [],
selectedFile: null,
fileName: '',
uploadMessage: '',
uploadMessageType: '' // success/error
}
},
mounted() {
this.getShopList()
},
methods: {
open() {
this.visible = true
this.selectedFile = null
this.fileName = ''
this.uploadMessage = ''
this.uploadMessageType = ''
},
async getShopList() {
try {
let res = await GoodsToolApi.getShopList()
this.shopList = res.data.items || []
} catch (error) {
console.error('获取店铺列表失败:', error)
}
},
//
openFileSelector() {
this.$refs.fileInput.click()
},
//
handleFileChange(e) {
const file = e.target.files[0]
if (file) {
//
const fileExt = file.name.split('.').pop().toLowerCase()
if (fileExt !== 'xlsx' && fileExt !== 'xls') {
this.uploadMessage = '请选择Excel文件(.xlsx/.xls)'
this.uploadMessageType = 'error'
this.selectedFile = null
this.fileName = ''
// input便
this.$refs.fileInput.value = ''
return
}
//
const maxSize = 5 * 1024 * 1024 // 5MB
if (file.size > maxSize) {
this.uploadMessage = '文件大小不能超过5MB'
this.uploadMessageType = 'error'
this.selectedFile = null
this.fileName = ''
this.$refs.fileInput.value = ''
return
}
this.selectedFile = file
this.fileName = file.name
this.uploadMessage = '' //
}
},
async handlerImportCategory() {
if (!this.filter.storeId) {
this.uploadMessage = '请先选择店铺'
this.uploadMessageType = 'error'
return
}
if (!this.selectedFile) {
this.uploadMessage = '请先选择要上传的Excel文件'
this.uploadMessageType = 'error'
return
}
try {
const formData = new FormData()
formData.append('file', this.selectedFile)
formData.append('storeId', this.filter.storeId)
this.uploadMessage = '正在上传文件,请稍候...'
this.uploadMessageType = 'success'
const res = await GoodsToolApi.categoryImportData(formData)
console.log("?????",res)
if (res.status === 200) {
this.uploadMessage = '文件上传成功!'
this.uploadMessageType = 'success'
//
this.selectedFile = null
this.fileName = ''
this.$refs.fileInput.value = ''
this.$message.success('导入分类成功!')
this.visible=false
} else {
this.uploadMessage = res.msg || '文件上传失败,请重试'
this.uploadMessageType = 'error'
}
} catch (error) {
console.error('文件上传失败:', error)
this.uploadMessage = '文件上传失败:' + (error.message || '网络错误')
this.uploadMessageType = 'error'
}
},
close() {
this.visible = false
this.selectedFile = null
this.fileName = ''
this.uploadMessage = ''
this.filter.storeId = ''
if (this.$refs.fileInput) {
this.$refs.fileInput.value = ''
}
}
}
}
</script>
<style scoped>
.el-dialog {
height: 300px !important;
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<el-dialog
title="品牌数据导入"
width="50%"
:visible.sync="visible"
height="300px"
>
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<el-select
v-model="filter.storeId"
clearable
filterable
placeholder="选择店铺"
style="width: 200px;"
>
<el-option
v-for="item in shopList"
:key="item.store_id"
:label="item.store_name"
:value="item.store_id"
/>
</el-select>
<div style="margin-left: 10px; position: relative;">
<input
type="file"
ref="fileInput"
accept=".xlsx,.xls"
style="display: none;"
@change="handleFileChange"
/>
<el-button
type="default"
size="small"
@click="openFileSelector"
>
选择文件
</el-button>
<span v-if="fileName" style="margin-left: 10px; font-size: 12px;">
{{ fileName }}
</span>
</div>
</div>
<div v-if="uploadMessage" :style="{ color: uploadMessageType === 'success' ? 'green' : 'red', fontSize: '12px', marginTop: '10px' }">
{{ uploadMessage }}
</div>
<template #footer>
<span>
<el-button @click="close">取消</el-button>
<el-button
type="primary"
size="small"
@click="handlerImportCategory"
:disabled="!filter.storeId || !selectedFile"
>
导入
</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
export default {
name: 'importCategory',
data() {
return {
filter: {
productName: '',
storeId: '',
},
visible: false,
shopList: [],
selectedFile: null,
fileName: '',
uploadMessage: '',
uploadMessageType: '' // success/error
}
},
mounted() {
this.getShopList()
},
methods: {
open() {
this.visible = true
this.selectedFile = null
this.fileName = ''
this.uploadMessage = ''
this.uploadMessageType = ''
},
async getShopList() {
try {
let res = await GoodsToolApi.getShopList()
this.shopList = res.data.items || []
} catch (error) {
console.error('获取店铺列表失败:', error)
}
},
//
openFileSelector() {
this.$refs.fileInput.click()
},
//
handleFileChange(e) {
const file = e.target.files[0]
if (file) {
//
const fileExt = file.name.split('.').pop().toLowerCase()
if (fileExt !== 'xlsx' && fileExt !== 'xls') {
this.uploadMessage = '请选择Excel文件(.xlsx/.xls)'
this.uploadMessageType = 'error'
this.selectedFile = null
this.fileName = ''
// input便
this.$refs.fileInput.value = ''
return
}
//
const maxSize = 5 * 1024 * 1024 // 5MB
if (file.size > maxSize) {
this.uploadMessage = '文件大小不能超过5MB'
this.uploadMessageType = 'error'
this.selectedFile = null
this.fileName = ''
this.$refs.fileInput.value = ''
return
}
this.selectedFile = file
this.fileName = file.name
this.uploadMessage = '' //
}
},
async handlerImportCategory() {
if (!this.filter.storeId) {
this.uploadMessage = '请先选择店铺'
this.uploadMessageType = 'error'
return
}
if (!this.selectedFile) {
this.uploadMessage = '请先选择要上传的Excel文件'
this.uploadMessageType = 'error'
return
}
try {
const formData = new FormData()
formData.append('file', this.selectedFile)
formData.append('storeId', this.filter.storeId)
this.uploadMessage = '正在上传文件,请稍候...'
this.uploadMessageType = 'success'
const res = await GoodsToolApi.shopImportData(formData)
if (res.status == 200) {
this.uploadMessage = '文件上传成功!'
this.uploadMessageType = 'success'
//
this.selectedFile = null
this.fileName = ''
this.$refs.fileInput.value = ''
this.$message.success('导入商品数据成功!')
this.visible=false
} else {
this.uploadMessage = res.msg || '文件上传失败,请重试'
this.uploadMessageType = 'error'
}
} catch (error) {
console.error('文件上传失败:', error)
this.uploadMessage = '文件上传失败:' + (error.message || '网络错误')
this.uploadMessageType = 'error'
}
},
close() {
this.visible = false
this.selectedFile = null
this.fileName = ''
this.uploadMessage = ''
this.filter.storeId = ''
if (this.$refs.fileInput) {
this.$refs.fileInput.value = ''
}
}
}
}
</script>
<style scoped>
.el-dialog {
height: 300px !important;
}
</style>

View File

@ -0,0 +1,115 @@
<template>
<div class="goods_tool_container">
<div class="list">
<div class="tool">
<el-button type="primary" @click="downloadBrandTemplate">品牌模板下载</el-button>
<el-button type="success" @click="downloadCategoryTemplate">商品分类导入模板下载</el-button>
<el-button plain size="mini" type="info" @click="downloadGoodsTemplate">商品导入模板下载</el-button>
<el-button type="warning" @click="openBrandImport">品牌数据导入</el-button>
<el-button type="danger" @click="openCategoryImport">商品分类数据导入</el-button>
<el-button @click="openGoodsImport">商品数据导入</el-button>
</div>
</div>
<importCategory ref="importCategoryRef"></importCategory>
<importBrand ref="importBrandRef"></importBrand>
<importGoods ref="importGoodsRef"></importGoods>
</div>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
import importBrand from '@/views/product/goodsImport/importBrand.vue'
import importCategory from '@/views/product/goodsImport/importCategory.vue'
import importGoods from '@/views/product/goodsImport/importGoods.vue'
export default {
components: {importBrand,importCategory,importGoods},
data() {
return {
importCategoryRef:null,
importBrandRef:null,
importGoodsRef:null,
}
},
mounted() {},
methods: {
async downloadBrandTemplate() {
const res = await GoodsToolApi.downloadBrandTemplate()
if (res) {
this.$message.success('操作成功')
this.downloadFile(res, '品牌模板.xlsx')
}
},
async downloadCategoryTemplate() {
const res = await GoodsToolApi.downloadCategoryTemplate()
if (res) {
this.$message.success('操作成功')
this.downloadFile(res, '商品分类导入模板.xlsx')
}
},
async downloadGoodsTemplate() {
const res = await GoodsToolApi.downloadGoodsTemplate()
if (res) {
this.$message.success('操作成功')
this.downloadFile(res, '商品导入模板.xlsx')
}
},
downloadFile(blobData, fileName) {
const url = window.URL.createObjectURL(new Blob([blobData]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
},
openBrandImport(){
this.$refs.importBrandRef?.open()
},
openCategoryImport(){
this.$refs.importCategoryRef?.open()
},
openGoodsImport(){
this.$refs.importGoodsRef?.open()
}
},
}
</script>
<style lang="scss" scoped>
.filter {
display: flex;
align-items: center;
padding: 20px;
margin-bottom: 15px;
gap: 10px;
border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
background: #fff;
.input_item {
width: 250px;
}
}
.list {
background: #fff;
padding: 20px;
display: flex;
flex-direction: column;
gap: 10px;
border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
}
.tool {
display: flex;
gap: 10px;
::v-deep .el-button {
margin-left: 0 !important;
}
}
</style>