404 lines
11 KiB
Vue
404 lines
11 KiB
Vue
<template>
|
|
<div class="imgs-container">
|
|
<div class="filter">
|
|
<el-input
|
|
v-model="filter.name"
|
|
class="input_item"
|
|
clearable
|
|
placeholder="请输入商品名字"
|
|
prefix-icon="el-icon-search"
|
|
/>
|
|
<el-input
|
|
v-model="filter.barcode"
|
|
class="input_item"
|
|
clearable
|
|
placeholder="请输入商品条码"
|
|
prefix-icon="el-icon-barcode"
|
|
/>
|
|
<el-select
|
|
v-model="filter.barcodeEmty"
|
|
clearable
|
|
placeholder="条码是否为空"
|
|
class="input_item"
|
|
>
|
|
<el-option label="无条码" value="yes" />
|
|
<el-option label="有条码" value="no" />
|
|
</el-select>
|
|
<el-button size="medium" type="primary" @click="handleSearch">
|
|
查询
|
|
</el-button>
|
|
<el-button size="medium" @click="handleReset">重置</el-button>
|
|
<el-button size="medium" type="warning" @click="handleBatchEdit">
|
|
批量编辑
|
|
</el-button>
|
|
</div>
|
|
<div class="list">
|
|
<el-table
|
|
ref="imgTable"
|
|
:data="tableData"
|
|
style="width: 100%"
|
|
border
|
|
stripe
|
|
:empty-text="tableData.length === 0 ? '暂无商品数据' : '加载中...'"
|
|
@selection-change="handleSelectionChange"
|
|
>
|
|
<el-table-column type="selection" width="55" />
|
|
<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"
|
|
type="success"
|
|
@click="handleSaveSingle(scope.row)"
|
|
:loading="scope.row.saveLoading"
|
|
>
|
|
保存条码
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="商品条码" width="180">
|
|
<template #default="scope">
|
|
<el-input
|
|
v-model="scope.row.barcode"
|
|
placeholder="请输入商品条码"
|
|
clearable
|
|
size="small"
|
|
class="barcode-input"
|
|
:disabled="scope.row.saveLoading"
|
|
@change="handleBarcodeChange(scope.row)"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="商品主图" width="180px">
|
|
<template #default="scope">
|
|
<div v-if="scope.row.thumb" class="img-group">
|
|
<el-image
|
|
:src="scope.row.thumb"
|
|
:preview-src-list="[scope.row.thumb]"
|
|
class="img-thumb"
|
|
fit="cover"
|
|
:title="scope.row.name || '商品主图'"
|
|
/>
|
|
</div>
|
|
<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">
|
|
{{ scope.row.name }}
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="商品id" prop="id" width="120px" />
|
|
<el-table-column label="分类" prop="category" width="120" />
|
|
<el-table-column label="价格" width="120">
|
|
<template #default="scope">
|
|
{{ Number(scope.row.price).toFixed(2) }} 元
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="创建时间" prop="createdAt" width="180" />
|
|
<el-table-column label="更新时间" prop="updatedAt" width="180" />
|
|
</el-table>
|
|
|
|
<el-pagination
|
|
background
|
|
:current-page="pagination.pageNum"
|
|
:page-size="pagination.pageSize"
|
|
:total="pagination.total"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@current-change="handleCurrentChange"
|
|
@size-change="handleSizeChange"
|
|
:disabled="pagination.total === 0"
|
|
/>
|
|
</div>
|
|
<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: [],
|
|
pagination: {
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
total: 0,
|
|
pages: 0,
|
|
},
|
|
filter: {
|
|
name: '',
|
|
barcode: '',
|
|
barcodeEmty: '', // 条码是否为空: 'yes'无条码, 'no'有条码
|
|
},
|
|
multipleSelection: [],
|
|
imgEditRef: null,
|
|
batchEditBarcodeRef: null,
|
|
}
|
|
},
|
|
mounted() {
|
|
this.handleImgList()
|
|
},
|
|
methods: {
|
|
async handleImgList() {
|
|
try {
|
|
const params = {
|
|
pageNum: this.pagination.pageNum,
|
|
pageSize: this.pagination.pageSize,
|
|
...(this.filter.name && { name: this.filter.name }),
|
|
...(this.filter.barcode && { barcode: this.filter.barcode }),
|
|
...(this.filter.barcodeEmty && {
|
|
barcodeEmty: this.filter.barcodeEmty,
|
|
}),
|
|
}
|
|
|
|
const res = await GoodsToolApi.getImgList(params)
|
|
|
|
this.tableData = (res.records || []).map(item => ({
|
|
...item,
|
|
saveLoading: false,
|
|
originalBarcode: item.barcode,
|
|
}))
|
|
this.pagination.total = res.total || 0
|
|
this.pagination.pages = res.pages || 0
|
|
this.pagination.current = res.current || 1
|
|
|
|
} catch (error) {
|
|
this.$message.error('获取数据失败,请重试')
|
|
this.tableData = []
|
|
}
|
|
},
|
|
|
|
// 查询
|
|
handleSearch() {
|
|
this.pagination.pageNum = 1
|
|
this.handleImgList()
|
|
},
|
|
|
|
// 重置查询条件
|
|
handleReset() {
|
|
this.filter = {
|
|
name: '',
|
|
barcode: '',
|
|
barcodeEmty: '',
|
|
}
|
|
this.pagination.pageNum = 1
|
|
this.handleImgList()
|
|
},
|
|
|
|
// 分页切换
|
|
handleCurrentChange(pageNum) {
|
|
this.pagination.pageNum = pageNum
|
|
this.handleImgList()
|
|
},
|
|
|
|
handleSizeChange(pageSize) {
|
|
this.pagination.pageSize = pageSize
|
|
this.pagination.pageNum = 1
|
|
this.handleImgList()
|
|
},
|
|
|
|
// 表格选中
|
|
handleSelectionChange(val) {
|
|
this.multipleSelection = val
|
|
},
|
|
|
|
// 批量编辑
|
|
handleBatchEdit() {
|
|
if (this.multipleSelection.length === 0) {
|
|
this.$message.warning('请先选中需要编辑的商品');
|
|
return;
|
|
}
|
|
this.$refs.batchEditBarcodeRef.open(this.multipleSelection);
|
|
},
|
|
|
|
// 单条保存(提交当前行条码修改)
|
|
async handleSaveSingle(row) {
|
|
// 条码未修改则不提交
|
|
if (row.barcode === row.originalBarcode) {
|
|
this.$message.info('商品条码未修改,无需保存');
|
|
return;
|
|
}
|
|
|
|
// 校验:条码格式
|
|
if (row.barcode && row.barcode.length > 50) {
|
|
this.$message.warning('商品条码长度不能超过50个字符');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// 显示加载状态,禁用输入和按钮
|
|
row.saveLoading = true;
|
|
|
|
// 调用接口提交(复用批量编辑接口,单条也按数组格式传参)
|
|
const updateData = [{
|
|
id: row.id,
|
|
barcode: row.barcode || '', // 空值也提交(清除条码)
|
|
}];
|
|
let res=await GoodsToolApi.saveBatchBarcode(updateData);
|
|
if(res.status==200){
|
|
// 保存成功:更新原始条码,提示用户
|
|
row.originalBarcode = row.barcode;
|
|
this.$message.success('商品条码保存成功');
|
|
}else{
|
|
this.$message.error('操作异常');
|
|
}
|
|
} catch (error) {
|
|
// 保存失败:回滚条码,提示错误
|
|
row.barcode = row.originalBarcode;
|
|
this.$message.error('保存失败,请重试');
|
|
console.error('单条保存条码失败:', error);
|
|
} finally {
|
|
// 无论成功失败,关闭加载状态
|
|
row.saveLoading = false;
|
|
}
|
|
},
|
|
|
|
|
|
handleBarcodeChange(row) {
|
|
if (row.barcode && row.barcode.length > 50) {
|
|
this.$message.warning('商品条码长度不能超过50个字符');
|
|
}
|
|
},
|
|
|
|
// 编辑成功后刷新列表(批量编辑成功后调用)
|
|
handleEditSuccess() {
|
|
this.handleImgList();
|
|
this.$refs.imgTable?.clearSelection();
|
|
this.multipleSelection = [];
|
|
},
|
|
|
|
handleEdit(row) {
|
|
this.$refs.imgEditRef?.open(row);
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.filter {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 15px 20px;
|
|
margin-bottom: 15px;
|
|
gap: 10px;
|
|
border-radius: 5px;
|
|
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
|
|
background: #fff;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.input_item {
|
|
width: 200px;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
.img-group {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
.img-thumb {
|
|
width: 50px;
|
|
height: 50px;
|
|
cursor: pointer;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.text-ellipsis {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.no-data {
|
|
color: #999;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.barcode-input {
|
|
width: 100%;
|
|
}
|
|
|
|
::v-deep .el-table__cell {
|
|
vertical-align: middle !important;
|
|
}
|
|
|
|
::v-deep .el-pagination {
|
|
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> |