update:新增主图修改,副图显示

This commit is contained in:
lihaoyuan 2025-11-28 10:26:20 +08:00
parent cedc139f62
commit 33538a3aaa
2 changed files with 431 additions and 22 deletions

View File

@ -43,10 +43,16 @@
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<!-- 操作列编辑 + 新增保存按钮 -->
<el-table-column label="操作" width="100">
<el-table-column label="操作" width="220">
<template #default="scope">
<!-- 保存按钮点击提交当前行条码修改 -->
<el-button
plain
size="mini"
type="primary"
@click="handleEdit(scope.row)"
>
编辑图片
</el-button>
<el-button
plain
size="mini"
@ -54,7 +60,7 @@
@click="handleSaveSingle(scope.row)"
:loading="scope.row.saveLoading"
>
保存
保存条码
</el-button>
</template>
</el-table-column>
@ -66,8 +72,8 @@
clearable
size="small"
class="barcode-input"
:disabled="scope.row.saveLoading"
@change="handleBarcodeChange(scope.row)"
:disabled="scope.row.saveLoading"
@change="handleBarcodeChange(scope.row)"
/>
</template>
</el-table-column>
@ -85,6 +91,32 @@
<span v-else class="no-data">无主图</span>
</template>
</el-table-column>
<el-table-column label="商品副图" width="200px">
<template #default="scope">
<div class="sub-img-group">
<div
v-for="(item, index) in scope.row.product_image_list || []"
:key="item.id || index"
class="sub-img-item"
>
<el-image
:src="item.imageUrl"
:preview-src-list="[scope.row.thumb, ...(scope.row.product_image_list || []).map(img => img.imageUrl)]"
:preview-index="index + 1"
class="sub-img-thumb"
fit="cover"
:title="`副图${index + 1}`"
@error="() => {
if (scope.row.product_image_list && scope.row.product_image_list[index]) {
scope.row.product_image_list[index].imageUrl = '';
}
}"
/>
</div>
<span v-if="!(scope.row.product_image_list && scope.row.product_image_list.length)" class="no-data">无副图</span>
</div>
</template>
</el-table-column>
<el-table-column label="商品名称" prop="name" width="300">
<template #default="scope">
<div class="text-ellipsis" :title="scope.row.name">
@ -114,15 +146,21 @@
:disabled="pagination.total === 0"
/>
</div>
<batchEditBarcode ref="batchEditBarcode" @success="handleEditSuccess" />
<batchEditBarcode ref="batchEditBarcodeRef" @success="handleEditSuccess" />
<img-edit ref="imgEditRef" @success="handleEditSuccess" />
</div>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
import batchEditBarcode from './batchEditBarcode.vue'
import imgEdit from './imgEdit.vue'
export default {
components: {
batchEditBarcode,
imgEdit
},
data() {
return {
tableData: [],
@ -138,16 +176,14 @@ export default {
barcodeEmty: '', // : 'yes', 'no'
},
multipleSelection: [],
imgEditRef: null,
batchEditBarcodeRef: null,
}
},
components: {
batchEditBarcode,
},
mounted() {
this.handleImgList()
},
methods: {
//
async handleImgList() {
try {
const params = {
@ -162,21 +198,15 @@ export default {
const res = await GoodsToolApi.getImgList(params)
//
this.tableData = (res.records || []).map(item => ({
...item,
saveLoading: false, //
originalBarcode: item.barcode, //
saveLoading: false,
originalBarcode: item.barcode,
}))
this.pagination.total = res.total || 0
this.pagination.pages = res.pages || 0
this.pagination.current = res.current || 1
console.log('商品列表数据获取成功', {
tableData: this.tableData,
pagination: this.pagination,
params: params,
})
} catch (error) {
this.$message.error('获取数据失败,请重试')
this.tableData = []
@ -223,7 +253,7 @@ export default {
this.$message.warning('请先选中需要编辑的商品');
return;
}
this.$refs.batchEditBarcode.open(this.multipleSelection);
this.$refs.batchEditBarcodeRef.open(this.multipleSelection);
},
//
@ -278,9 +308,13 @@ export default {
//
handleEditSuccess() {
this.handleImgList();
this.$refs.imgTable.clearSelection();
this.$refs.imgTable?.clearSelection();
this.multipleSelection = [];
},
handleEdit(row) {
this.$refs.imgEditRef?.open(row);
},
},
}
</script>
@ -335,7 +369,6 @@ export default {
font-size: 12px;
}
// 使
.barcode-input {
width: 100%;
}
@ -348,4 +381,24 @@ export default {
margin-top: 10px;
text-align: right;
}
.sub-img-group {
display: flex;
flex-wrap: nowrap;
gap: 4px;
align-items: center;
overflow-x: auto; //
}
.sub-img-item {
flex: 0 0 auto;
}
.sub-img-thumb {
width: 46px;
height: 46px;
border-radius: 3px;
object-fit: cover;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,356 @@
<template>
<el-dialog
title="编辑商品图片"
:visible.sync="visible"
width="800px"
:close-on-click-modal="false"
@close="handleClose"
>
<div class="edit-tip">
正在编辑商品: <strong>{{ formData.name }}</strong>
(条码: {{ formData.barcode || '无条码' }})
</div>
<el-form ref="editForm" :model="formData" label-width="100px" label-position="right">
<!-- 主图编辑 -->
<!-- <el-form-item label="当前主图">
<el-image
v-if="formData.thumb"
:src="formData.thumb"
style="width: 100px; height: 100px; object-fit: cover; border: 1px solid #eee;"
:preview-src-list="[formData.thumb, ...getSubImages(formData.product_image_list)]"
/>
<span v-else class="text-gray-500">暂无主图</span>
</el-form-item> -->
<el-form-item
label="上传新主图"
prop="newThumb"
:rules="[
{ required: true, message: '请上传新的商品主图', trigger: 'change' }
]"
>
<upload
height="100px"
:image="formData.newThumb"
width="100px"
@upImage="handleMainImageUpload"
/>
<div class="el-form-item__error" style="padding-top: 10px;">
<span>支持 JPG, PNG 格式建议尺寸 800x800px</span>
</div>
</el-form-item>
<!-- 副图编辑 -->
<!-- <el-form-item label="商品副图">
<div class="sub-images-container">
<div v-for="(item, index) in formData.product_image_list" :key="item.id || index" class="sub-image-item">
<upload
v-if="item.uploadNew"
height="80px"
:image="item.newImageUrl || item.imageUrl"
width="80px"
@upImage="(url) => handleSubImageUpload(url, index)"
/>
<el-image
v-else
:src="item.imageUrl"
style="width: 80px; height: 80px; object-fit: cover; border: 1px solid #eee;"
:preview-src-list="[formData.thumb, ...getSubImages(formData.product_image_list)]"
:preview-index="index + 1"
/>
<div class="sub-img-actions">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="toggleEditSubImage(index)"
/>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="deleteSubImage(index)"
/>
</div>
</div>
<div class="add-sub-image" @click="addSubImage">
<i class="el-icon-plus" />
<span>添加副图</span>
</div>
</div>
</el-form-item> -->
</el-form>
<template #footer>
<el-button @click="handleClose">{{ __('取消') }}</el-button>
<el-button type="primary" @click="handleSubmit">{{ __('提交修改') }}</el-button>
</template>
</el-dialog>
</template>
<script>
import upload from '@/components/upload'
import { URL } from '@/config'
import { saveBatchBarcode } from '@/api/goodsTool';
export default {
name: 'ImgEdit',
components: {
upload
},
data() {
return {
visible: false,
formData: {
id: '',
name: '',
barcode: '', //
thumb: '',
newThumb: '',
product_image_list: [],
original_product_image_list: [], //
original_thumb: '' //
},
}
},
methods: {
// URL
getSubImages(imageList) {
return (imageList || []).map(item => item.imageUrl).filter(url => url);
},
// open
open(row) {
if (!row || !row.id) {
this.$message.error('无效的商品数据');
return;
}
//
const originalProductImages = JSON.parse(JSON.stringify(row.product_image_list || []));
const originalThumb = row.thumb || '';
// barcode
this.formData = {
id: row.id,
name: row.name || '未知名称',
barcode: row.barcode || '', //
thumb: originalThumb,
newThumb: originalThumb,
original_thumb: originalThumb,
product_image_list: originalProductImages.map(item => ({
...item,
uploadNew: false,
newImageUrl: ''
})),
original_product_image_list: originalProductImages
};
this.visible = true;
this.$nextTick(() => {
this.$refs.editForm?.clearValidate();
});
},
handleClose() {
this.visible = false;
this.formData = {
id: '',
name: '',
thumb: '',
newThumb: '',
product_image_list: [],
original_product_image_list: [],
original_thumb: ''
};
this.$refs.editForm?.clearValidate();
},
//
handleMainImageUpload(imageUrl) {
this.formData.newThumb = imageUrl;
},
//
handleSubImageUpload(imageUrl, index) {
if (this.formData.product_image_list[index]) {
const item = this.formData.product_image_list[index];
item.newImageUrl = imageUrl;
item.imageUrl = imageUrl; // URL
item.uploadNew = false; // 退
item.tmpUrl = ''; // URL
}
},
//
toggleEditSubImage(idx) {
const item = this.formData.product_image_list[idx];
if (item.uploadNew) {
// URL
if (item.tmpUrl) {
item.imageUrl = item.tmpUrl;
item.tmpUrl = ''; // URL
}
item.newImageUrl = '';
} else {
// URL
item.tmpUrl = item.imageUrl;
}
item.uploadNew = !item.uploadNew;
},
//
deleteSubImage(index) {
this.formData.product_image_list.splice(index, 1);
},
addSubImage() {
const max = this.formData.product_image_list.reduce((p, c) => Math.max(p, c.seq || 0), 0);
this.formData.product_image_list.push({
id: null,
productId: this.formData.id,
imageUrl: '',
newImageUrl: '',
uploadNew: true, //
tmpUrl: '', // URL
isMain: false,
seq: max + 1,
status: 1
});
},
async handleSubmit() {
try {
await this.$refs.editForm.validate();
// - 使
const submitData = [
{
id: this.formData.id,
name: this.formData.name,
// 使
thumb: this.formData.newThumb || this.formData.original_thumb,
// 使使
product_image_list: this.hasSubImagesChanged()
? this.formData.product_image_list.filter(item => item.imageUrl).map((item, index) => ({
id: item.id,
productId: this.formData.id,
imageUrl: item.imageUrl,
isMain: false,
seq: index + 1,
status: 1
}))
: this.formData.original_product_image_list
}
];
console.log('提交更新商品图片请求:', submitData);
const response = await saveBatchBarcode(submitData);
if (response.status === 200) {
this.$message.success('商品图片修改成功!');
this.handleClose();
this.$emit('success');
} else {
this.$message.error('修改失败: ' + (response.msg || '未知错误'));
}
} catch (error) {
console.error('提交修改失败:', error);
if (error.name !== 'ValidateError') {
this.$message.error('修改失败,请稍后重试');
}
}
},
//
hasSubImagesChanged() {
//
if (this.formData.product_image_list.length !== this.formData.original_product_image_list.length) {
return true;
}
//
for (let i = 0; i < this.formData.product_image_list.length; i++) {
const newItem = this.formData.product_image_list[i];
const originalItem = this.formData.original_product_image_list[i];
if (!originalItem || newItem.imageUrl !== originalItem.imageUrl) {
return true;
}
}
return false;
}
}
}
</script>
<style lang="scss" scoped>
.edit-tip {
color: #666;
padding: 8px 12px;
background-color: #f5f7fa;
border-radius: 4px;
margin-bottom: 16px;
font-size: 13px;
}
.sub-images-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
padding: 10px 0;
.sub-image-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
.sub-img-actions {
display: flex;
gap: 5px;
.el-button {
color: #666;
padding: 0;
&:hover {
color: #409eff;
}
}
.el-button:nth-child(2):hover {
color: #f56c6c;
}
}
}
.add-sub-image {
width: 80px;
height: 80px;
border: 1px dashed #ccc;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
color: #999;
.el-icon-plus {
font-size: 20px;
margin-bottom: 5px;
}
&:hover {
border-color: #409eff;
color: #409eff;
}
}
}
::v-deep .el-form-item {
margin-bottom: 20px;
}
</style>