update:商品图片匹配

This commit is contained in:
mixtan 2025-06-27 20:17:43 +08:00
parent c641138f22
commit ebf8049e35
2 changed files with 1865 additions and 1825 deletions

View File

@ -1,125 +1,134 @@
import request from "@/utils/request";
import { stringify } from "qs";
import request from '@/utils/request'
import { stringify } from 'qs'
export async function getProductMapperList(data) {
data = stringify(data);
data = stringify(data)
return request({
url: `/admin/shop/shop-sync-productMapper/list?${data}`,
method: "get",
});
method: 'get',
})
}
export async function getShopList() {
return request({
url: "/admin/shop/shop-store-base/list?pageNum=1&pageSize=99999&store_type=1",
method: "get",
});
url: '/admin/shop/shop-store-base/list?pageNum=1&pageSize=99999&store_type=1',
method: 'get',
})
}
export async function updateGoods(data) {
return request({
url: "/admin/shop/shop-sync-productMapper/udpateProductMapping",
method: "put",
url: '/admin/shop/shop-sync-productMapper/udpateProductMapping',
method: 'put',
headers: {
'Content-Type': 'application/json',
},
data,
});
})
}
export async function deleteGoods(data) {
return request({
url: "/admin/shop/shop-sync-productMapper/delProductMapping",
method: "delete",
url: '/admin/shop/shop-sync-productMapper/delProductMapping',
method: 'delete',
headers: {
'Content-Type': 'application/json',
},
data,
});
})
}
export async function downloadTempGoods(data) {
data = stringify(data);
data = stringify(data)
return request({
url: `/admin/shop/shop-sync-productMapper/template?${data}`,
method: "get",
});
method: 'get',
})
}
export async function batchCreateGoods(data) {
return request({
url: "/admin/shop/shop-sync-productMapper/saveProductMappingBatch",
method: "post",
url: '/admin/shop/shop-sync-productMapper/saveProductMappingBatch',
method: 'post',
headers: {
'Content-Type': 'application/json',
},
data,
});
})
}
export async function batchExportGoods(data) {
return request({
url: "/admin/shop/shop-sync-productMapper/exportSelected",
method: "post",
url: '/admin/shop/shop-sync-productMapper/exportSelected',
method: 'post',
headers: {
'Content-Type': 'application/json',
},
data,
});
})
}
export async function getProductMapping(data) {
return request({
url: "/admin/shop/shop-sync-productMapper/getProductMapping",
method: "get",
url: '/admin/shop/shop-sync-productMapper/getProductMapping',
method: 'get',
data,
});
})
}
export async function getSyncBaseMapingProducts() {
return request({
url: "/admin/shop/shop-sync-productMapper/getSyncBaseMapingProducts",
method: "get",
});
url: '/admin/shop/shop-sync-productMapper/getSyncBaseMapingProducts',
method: 'get',
})
}
export async function HandleDownloadErrorReport(data) {
data = stringify(data);
export async function downloadErrorReport(data) {
data = stringify(data)
return request({
url: `/admin/shop/shop-sync-productMapper/download?${data}`,
method: "get",
});
method: 'get',
})
}
export async function syncProductMaping() {
return request({
url: `/admin/shop/shop-sync-productMapper/syncProductMaping`,
method: "put",
method: 'put',
headers: {
'Content-Type': 'application/json',
},
});
})
}
export async function importGoodsData(data) {
return request({
url: `/admin/shop/shop-sync-productMapper/importData`,
method: "post",
method: 'post',
data,
});
})
}
export async function syncShopImages(data) {
data = stringify(data)
return request({
url: `/admin/shop/shop-sync-productMapper/syncShopImages?${data}`,
method: 'get',
})
}
export default {
getProductMapperList,
getShopList,
updateGoods,
deleteGoods,/* */
deleteGoods /* */,
downloadTempGoods,
batchCreateGoods,
batchExportGoods,
getProductMapping,
getSyncBaseMapingProducts,
HandleDownloadErrorReport,
downloadErrorReport,
syncProductMaping,
importGoodsData,
};
syncShopImages,
}

View File

@ -2,28 +2,27 @@
<div class="goods_tool_container">
<div class="filter">
<el-input
v-model="filter.productName"
class="input_item"
clearable
placeholder="输入商品名称"
prefix-icon="el-icon-search"
v-model="filter.productName"
clearable
></el-input>
/>
<el-select
v-model="filter.storeId"
placeholder="选择店铺"
filterable
clearable
filterable
placeholder="选择店铺"
>
<el-option
v-for="item in shopList"
:key="item.store_id"
:label="item.store_name"
:value="item.store_id"
>
</el-option>
/>
</el-select>
<el-button type="primary" size="medium" @click="handleSearch">
<el-button size="medium" type="primary" @click="handleSearch">
搜索
</el-button>
</div>
@ -33,29 +32,38 @@
<el-button type="primary" @click="openBatchAdd">批量新增</el-button>
<el-button
:disabled="!selectedRowKeys.length"
type="success"
@click="handleExport"
:disabled="!selectedRowKeys.length"
>
导出数据
</el-button>
<el-upload action="" :show-file-list="false" :limit="1" :before-upload="beforeGoodsDataUpload">
<el-button type="warning"> 导入数据 </el-button>
<el-upload
action=""
:before-upload="beforeGoodsDataUpload"
:limit="1"
:show-file-list="false"
>
<el-button type="warning">导入数据</el-button>
</el-upload>
<el-button type="danger" @click="HandleSyncGoods">同步数据</el-button>
<el-button type="danger" @click="handleSyncGoods">同步数据</el-button>
<el-popover placement="bottom" :width="400" trigger="click">
<el-button type="danger" @click="handleSyncShopImages">
商品图库匹配
</el-button>
<el-popover placement="bottom" trigger="click" :width="400">
<template slot="reference">
<el-button >下载错误报告</el-button>
<el-button>下载错误报告</el-button>
</template>
<div>
<el-input v-model="errReportFilePath" placeholder="输入文件地址">
<template slot="append">
<el-button
type="primary"
size="medium"
type="primary"
@click="HandleDownloadErrorReport"
>
确定
@ -65,22 +73,22 @@
</div>
</el-popover>
<el-button type="info" plain @click="handleSyncProductMaping">
<el-button plain type="info" @click="handleSyncProductMaping">
自动计算并上架商品
</el-button>
</div>
<el-table
ref="productTable"
:data="tableData"
style="width: 100%"
@selection-change="handleSelectionChange"
ref="productTable"
>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" />
<el-table-column prop="storeId" label="店铺ID" />
<el-table-column prop="productName" label="商品名称" />
<el-table-column prop="description" label="商品描述" />
<el-table-column prop="specValue" label="商品规格">
<el-table-column type="selection" width="55" />
<el-table-column label="ID" prop="id" />
<el-table-column label="店铺ID" prop="storeId" />
<el-table-column label="商品名称" prop="productName" />
<el-table-column label="商品描述" prop="description" />
<el-table-column label="商品规格" prop="specValue">
<template #default="scope">
{{ scope.row.specValue }}{{ scope.row.specUnit }}
</template>
@ -88,41 +96,45 @@
<el-table-column label="操作" width="400">
<template #default="scope">
<el-button
plain
size="mini"
type="primary"
plain
@click="editGoods(scope.row)"
>编辑配置</el-button
>
编辑配置
</el-button>
<el-button
plain
size="mini"
type="danger"
plain
@click="handleDelete(scope.row)"
>删除配置</el-button
>
删除配置
</el-button>
<el-button
plain
size="mini"
type="info"
plain
@click="downloadTemplate(scope.row)"
>下载模板</el-button
>
下载模板
</el-button>
<el-button
plain
size="mini"
type="success"
plain
@click="getProductMapping(scope.row)"
>店铺商品映射</el-button
>
店铺商品映射
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:current-page="pagination.pageNum"
layout="total, sizes, prev, pager, next, jumper"
:page-size="pagination.pageSize"
:total="pagination.total"
@current-change="handleCurrentChange"
@ -132,27 +144,27 @@
<GoodsItem
ref="batchFormRef"
:initial-data="formData"
:mode="formMode"
:initialData="formData"
:shopListData="shopList"
:shop-list-data="shopList"
@success="handleSuccess"
></GoodsItem>
/>
</div>
</template>
<script>
import GoodsToolApi from "@/api/goodsTool";
import GoodsItem from "./GoodsItem.vue";
import GoodsToolApi from '@/api/goodsTool'
import GoodsItem from './GoodsItem.vue'
export default {
export default {
components: {
GoodsItem,
},
data() {
return {
filter: {
productName: "",
storeId: "",
productName: '',
storeId: '',
},
pagination: {
pageNum: 1,
@ -162,19 +174,19 @@ export default {
shopList: [],
tableData: [],
selectedRowKeys: [],
formMode: "add",
formMode: 'add',
formData: [],
errReportFilePath: "",
};
errReportFilePath: '',
}
},
mounted() {
this.getShopList();
this.getGoodsList();
this.getShopList()
this.getGoodsList()
},
methods: {
handleSearch(){
this.pagination.pageNum = 1;
this.pagination.pageSize = 10;
handleSearch() {
this.pagination.pageNum = 1
this.pagination.pageSize = 10
this.getGoodsList()
},
async getGoodsList() {
@ -182,7 +194,7 @@ export default {
...this.filter,
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize,
});
})
// res = {
// code: 0,
// status: 200,
@ -602,11 +614,11 @@ export default {
// ],
// },
// };
this.tableData = res.data.items;
this.pagination.total = res.data.records;
this.tableData = res.data.items
this.pagination.total = res.data.records
},
async getShopList() {
let res = await GoodsToolApi.getShopList();
let res = await GoodsToolApi.getShopList()
// res = {
// code: 0,
// status: 200,
@ -1709,176 +1721,196 @@ export default {
// ],
// },
// };
this.shopList = res.data.items;
this.shopList = res.data.items
},
handleCurrentChange(val) {
this.pagination.pageNum = val;
this.getGoodsList();
this.pagination.pageNum = val
this.getGoodsList()
},
handleSizeChange(val) {
this.pagination.pageSize = val;
this.getGoodsList();
this.pagination.pageSize = val
this.getGoodsList()
},
openBatchAdd() {
this.formMode = "add";
this.formData = [];
this.$refs.batchFormRef.open();
this.formMode = 'add'
this.formData = []
this.$refs.batchFormRef.open()
},
openBatchEdit() {
const selectedProducts = this.tableData.filter((item) => item.selected);
const selectedProducts = this.tableData.filter((item) => item.selected)
if (selectedProducts.length === 0) {
this.$message.warning("请先选择要编辑的商品");
return;
this.$message.warning('请先选择要编辑的商品')
return
}
this.formMode = "edit";
this.formData = selectedProducts;
this.$refs.batchFormRef.open();
this.formMode = 'edit'
this.formData = selectedProducts
this.$refs.batchFormRef.open()
},
editGoods(data) {
this.formMode = "edit";
this.formData = [data];
this.$refs.batchFormRef.open();
this.formMode = 'edit'
this.formData = [data]
this.$refs.batchFormRef.open()
},
async getProductMapping(data) {
this.$confirm("确定要映射选中的商品配置吗?", "友情提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "danger",
this.$confirm('确定要映射选中的商品配置吗?', '友情提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger',
}).then(async () => {
console.log(data);
const res = await GoodsToolApi.getProductMapping({ id: data.id });
console.log(data)
const res = await GoodsToolApi.getProductMapping({ id: data.id })
if (res.status == 200) {
this.$message.success("操作成功");
this.getProductMapperList();
this.$message.success('操作成功')
this.getProductMapperList()
}
});
})
},
handleSuccess(data) {
this.fetchProductList();
handleSuccess() {
this.fetchProductList()
},
handleSelectionChange(selection) {
this.selectedRowKeys = selection.map((item) => item.id);
this.selectedRowKeys = selection.map((item) => item.id)
},
handleDelete(data) {
this.$confirm(
"确定要删除选中的商品配置吗?此操作不可撤销。",
"友情提示",
'确定要删除选中的商品配置吗?此操作不可撤销。',
'友情提示',
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "danger",
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger',
}
).then(async () => {
console.log(data);
const res = await GoodsToolApi.deleteGoods({ id: data.id });
console.log(data)
const res = await GoodsToolApi.deleteGoods({ id: data.id })
if (res.status == 200) {
this.$message.success("已删除");
this.getProductMapperList();
this.$message.success('已删除')
this.getProductMapperList()
}
});
})
},
HandleSyncGoods() {
this.$confirm("确定要获取同步未分配的商品数据?", "友情提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "danger",
handleSyncGoods() {
this.$confirm('确定要获取同步未分配的商品数据?', '友情提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger',
}).then(async () => {
const res = await GoodsToolApi.getSyncBaseMapingProducts();
const res = await GoodsToolApi.getSyncBaseMapingProducts()
if (res.status == 200) {
this.$message.success("操作成功");
this.getProductMapperList();
this.$message.success('操作成功')
this.getProductMapperList()
}
});
})
},
handleSyncShopImages() {
if (!this.filter.storeId) {
this.$message.error('请选择店铺')
return
}
this.$confirm('确定要匹配当前选择店铺的商品图库?', '友情提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger',
}).then(async () => {
const res = await GoodsToolApi.syncShopImages({
id: this.filter.storeId,
})
if (res.status == 200) {
this.$message.success('操作成功')
this.getProductMapperList()
}
})
},
async HandleDownloadErrorReport() {
if (!this.errReportFilePath) {
this.$message.warning("请输入文件地址");
return;
this.$message.warning('请输入文件地址')
return
}
const res = await GoodsToolApi.HandleDownloadErrorReport({
const res = await GoodsToolApi.downloadErrorReport({
file: this.errReportFilePath,
});
})
if (res.status == 200) {
this.$message.success("操作成功");
this.downloadFile(res.data, `${this.errReportFilePath}.xlsx`);
this.$message.success('操作成功')
this.downloadFile(res.data, `${this.errReportFilePath}.xlsx`)
}
},
async handleSyncProductMaping() {
const res = await GoodsToolApi.syncProductMaping();
const res = await GoodsToolApi.syncProductMaping()
if (res.status == 200) {
this.$message.success("操作成功");
this.$message.success('操作成功')
}
},
async handleExport() {
if (!this.selectedRowKeys.length) {
this.$message.warning("请先选择要导出的商品");
return;
this.$message.warning('请先选择要导出的商品')
return
}
const res = await GoodsToolApi.batchExportGoods(this.selectedRowKeys);
const res = await GoodsToolApi.batchExportGoods(this.selectedRowKeys)
if (res.status == 200) {
this.downloadFile(response.data, "商品映射实体数据.xlsx");
this.downloadFile(res.data, '商品映射实体数据.xlsx')
}
},
async beforeGoodsDataUpload(file) {
const name = file.name.toLocaleLowerCase();
if(name.endsWith(".xlsx") || name.endsWith(".csv")){
handleImportGoodsData(file)
}else{
this.$message.error("文件格式错误仅支持xlsx或csv格式");
const name = file.name.toLocaleLowerCase()
if (name.endsWith('.xlsx') || name.endsWith('.csv')) {
this.handleImportGoodsData(file)
} else {
this.$message.error('文件格式错误仅支持xlsx或csv格式')
}
},
async handleImportGoodsData(file) {
const data = new FormData();
data.append("file", file);
const res = await GoodsToolApi.importGoodsData(data);
const data = new FormData()
data.append('file', file)
const res = await GoodsToolApi.importGoodsData(data)
if (res.status == 200) {
this.$message.success("操作成功");
this.$message.success('操作成功')
}
},
async downloadTemplate(data) {
const res = await GoodsToolApi.downloadTempGoods({ id: data.id });
const res = await GoodsToolApi.downloadTempGoods({ id: data.id })
if (res.status == 200) {
this.$message.success("操作成功");
this.downloadFile(res, "商品商品映射数据模板.xlsx");
this.$message.success('操作成功')
this.downloadFile(res, '商品商品映射数据模板.xlsx')
}
},
downloadFile(blobData, fileName) {
const url = window.URL.createObjectURL(new Blob([blobData]));
const url = window.URL.createObjectURL(new Blob([blobData]))
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", fileName);
const link = document.createElement('a')
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link);
link.click();
document.body.appendChild(link)
link.click()
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
},
},
};
}
</script>
<style lang="scss" scoped>
.filter {
.filter {
display: flex;
align-items: center;
padding: 20px;
@ -1886,14 +1918,14 @@ export default {
gap: 10px;
border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
background:#fff;
background: #fff;
.input_item {
width: 250px;
}
}
.list {
background:#fff;
}
.list {
background: #fff;
padding: 20px;
margin: 20px;
display: flex;
@ -1901,13 +1933,12 @@ export default {
gap: 10px;
border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
}
.tool{
}
.tool {
display: flex;
gap: 10px;
::v-deep .el-button{
::v-deep .el-button {
margin-left: 0 !important;
}
}
}
</style>