商品前台交互更新

This commit is contained in:
SjS 2025-06-04 18:13:16 +08:00
parent ff1a8c2fca
commit 080f6d8fba
3 changed files with 172 additions and 34 deletions

View File

@ -44,9 +44,9 @@ export function delGoods(id) {
}
// 商品上架/下架
export function changeStatus(data) {
export function changeStatusAndSort(data) {
return request({
url: '/bst/goods/changeStatus',
url: '/bst/goods/changeStatusAndSort',
method: 'put',
data: data
})

View File

@ -199,7 +199,7 @@
type="primary"
size="small"
@click="applyBatchSetting"
:disabled="!batchField || !batchValue || selectedSkuRows.length === 0"
:disabled="isReadOnly || !batchField || !batchValue || selectedSkuRows.length === 0"
>
应用设置
</el-button>
@ -210,8 +210,12 @@
</div>
<div class="sku-table">
<el-table :data="form.skuList" style="width: 85%" :row-style="{height: '100px'}"
@selection-change="handleSelectionChange"
<el-table
ref="skuTable"
:data="form.skuList"
style="width: 85%"
:row-style="{height: '100px'}"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<!-- 动态生成规格列 -->
@ -449,6 +453,9 @@ export default {
});
this.$message.success(`已批量设置${this.selectedSkuRows.length}个值`);
this.selectedSkuRows = [];
this.batchValue = '';
this.$refs.skuTable.clearSelection();
},
getList() {
this.loading = true;
@ -627,8 +634,7 @@ export default {
// 使
return Date.now().toString();
}
},
generateSKU() {
},generateSKU() {
// ID
const currentValueIds = new Set();
this.form.specs.forEach(spec => {
@ -654,17 +660,15 @@ export default {
const validSpecValues = (sku.specValues || sku.specs || []).filter(id =>
currentValueIds.has(id)
);
const specKey = [...validSpecValues].sort().join('##');
const specKey = validSpecValues.join('##');
validSkuMap.set(specKey, sku);
});
// SKU
// SKU使
this.form.skuList = combinations.map(comb => {
// ID
const sortedValueIds = comb
.map(item => item.id)
.sort();
const specKey = sortedValueIds.join('##');
// ID
const specIds = comb.map(item => item.id);
const specKey = specIds.join('##');
// SKU
const matchedSku = validSkuMap.get(specKey);
@ -673,28 +677,29 @@ export default {
// SKUID
return {
...matchedSku,
// ID
specs: sortedValueIds,
specValues: sortedValueIds
specs: specIds,
specValues: specIds
};
} else {
// SKU
return {
id: undefined, // SKUID
specs: sortedValueIds,
specValues: sortedValueIds,
specs: specIds,
specValues: specIds,
price: 0,
stock: "0",
image: ''
};
}
});
// 使
const oldSelectedIds = this.selectedSkuRows.map(row =>
row.specs ? row.specs.sort().join('##') : ''
row.specs ? row.specs.join('##') : ''
);
this.selectedSkuRows = this.form.skuList.filter(row => {
const rowKey = row.specs ? row.specs.sort().join('##') : '';
const rowKey = row.specs ? row.specs.join('##') : '';
return oldSelectedIds.includes(rowKey);
});
},

View File

@ -1,7 +1,19 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<div class="app-container">
<div v-if="showSortEditPanel" class="sort-edit-panel" :style="panelStyle">
<el-input
v-model="editSortValue"
size="mini"
@keyup.enter.native="saveSort"
ref="sortInput"
></el-input>
<div class="sort-edit-buttons">
<el-button size="mini" @click="cancelEditSort">取消</el-button>
<el-button type="primary" size="mini" @click="saveSort">确定</el-button>
</div>
</div>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品名称" prop="name">
<el-input
v-model="queryParams.name"
@ -119,6 +131,17 @@
<template v-else-if="column.key === 'status'">
<dict-tag :value="d.row.status" :options="dict.type.goods_status"/>
</template>
<template v-else-if="column.key === 'sort'">
<div>
{{ d.row.sort }}
<el-button
type="text"
icon="el-icon-edit"
size="mini"
@click="startEditSort(d.row, $event)">
</el-button>
</div>
</template>
<template v-else-if="column.key === 'deposit'">
<dict-tag :value="d.row.deposit" :options="dict.type.goods_deposit"/>
</template>
@ -135,7 +158,7 @@
size="mini"
type="text"
icon="el-icon-top"
@click="handleUpdate(scope.row)"
@click="handleOnline(scope.row)"
v-has-permi="['bst:goods:edit']"
>上架</el-button>
<el-button
@ -143,7 +166,7 @@
size="mini"
type="text"
icon="el-icon-bottom"
@click="handleUpdate(scope.row)"
@click="handleOffline(scope.row)"
v-has-permi="['bst:goods:edit']"
>下架</el-button>
<el-button
@ -183,14 +206,14 @@
</template>
<script>
import {listGoods, getGoods, delGoods, addGoods, updateGoods, changeStatus} from "@/api/bst/goods";
import {listGoods, getGoods, delGoods, addGoods, updateGoods, changeStatus, changeStatusAndSort} from "@/api/bst/goods";
import { $showColumns } from '@/utils/mixins';
import FormCol from "@/components/FormCol/index.vue";
//
const defaultSort = {
prop: "createTime",
order: "descending"
prop: "sort",
order: "ascending"
}
export default {
@ -200,6 +223,10 @@ export default {
dicts: ['goods_deposit','goods_status'],
data() {
return {
showSortEditPanel: false, //
editSortValue: null, //
editingRow: null, //
panelPosition: { top: 0, left: 0 }, //
span: 24,
//
columns: [
@ -211,7 +238,7 @@ export default {
// {key: 'virtualSales', visible: true, label: '', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'deposit', visible: true, label: '是否可存', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'status', visible: true, label: '商品状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'sort', visible: true, label: '排序', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'sort', visible: true, label: '排序', minWidth: null, sortable: 'custom', overflow: false, align: 'center', width: '120'},
{key: 'createTime', visible: true, label: '创建时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
//{key: 'storeId', visible: true, label: '', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
//{key: 'categoryId', visible: true, label: '', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
@ -284,19 +311,78 @@ export default {
activated() {
this.getList();
},
methods: {
mounted() {
window.addEventListener('scroll', this.handleScroll);
},
beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll);
},
methods: {startEditSort(row, event) {
this.showSortEditPanel = false;
this.editingRow = row;
this.editSortValue = row.sort;
this.$nextTick(() => {
//
const buttonRect = event.currentTarget.getBoundingClientRect();
// 10px
this.panelStyle = {
position: 'fixed',
top: `${buttonRect.bottom + 10}px`,
left: `${buttonRect.left}px`,
transform: 'translateX(-50%)',
zIndex: 2000
};
this.showSortEditPanel = true;
this.$nextTick(() => {
this.$refs.sortInput.focus();
});
});
},
//
cancelEditSort() {
this.showSortEditPanel = false;
this.editingRow = null;
},
//
saveSort() {
if (!this.editingRow) return;
//
const sortValue = parseInt(this.editSortValue);
if (isNaN(sortValue)) {
this.$message.error('排序值必须是数字');
return;
}
// API
changeStatusAndSort({ id: this.editingRow.id, sort: sortValue })
.then(response => {
this.$modal.msgSuccess("排序更新成功");
//
this.editingRow.sort = sortValue;
this.cancelEditSort();
})
.catch(error => {
this.$modal.msgError("更新失败: " + (error.message || error));
});
this.getList()
},
//
handleOnline(row) {
this.changeGoodsStatus(row, '1', '上架');
this.changeStatusAndSort(row, '1', '上架');
},
//
handleOffline(row) {
this.changeGoodsStatus(row, '0', '下架');
this.changeStatusAndSort(row, '0', '下架');
},
//
async changeGoodsStatus(row, status, actionName) {
async changeStatusAndSort(row, status, actionName) {
try {
//
const confirmMsg = `是否确认${actionName}商品【${row.name}】?`;
@ -309,7 +395,7 @@ export default {
};
//
await changeStatus(updateData);
await changeStatusAndSort(updateData);
//
this.$modal.msgSuccess(`${actionName}成功`);
@ -345,6 +431,10 @@ export default {
getList() {
this.loading = true;
listGoods(this.queryParams).then(response => {
response.rows.forEach(item => {
//
this.$set(item, 'isEditingSort', false)
})
this.goodsList = response.rows;
this.total = response.total;
this.loading = false;
@ -443,3 +533,46 @@ export default {
}
};
</script>
<style scoped>
/* 新增排序编辑面板样式 */
.sort-edit-panel {
position: absolute;
display: flex;
align-items: center;
gap: 10px;
background: white;
padding: 8px 12px;
border: 1px solid #ebeef5;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 2000;
}
.sort-edit-panel .el-input {
width: 80px;
}
.sort-edit-buttons {
display: flex;
gap: 5px;
}
/* 确保容器有相对定位 */
.app-container {
position: relative;
}
.sort-edit-panel {
position: fixed; /* 改为固定定位 */
display: flex;
align-items: center;
gap: 10px;
background: white;
padding: 8px 12px;
border: 1px solid #ebeef5;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 2000;
min-width: 150px;
}
</style>