【商城实战(20)】商品管理功能深化实战
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想深入钻研技术细节,还是探寻商城运营之道,本专栏都能提供从 0 到 1 的系统讲解,助力你打造独具竞争力的电商平台,开启电商实战之旅。
目录
- 一、商品批量上架、下架功能实现
- 1.1 前端交互设计
- 1.2 后端接口开发(SpringBoot)
- 1.3 数据库操作(Mybatis-plus)
- 1.4 完整源码展示与分析
- 二、商品库存管理页面开发
- 2.1 库存管理页面布局设计
- 2.2 库存预警功能实现
- 2.3 库存盘点功能实现
- 2.4 源码实现与解读
- 三、完善商品信息编辑功能
- 3.1 前端编辑页面优化
- 3.2 后端编辑接口升级
- 3.3 数据库关联更新(Mybatis-plus)
- 3.4 完整代码示例与解析
- 四、总结与展望
一、商品批量上架、下架功能实现
在电商系统中,商品的批量上架与下架操作是提升商品管理效率的关键环节。当商家需要快速调整一批商品的展示状态时,逐个操作不仅耗时费力,还容易出错。批量操作功能能够一次性处理多个商品,大大节省了时间和人力成本,使得商家能够更高效地应对市场变化和业务需求。接下来,我们将详细介绍如何使用 uniapp、Element Plus、SpringBoot 和 Mybatis-plus 实现这一功能。
1.1 前端交互设计
- uniapp 实现:在 uniapp 中,使用checkbox组件实现商品选择,checkbox-group组件用于收集选中的商品。通过v-model指令绑定选中的商品列表,然后添加 “批量上架” 和 “批量下架” 按钮,并绑定对应的点击事件。示例代码如下:
<template><view><checkbox-group v-model="selectedIds"><view v-for="(item, index) in productList" :key="index"><checkbox :value="item.id">{{ item.productName }}</checkbox></view></checkbox-group><button @click="batchOperate('上架')">批量上架</button><button @click="batchOperate('下架')">批量下架</button></view>
</template><script>
export default {data() {return {selectedIds: [],productList: []};},methods: {async batchOperate(status) {if (this.selectedIds.length === 0) {uni.showToast({ title: '请选择商品', icon: 'none' });return;}const res = await uni.request({url: 'http://your-backend-url/batchProduct',method: 'POST',data: {status,productIds: this.selectedIds}});if (res.data.success) {uni.showToast({ title: `批量${status}成功`, icon:'success' });// 刷新商品列表await this.getProductList();} else {uni.showToast({ title: `批量${status}失败`, icon: 'none' });}},async getProductList() {const res = await uni.request({url: 'http://your-backend-url/products',method: 'GET'});this.productList = res.data.data;}},onLoad() {this.getProductList();}
};
</script>
- Element Plus 实现:Element Plus 中,使用el-checkbox和el-checkbox-group组件实现商品选择,同样通过v-model绑定选中的商品 ID 数组。按钮部分使用el-button组件,并绑定点击事件。示例代码如下:
<template><el-container><el-main><el-checkbox-group v-model="selectedIds"><el-checkbox v-for="(item, index) in productList" :key="index" :label="item.id">{{ item.productName }}</el-checkbox></el-checkbox-group><el-button @click="batchOperate('上架')">批量上架</el-button><el-button @click="batchOperate('下架')">批量下架</el-button></el-main></el-container>
</template><script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';const selectedIds = ref([]);
const productList = ref([]);const batchOperate = async (status) => {if (selectedIds.value.length === 0) {alert('请选择商品');return;}try {const res = await axios.post('http://your-backend-url/batchProduct', {status,productIds: selectedIds.value});if (res.data.success) {alert(`批量${status}成功`);await getProductList();} else {alert(`批量${status}失败`);}} catch (error) {console.error('操作失败', error);alert(`批量${status}失败`);}
};const getProductList = async () => {const res = await axios.get('http://your-backend-url/products');productList.value = res.data.data;
};onMounted(() => {getProductList();
});
</script>
1.2 后端接口开发(SpringBoot)
在 SpringBoot 中,创建一个 Controller 来接收前端传递的批量操作请求。通过@RequestBody注解获取前端发送的参数,包括操作状态(上架或下架)和商品 ID 数组。然后调用 Service 层方法来处理业务逻辑。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
public class ProductController {@Autowiredprivate ProductService productService;@PostMapping("/batchProduct")public Result batchProduct(@RequestBody BatchProductDTO batchProductDTO) {try {productService.batchUpdateProductStatus(batchProductDTO.getStatus(), batchProductDTO.getProductIds());return Result.success("操作成功");} catch (Exception e) {e.printStackTrace();return Result.error("操作失败");}}
}class BatchProductDTO {private String status;private List<Long> productIds;// 省略getter和setter方法
}class Result {private boolean success;private String message;public static Result success(String message) {Result result = new Result();result.success = true;result.message = message;return result;}public static Result error(String message) {Result result = new Result();result.success = false;result.message = message;return result;}// 省略getter和setter方法
}
1.3 数据库操作(Mybatis-plus)
Mybatis-plus 提供了强大的 CRUD 操作,无需手写 mapper 文件。在 Service 层中,通过LambdaUpdateWrapper来构建更新条件,实现批量更新商品的上下架状态。示例代码如下:
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.util.List;@Service
public class ProductService {@Resourceprivate ProductMapper productMapper;@Transactionalpublic void batchUpdateProductStatus(String status, List<Long> productIds) {LambdaUpdateWrapper<Product> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.in(Product::getId, productIds);updateWrapper.set(Product::getStatus, status);productMapper.update(null, updateWrapper);}
}
1.4 完整源码展示与分析
上述代码分别展示了 uniapp、Element Plus 前端以及 SpringBoot 后端和 Mybatis-plus 数据库操作实现商品批量上架、下架功能的关键部分。前端部分通过checkbox组件实现商品选择,点击按钮触发请求,将选中的商品 ID 和操作状态发送到后端。后端 Controller 接收请求后,调用 Service 层方法,Service 层利用 Mybatis-plus 的LambdaUpdateWrapper构建更新条件,实现对数据库中商品状态的批量更新。这样,通过前后端的协同工作,高效地实现了商品批量上架、下架功能,提升了商品管理的效率。
二、商品库存管理页面开发
商品库存管理是商城运营的核心环节之一,它直接关系到商品的供应与销售。准确的库存管理能够避免缺货现象,提升客户满意度,同时防止库存积压,减少资金占用。开发支持库存预警和库存盘点功能的页面,有助于商家及时掌握库存动态,做出合理的采购与销售决策,保障商城的稳定运营。
2.1 库存管理页面布局设计
- uniapp 实现:使用view组件构建页面布局,u-table组件展示库存数据表格,每列展示商品 ID、名称、库存数量、预警阈值等信息。在表格列中添加操作按钮,如盘点按钮,绑定点击事件。示例代码如下:
<template><view><u-table :columns="columns" :data="stockList"><template #default="{ row }"><view :key="row.id" class="u-table-cell">{{ row.productId }}</view><view :key="row.id + 'name'" class="u-table-cell">{{ row.productName }}</view><view :key="row.id + 'quantity'" class="u-table-cell">{{ row.stockQuantity }}</view><view :key="row.id + 'threshold'" class="u-table-cell">{{ row.threshold }}</view><view :key="row.id + 'operation'" class="u-table-cell"><button @click="startStockTaking(row.id)">盘点</button></view></template></u-table></view>
</template><script>
export default {data() {return {columns: [{ title: '商品ID' },{ title: '商品名称' },{ title: '库存数量' },{ title: '预警阈值' },{ title: '操作' }],stockList: []};},methods: {async getStockList() {const res = await uni.request({url: 'http://your-backend-url/stockList',method: 'GET'});this.stockList = res.data.data;},async startStockTaking(productId) {// 发起盘点请求逻辑}},onLoad() {this.getStockList();}
};
</script>
- Element Plus 实现:通过el-table组件展示库存数据,el-table-column定义每列内容。操作按钮使用el-button,并绑定点击事件。示例代码如下:
<template><el-container><el-main><el-table :data="stockList" border><el-table-column prop="productId" label="商品ID"></el-table-column><el-table-column prop="productName" label="商品名称"></el-table-column><el-table-column prop="stockQuantity" label="库存数量"></el-table-column><el-table-column prop="threshold" label="预警阈值"></el-table-column><el-table-column label="操作"><template #default="scope"><el-button @click="startStockTaking(scope.row.productId)">盘点</el-button></template></el-table-column></el-table></el-main></el-container>
</template><script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';const stockList = ref([]);const getStockList = async () => {const res = await axios.get('http://your-backend-url/stockList');stockList.value = res.data.data;
};const startStockTaking = async (productId) => {// 发起盘点请求逻辑
};onMounted(() => {getStockList();
});
</script>
2.2 库存预警功能实现
- 后端实现:在 SpringBoot 的 Service 层中,通过LambdaQueryWrapper查询库存数量小于预警阈值的商品。当查询到符合条件的商品时,将预警信息封装成对象返回给前端。示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;@Service
public class StockService {@Resourceprivate StockMapper stockMapper;public List<Stock> getLowStockProducts() {LambdaQueryWrapper<Stock> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.lt(Stock::getStockQuantity, Stock::getThreshold);return stockMapper.selectList(queryWrapper);}
}
- 前端实现:在 uniapp 或 Element Plus 前端页面,通过定时器定时调用后端接口获取预警信息。当接收到预警信息后,在页面上以红色字体或特殊图标标识预警商品。在 uniapp 中,可以在u-table组件的对应列中根据数据判断添加样式;在 Element Plus 中,在el-table-column的default插槽中根据数据判断添加样式。示例代码(以 Element Plus 为例):
<template><el-container><el-main><el-table :data="stockList" border><el-table-column prop="productId" label="商品ID"></el-table-column><el-table-column prop="productName" label="商品名称"><template #default="scope"><span :class="scope.row.stockQuantity < scope.row.threshold? 'warning-text' : ''">{{ scope.row.productName }}</span></template></el-table-column><el-table-column prop="stockQuantity" label="库存数量"></el-table-column><el-table-column prop="threshold" label="预警阈值"></el-table-column><el-table-column label="操作"><template #default="scope"><el-button @click="startStockTaking(scope.row.productId)">盘点</el-button></template></el-table-column></el-table></el-main></el-container>
</template><script setup>
import { ref, onMounted, setInterval } from 'vue';
import axios from 'axios';const stockList = ref([]);const getStockList = async () => {const res = await axios.get('http://your-backend-url/stockList');stockList.value = res.data.data;
};const startStockTaking = async (productId) => {// 发起盘点请求逻辑
};onMounted(() => {getStockList();setInterval(getStockList, 60 * 1000); // 每分钟获取一次预警信息
});
</script><style scoped>
.warning-text {color: red;
}
</style>
2.3 库存盘点功能实现
- 后端实现:在 SpringBoot 的 Controller 中接收前端传递的盘点数据,包括商品 ID 和实际盘点数量。调用 Service 层方法更新数据库中的库存数据。在 Service 层中,通过LambdaUpdateWrapper构建更新条件,实现库存数据的更新。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class StockController {@Autowiredprivate StockService stockService;@PostMapping("/stockTaking")public Result stockTaking(@RequestBody StockTakingDTO stockTakingDTO) {try {stockService.updateStock(stockTakingDTO.getProductId(), stockTakingDTO.getActualQuantity());return Result.success("盘点成功");} catch (Exception e) {e.printStackTrace();return Result.error("盘点失败");}}
}class StockTakingDTO {private Long productId;private Integer actualQuantity;// 省略getter和setter方法
}import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service
public class StockService {@Resourceprivate StockMapper stockMapper;@Transactionalpublic void updateStock(Long productId, Integer actualQuantity) {LambdaUpdateWrapper<Stock> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(Stock::getProductId, productId);updateWrapper.set(Stock::getStockQuantity, actualQuantity);stockMapper.update(null, updateWrapper);}
}
- 前端实现:当用户点击盘点按钮时,弹出盘点弹窗。在弹窗中,用户输入实际盘点数量,点击确认按钮后,将盘点数据发送到后端。在 uniapp 中,使用uni.showModal弹出确认框,获取用户输入后发送请求;在 Element Plus 中,使用el-dialog组件作为弹窗,通过表单获取用户输入,点击按钮时发送请求。示例代码(以 Element Plus 为例):
<template><el-container><el-main><el-table :data="stockList" border><el-table-column prop="productId" label="商品ID"></el-table-column><el-table-column prop="productName" label="商品名称"></el-table-column><el-table-column prop="stockQuantity" label="库存数量"></el-table-column><el-table-column prop="threshold" label="预警阈值"></el-table-column><el-table-column label="操作"><template #default="scope"><el-button @click="showStockTakingDialog(scope.row.productId)">盘点</el-button></template></el-table-column></el-table><el-dialog title="库存盘点" :visible.sync="stockTakingDialogVisible" width="30%"><el-form :model="stockTakingForm" label-width="80px"><el-form-item label="实际数量"><el-input v-model="stockTakingForm.actualQuantity" type="number"></el-input></el-form-item></el-form><template #footer><el-button @click="stockTakingDialogVisible = false">取消</el-button><el-button type="primary" @click="confirmStockTaking">确认</el-button></template></el-dialog></el-main></el-container>
</template><script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';const stockList = ref([]);
const stockTakingDialogVisible = ref(false);
const stockTakingForm = ref({productId: null,actualQuantity: null
});const getStockList = async () => {const res = await axios.get('http://your-backend-url/stockList');stockList.value = res.data.data;
};const showStockTakingDialog = (productId) => {stockTakingForm.value.productId = productId;stockTakingDialogVisible.value = true;
};const confirmStockTaking = async () => {try {const res = await axios.post('http://your-backend-url/stockTaking', stockTakingForm.value);if (res.data.success) {stockTakingDialogVisible.value = false;await getStockList();} else {alert('盘点失败');}} catch (error) {console.error('盘点失败', error);alert('盘点失败');}
};onMounted(() => {getStockList();
});
</script>
2.4 源码实现与解读
上述代码展示了库存管理页面前后端实现的关键部分。前端通过表格展示库存数据,实现了库存预警标识和库存盘点操作的交互。后端通过 SpringBoot 和 Mybatis-plus 实现了数据的查询、更新等业务逻辑。在整个实现过程中,前后端通过 HTTP 请求进行数据交互,确保了库存管理功能的正常运行。通过对这些源码的解读,可以清晰地了解库存管理功能的实现思路和技术细节,为进一步优化和扩展功能提供了基础。
三、完善商品信息编辑功能
在商城系统中,完善商品信息编辑功能是实现精细化商品管理的重要一环。随着业务的发展,商品的属性和关联信息可能会发生变化,例如商品的分类调整、品牌更换等。一个完善的商品信息编辑功能能够满足这些业务需求,确保商品数据的准确性和完整性,提升用户体验和商城的运营效率。接下来,我们将详细介绍如何使用 uniapp、Element Plus、SpringBoot 和 Mybatis-plus 来完善这一功能。
3.1 前端编辑页面优化
- uniapp 实现:在 uniapp 中,使用u-form组件搭建商品信息编辑表单。对于商品分类和品牌选择,使用u-picker组件实现下拉选择功能。通过v-model指令绑定表单数据,实现数据的双向绑定。示例代码如下:
<template><view><u-form :model="productForm" ref="productFormRef"><u-form-item label="商品名称"><u-input v-model="productForm.productName"></u-input></u-form-item><u-form-item label="商品分类"><u-picker :columns="categoryOptions" @change="handleCategoryChange" v-model="productForm.categoryId"></u-picker></u-form-item><u-form-item label="商品品牌"><u-picker :columns="brandOptions" @change="handleBrandChange" v-model="productForm.brandId"></u-picker></u-form-item><!-- 其他商品信息字段 --><button @click="submitEdit">提交编辑</button></u-form></view>
</template><script>
export default {data() {return {productForm: {productName: '',categoryId: '',brandId: ''},categoryOptions: [],brandOptions: []};},methods: {async getCategoryOptions() {const res = await uni.request({url: 'http://your-backend-url/categories',method: 'GET'});this.categoryOptions = res.data.data.map(item => ({ label: item.categoryName, value: item.id }));},async getBrandOptions() {const res = await uni.request({url: 'http://your-backend-url/brands',method: 'GET'});this.brandOptions = res.data.data.map(item => ({ label: item.brandName, value: item.id }));},handleCategoryChange(e) {this.productForm.categoryId = e.detail.value;},handleBrandChange(e) {this.productForm.brandId = e.detail.value;},async submitEdit() {await this.$refs.productFormRef.validate();const res = await uni.request({url: 'http://your-backend-url/products/' + this.productForm.id,method: 'PUT',data: this.productForm});if (res.data.success) {uni.showToast({ title: '编辑成功', icon:'success' });// 刷新商品列表或返回上一页} else {uni.showToast({ title: '编辑失败', icon: 'none' });}}},onLoad() {this.getCategoryOptions();this.getBrandOptions();// 从路由参数获取商品ID,查询商品信息并填充表单const productId = this.$route.query.productId;if (productId) {this.getProductDetail(productId);}},async getProductDetail(productId) {const res = await uni.request({url: 'http://your-backend-url/products/' + productId,method: 'GET'});this.productForm = res.data.data;}
};
</script>
- Element Plus 实现:利用el-form组件创建商品信息编辑表单,el-select组件实现商品分类和品牌的下拉选择。同样通过v-model绑定数据,实现数据的实时更新。示例代码如下:
<template><el-container><el-main><el-form :model="productForm" ref="productFormRef" label-width="120px"><el-form-item label="商品名称"><el-input v-model="productForm.productName"></el-input></el-form-item><el-form-item label="商品分类"><el-select v-model="productForm.categoryId" placeholder="请选择分类"><el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option></el-select></el-form-item><el-form-item label="商品品牌"><el-select v-model="productForm.brandId" placeholder="请选择品牌"><el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id"></el-option></el-select></el-form-item><!-- 其他商品信息字段 --><el-form-item><el-button type="primary" @click="submitEdit">提交编辑</el-button></el-form-item></el-form></el-main></el-container>
</template><script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';const productForm = ref({productName: '',categoryId: '',brandId: ''
});
const categoryOptions = ref([]);
const brandOptions = ref([]);const getCategoryOptions = async () => {const res = await axios.get('http://your-backend-url/categories');categoryOptions.value = res.data.data;
};const getBrandOptions = async () => {const res = await axios.get('http://your-backend-url/brands');brandOptions.value = res.data.data;
};const submitEdit = async () => {await productFormRef.value.validate();try {const res = await axios.put('http://your-backend-url/products/' + productForm.value.id, productForm.value);if (res.data.success) {alert('编辑成功');// 刷新商品列表或返回上一页} else {alert('编辑失败');}} catch (error) {console.error('编辑失败', error);alert('编辑失败');}
};onMounted(() => {getCategoryOptions();getBrandOptions();// 从路由参数获取商品ID,查询商品信息并填充表单const productId = new URLSearchParams(window.location.search).get('productId');if (productId) {getProductDetail(productId);}
});const getProductDetail = async (productId) => {const res = await axios.get('http://your-backend-url/products/' + productId);productForm.value = res.data.data;
};
</script>
3.2 后端编辑接口升级
在 SpringBoot 后端,创建一个 Controller 来接收前端传递的商品编辑请求。通过@PathVariable获取商品 ID,@RequestBody获取编辑后的商品数据。在 Service 层中,根据商品 ID 查询原有商品信息,更新需要修改的字段,如商品分类和品牌关联信息,然后调用 Mybatis-plus 的更新方法将数据持久化到数据库。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/products")
public class ProductController {@Autowiredprivate ProductService productService;@PutMapping("/{id}")public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {try {product.setId(id);productService.updateProduct(product);return Result.success("编辑成功");} catch (Exception e) {e.printStackTrace();return Result.error("编辑失败");}}
}import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service
public class ProductService {@Resourceprivate ProductMapper productMapper;@Transactionalpublic void updateProduct(Product product) {// 根据ID查询原有商品信息Product originalProduct = productMapper.selectById(product.getId());// 更新需要修改的字段originalProduct.setProductName(product.getProductName());originalProduct.setCategoryId(product.getCategoryId());originalProduct.setBrandId(product.getBrandId());// 其他字段更新// 更新数据库productMapper.updateById(originalProduct);}
}
3.3 数据库关联更新(Mybatis-plus)
Mybatis-plus 提供了便捷的更新操作,无需编写复杂的 SQL 语句。在上述 Service 层代码中,通过productMapper.updateById(originalProduct)方法即可实现商品信息的更新。如果商品与分类、品牌存在关联关系,并且在数据库设计中通过外键关联,当更新商品的分类 ID 和品牌 ID 时,数据库会自动维护关联关系的一致性。例如,假设商品表product中有category_id和brand_id字段分别关联分类表category和品牌表brand的主键:
<!-- 假设Mybatis-plus的配置文件,虽然不需要手写mapper文件,但这里展示配置结构 -->
<configuration><settings><!-- 开启驼峰命名映射 --><setting name="mapUnderscoreToCamelCase" value="true"/></settings>
</configuration>
在 Java 代码中,当执行productMapper.updateById(originalProduct)时,Mybatis-plus 会根据originalProduct对象中的属性值生成对应的 SQL 语句,实现商品信息及关联字段的更新。例如,生成的 SQL 语句可能类似于:
UPDATE product
SET product_name = #{productName}, category_id = #{categoryId}, brand_id = #{brandId}
WHERE id = #{id}
这样就完成了商品信息及其关联的分类、品牌数据在数据库中的更新操作。
3.4 完整代码示例与解析
- 前端完整代码(以 Element Plus 为例):
<template><el-container><el-main><el-form :model="productForm" ref="productFormRef" label-width="120px"><el-form-item label="商品名称"><el-input v-model="productForm.productName"></el-input></el-form-item><el-form-item label="商品分类"><el-select v-model="productForm.categoryId" placeholder="请选择分类"><el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option></el-select></el-form-item><el-form-item label="商品品牌"><el-select v-model="productForm.brandId" placeholder="请选择品牌"><el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id"></el-option></el-select></el-form-item><el-form-item label="商品价格"><el-input-number v-model="productForm.price" :precision="2"></el-input-number></el-form-item><el-form-item label="商品库存"><el-input-number v-model="productForm.stock"></el-input-number></el-form-item><el-form-item><el-button type="primary" @click="submitEdit">提交编辑</el-button></el-form-item></el-form></el-main></el-container>
</template><script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';const productForm = ref({id: null,productName: '',categoryId: '',brandId: '',price: 0,stock: 0
});
const categoryOptions = ref([]);
const brandOptions = ref([]);const getCategoryOptions = async () => {const res = await axios.get('http://your-backend-url/categories');categoryOptions.value = res.data.data;
};const getBrandOptions = async () => {const res = await axios.get('http://your-backend-url/brands');brandOptions.value = res.data.data;
};const submitEdit = async () => {await productFormRef.value.validate();try {if (productForm.value.id) {const res = await axios.put('http://your-backend-url/products/' + productForm.value.id, productForm.value);if (res.data.success) {alert('编辑成功');// 刷新商品列表或返回上一页} else {alert('编辑失败');}} else {alert('商品ID为空,无法编辑');}} catch (error) {console.error('编辑失败', error);alert('编辑失败');}
};onMounted(() => {getCategoryOptions();getBrandOptions();const productId = new URLSearchParams(window.location.search).get('productId');if (productId) {getProductDetail(productId);}
});const getProductDetail = async (productId) => {const res = await axios.get('http://your-backend-url/products/' + productId);productForm.value = res.data.data;
};
</script>
- 后端完整代码:
// ProductController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/products")
public class ProductController {@Autowiredprivate ProductService productService;@PutMapping("/{id}")public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {try {product.setId(id);productService.updateProduct(product);return Result.success("编辑成功");} catch (Exception e) {e.printStackTrace();return Result.error("编辑失败");}}
}// ProductService.java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service
public class ProductService {@Resourceprivate ProductMapper productMapper;@Transactionalpublic void updateProduct(Product product) {Product originalProduct = productMapper.selectById(product.getId());originalProduct.setProductName(product.getProductName());originalProduct.setCategoryId(product.getCategoryId());originalProduct.setBrandId(product.getBrandId());originalProduct.setPrice(product.getPrice());originalProduct.setStock(product.getStock());productMapper.updateById(originalProduct);}
}// ProductMapper.java 由Mybatis-plus自动生成,继承BaseMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.entity.Product;@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}// Product.java 商品实体类
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;@Data
public class Product {@TableIdprivate Long id;private String productName;private Long categoryId;private Long brandId;private Double price;private Integer stock;
}// Result.java 返回结果类
class Result {private boolean success;private String message;public static Result success(String message) {Result result = new Result();result.success = true;result.message = message;return result;}public static Result error(String message) {Result result = new Result();result.success = false;result.message = message;return result;}// 省略getter和setter方法
}
- 代码解析:
-
- 前端部分通过el-form和el-select等组件构建了商品信息编辑表单,实现了用户与页面的交互。getCategoryOptions和getBrandOptions方法从后端获取商品分类和品牌数据,填充下拉选择框。submitEdit方法在用户点击提交按钮时,验证表单数据并将编辑后的商品信息发送到后端。
-
- 后端ProductController接收前端的编辑请求,调用ProductService的updateProduct方法处理业务逻辑。ProductService中,先根据商品 ID 查询原有商品信息,然后更新需要修改的字段,最后通过ProductMapper的updateById方法将更新后的数据保存到数据库。
-
- Mybatis-plus 的BaseMapper为ProductMapper提供了基本的 CRUD 操作,使得数据库操作更加便捷高效。通过上述前后端代码的协同工作,实现了完善的商品信息编辑功能,满足了商城对商品精细化管理的需求。
四、总结与展望
通过上述对商品批量上架、下架功能,商品库存管理页面,以及商品信息编辑功能的深化开发,我们显著提升了商城后台商品管理的效率和灵活性。商品批量操作功能极大地减少了商家调整商品展示状态的时间成本,使得商品能够更及时地响应市场需求;库存管理页面的开发实现了库存的可视化管理和预警机制,有效避免了缺货和库存积压问题;完善的商品信息编辑功能则确保了商品数据的准确性和完整性,为商城的精细化运营提供了有力支持。
展望未来,我们可以进一步优化和拓展商品管理功能。在技术层面,持续提升系统性能,引入更先进的缓存技术和数据库优化策略,以应对高并发场景下的商品管理需求;在功能拓展方面,增加商品数据分析功能,通过对商品销售数据、库存数据等的深入挖掘,为商家提供更具针对性的决策支持,如智能补货建议、商品推荐策略等;同时,随着移动端购物的普及,进一步优化 uniapp 前端的用户体验,实现更流畅的操作和更友好的界面设计。此外,考虑到多语言和多地区的业务拓展,商品管理系统应具备良好的国际化支持,以满足不同地区用户的需求。通过不断地优化和创新,我们将为商城的稳定发展和业务增长奠定坚实的基础。
相关文章:
【商城实战(20)】商品管理功能深化实战
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
YC 孵化项目 Pinch:实时语音翻译视频会议平台;Mistral OCR:能处理多语言多模态复杂文档丨日报
开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。 我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 技术 」、「有亮点的 产品 」、「有思考的 文章 」、「有态度的 …...
数据库原理6
1.数据是信息的载体 2.数据库应用程序人员的主要职责:编写应用系统的程序模块 3.关系规范化理论主要属于数据库理论的研究范畴 4.数据库主要有检索和修改(包括插入,删除,更新)两大操作 5.概念模型又称为语义模型。…...
深度学习与大模型基础-向量
大家好!今天我们来聊聊向量(Vector)。别被这个词吓到,其实向量在我们的生活中无处不在,只是我们没注意罢了。 1. 向量是什么? 简单来说,向量就是有大小和方向的量。比如你从家走到学校&#x…...
OpenManus:3小时复刻 Manus(OpenManus安装指南)
项目地址:GitHub - mannaandpoem/OpenManus: No fortress, purely open ground. OpenManus is Coming. 安装指南 我们提供两种安装方式。推荐使用方式二(uv),因为它能提供更快的安装速度和更好的依赖管理。 方式一:使…...
2025年渗透测试面试题总结-快某手-安全实习生(一面、二面)(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 快某手-安全实习生 一面 一、Linux操作:查看进程PID的5种方法 二、Elasticsearch&#x…...
【微信小程序】uniapp开发微信小程序
uniapp开发微信小程序 1、上拉加载 下拉刷新 import { onReachBottom, onPullDownRefresh } from dcloudio/uni-app;配置允许下拉刷新: {"path" : "pages/pet/pet","style" : {"navigationBarTitleText" : ""…...
动态规划_最大子数组和
53. 最大子数组和 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组是数组中的一个连续部分。 示例 1:输入:nums [-2,1,-3,4,-1,2,1,-5,4] …...
从零开始的python学习(五)P71+P72+P73+P74
本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…...
Vue3实战学习(Element-Plus常用组件的使用(输入框、下拉框、单选框多选框、el-image图片))(上)(5)
目录 一、Vue3工程环境配置、项目基础脚手架搭建、Vue3基础语法、Vue3集成Element-Plus的详细教程。(博客链接如下) 二、Element-Plus常用组件使用。 (1)el-input。(input输入框) <1>正常状态的el-input。 <2>el-input的disable状态。 <3…...
HarmonyOS学习第18天:多媒体功能全解析
一、开篇引入 在当今数字化时代,多媒体已经深度融入我们的日常生活。无论是在工作中通过视频会议进行沟通协作,还是在学习时借助在线课程的音频讲解加深理解,亦或是在休闲时光用手机播放音乐放松身心、观看视频打发时间,多媒体功…...
多模态融合的分类、跨模态对齐的方法
两者的主要区别 维度扩模态对齐扩模态融合目标对齐模态间的表示,使其语义一致融合模态间的信息,生成联合表示关注点模态间的相似性和语义一致性模态间的互补性和信息整合空间映射到共享的公共语义空间生成新的联合特征空间方法对比学习、共享空间、注意…...
软件高级架构师 - 软件工程
补充中 测试 测试类型 静态测试 动态测试 测试阶段 单元测试中,包含性能测试,如下: 集成测试中,包含以下: 维护 遗留系统处置 高水平低价值:采取集成 对于这类系统,采取 集成 的方式&…...
Uniapp项目运行到微信小程序、H5、APP等多个平台教程
摘要:Uniapp作为一款基于Vue.js的跨平台开发框架,支持“一次开发,多端部署”。本文将手把手教你如何将Uniapp项目运行到微信小程序、H5、APP等多个平台,并解析常见问题。 一、环境准备 在开始前,请确保已安装以下工具…...
【JavaWeb12】数据交换与异步请求:JSON与Ajax的绝妙搭配是否塑造了Web的交互革命?
文章目录 🌍一. 数据交换--JSON❄️1. JSON介绍❄️2. JSON 快速入门❄️3. JSON 对象和字符串对象转换❄️4. JSON 在 java 中使用❄️5. 代码演示 🌍二. 异步请求--Ajax❄️1. 基本介绍❄️2. JavaScript 原生 Ajax 请求❄️3. JQuery 的 Ajax 请求 &a…...
2025-03-10 吴恩达机器学习1——机器学习概述
文章目录 1 监督学习1.1 回归1.2 分类 2 无监督学习2.1 聚类2.2 异常检测2.3 降维 3 使用 Jupyter Notebook 1959 年,Arthur Samuel 将机器学习定义如下: Field of study that gives computers the ability to learn without being explicitly pro…...
Spring Boot整合WebSocket
目录 ?引言 1.WebSocket 基础知识 ?1.1 什么是 WebSocket? ?1.2 WebSocket 的应用场景 ?2.Spring Boot WebSocket 整合步骤 2.1 创建 Spring Boot 项目 2.2 添加 Maven 依赖 2.3 配置 WebSocket 2.4 创建 WebSocket 控制器 2.5 创建前端页面 引言 在…...
PostgreSQL - Windows PostgreSQL 下载与安装
Windows PostgreSQL 下载与安装 1、PostgreSQL 下载 下载地址:https://www.enterprisedb.com/downloads/postgres-postgresql-downloads 2、PostgreSQL 安装 启动安装程序 -> 点击 【Next】 指定安装路径 -> 点击 【Next】 默认勾选 -> 点击 【Next】 指…...
【Java面试题汇总】Java面试100道最新合集!
1.说说你对面向对象的理解 得分点 封装,继承,多态、概念、实现方式和优缺点 面向对象的三大基本特征是:封装、继承、多态。 封装:将对象的状态和行为包装在一个类中并对外界隐藏实现的细节,可以通过访问修饰符控制成员的访问权限,…...
【LLM】kimi 1.5模型架构和训练流程
note 推出两个多模态模型,深度思考模型 long-CoT 对标 o1,通用模型 short-CoT 模型对标 gpt-4o。 文章目录 note一、kimi 1.5模型训练流程预训练SFT训练long-CoT SFTRL训练long2short 小结Reference 一、kimi 1.5模型训练流程 推出两个多模态模型&…...
Android Studio 配置国内镜像源
Android Studio版本号:2022.1.1 Patch 2 1、配置gradle国内镜像,用腾讯云 镜像源地址:https\://mirrors.cloud.tencent.com/gradle 2、配置Android SDK国内镜像 地址:Index of /AndroidSDK/...
永洪科技深度分析实战,零售企业的销量预测
随着人工智能技术的不断发展,智能预测已经成为各个领域的重要应用之一。现在,智能预测技术已经广泛应用于金融、零售、医疗、能源等领域,为企业和个人提供决策支持。 智能预测技术通过分析大量的数据,利用机器学习和深度学习算法…...
Pytorch实现之利用CGAN鉴别真假图像
简介 简介:利用生成对抗网络来鉴别是真图像还是假图像。 论文题目:Detection and Identification of Fake Images Using Conditional Generative Adversarial Networks (CGANs) (基于条件生成对抗网络(CGAN)的假图像检测与识别) 会议:16th IEEE International Confer…...
开源模型时代的 AI 开发革命:Dify 技术深度解析
开源模型时代的AI开发革命:Dify技术深度解析 引言:AI开发的开源新纪元 在生成式AI技术突飞猛进的2025年,开源模型正成为推动行业创新的核心力量。据统计,全球超过80%的AI开发者正在使用开源模型构建应用,这一趋势不仅…...
网络DNS怎么更改?
访问速度慢或某些网站无法打开?改变网络DNS设置可能会帮助解决这些问题。本文将详细介绍如何更改网络DNS,包括更改的原因、具体步骤。 一、为什么要更改DNS? 更改DNS的原因有很多,以下是一些主要的考虑因素:某些公共DNS服务器的响应速度比…...
计算机网络篇:基础知识总结与基于长期主义的内容更新
基础知识总结 和 MySQL 类似,我同样花了一周左右的时间根据 csview 对计算机网络部分的八股文进行了整理,主要的内容包括:概述、TCP 与 UDP、IP、HTTP,其中我个人认为最重要的是 TCP 这部分的内容。 在此做一篇目录索引…...
使用miniforge安装python并用pycharm打开使用
1.安装miniforge 参考文章:https://blog.csdn.net/loujiand/article/details/119976302 https://blog.csdn.net/qq_41946216/article/details/129481760 下载地址: 先从github下载miniforge:https://github.com/conda-forge/miniforge 2.使用conda命令…...
如何实现wordpress搜索自字义字段内容
有些网站需要根据自定义段字的内容来做为搜索项,比如,房产中介公司wordpress网站,需要搜索同一区域内容的楼盘,然后展示出内容。 不废话了,在function.php直接加上代码 add_action(posts_search, function($search, …...
【华为OD机考真题】- 星际篮球争霸赛(Java)
1. 题目描述 具体题目描述如下: 在星球争霸篮球赛对抗赛中,最大的宇宙战队希望每个人都能拿到 MVP,MVP 的条件是单场最高分得分获得者。 可以并列,所以宇宙战队决定在比赛中,尽可能让更多队员上场,并且让所有得分的选手…...
LeetCode 376. 摆动序列 java题解
https://leetcode.cn/problems/wiggle-subsequence/description/ 只要不满足摆动条件,就不更新count和prediff 当 prevDiff 取等号时,比如 prevDiff 0,在这种情况下,如果 currDiff > 0,说明从持平状态转变为上升…...
PyCharm 接入 DeepSeek、OpenAI、Gemini、Mistral等大模型完整版教程(通用)!
PyCharm 接入 DeepSeek、OpenAI、Gemini、Mistral等大模型完整版教程(通用)! 当我们成功接入大模型时,可以选中任意代码区域进行解答,共分为三个区域,分别是选中区域、提问区域以及回答区域,我…...
使用RabbitMQ实现流量削峰填谷
原理 流量削峰填谷是指在面对突发的高流量时,通过消息队列将瞬时大量请求暂时存储起来,并逐步处理这些请求,从而避免系统过载。RabbitMQ 作为消息中间件可以很好地支持这一需求,特别是结合其延时消息插件(rabbitmq_de…...
Apache Commons Lang3 和 Commons Net 详解
目录 1. Apache Commons Lang3 1.1 什么是 Apache Commons Lang3? 1.2 主要功能 1.3 示例代码 2. Commons Net 2.1 什么是 Commons Net? 2.2 主要功能 2.3 示例代码 3. 总结 3.1 Apache Commons Lang3 3.2 Commons Net 3.3 使用建议 4. 参考…...
ACE学习2——write transaction
用于处理缓存行的数据更新到主内存(main memory)的操作。 以下是用于更新主内存的几种事务类型: WriteBack: WriteBack事务用于将cache中的dirty态的cacheline写回主存,以释放cache中的cacheline,用于存…...
mac本地安装运行Redis-单机
记录一下我以前用的连接服务器的跨平台SSH客户端。 因为还要准备毕设...... 服务器又过期了,只能把redis安装下载到本地了。 目录 1.github下载Redis 2.安装homebrew 3.更新GCC 4.自行安装Redis 5.通过 Homebrew 安装 Redis 安装地址:https://git…...
sparkTTS window 安装
SparkTTS 的简介 Spark-TTS是一种基于SpardAudio团队提出的 BiCodec 构建的新系统,BiCodec 是一种单流语音编解码器,可将语音策略性地分解为两种互补的标记类型:用于语言内容的低比特率语义标记和用于说话者特定属性的固定长度全局标记。这种…...
颠覆语言认知的革命!神经概率语言模型如何突破人类思维边界?
颠覆语言认知的革命!神经概率语言模型如何突破人类思维边界? 一、传统模型的世纪困境:当n-gram遇上"月光族难题" 令人震惊的案例:2012年Google语音识别系统将 用户说:“我要还信用卡” 系统识别ÿ…...
大语言模型从理论到实践(第二版)-学习笔记(绪论)
大语言模型的基本概念 1.理解语言是人工智能算法获取知识的前提 2.语言模型的目标就是对自然语言的概率分布建模 3.词汇表 V 上的语言模型,由函数 P(w1w2 wm) 表示,可以形式化地构建为词序列 w1w2 wm 的概率分布,表示词序列 w1w2 wm…...
2.1 Vite + Vue 3 + TS 项目脚手架深度配置
文章目录 **一、环境准备与技术选型****二、项目初始化与基础架构****三、工程化配置深度优化****四、代码规范与质量保障****五、Vue 3 深度集成****六、TypeScript 高级配置****七、第三方库集成****八、构建优化策略****九、企业级最佳实践****十、扩展配置参考****本章核心…...
deepin安装rust
一、环境 操作系统:deepin V23 二、下载离线安装包 下载链接: https://forge.rust-lang.org/infra/other-installation-methods.html https://static.rust-lang.org/dist/rust-1.85.0-x86_64-unknown-linux-gnu.tar.xz 当时最新稳定版本为1.85。 三、解…...
【愚公系列】《Python网络爬虫从入门到精通》045-Charles的SSL证书的安装
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主&…...
UniApp 运行的微信小程序如何进行深度优化
UniApp 运行的微信小程序如何进行深度优化 目录 引言性能优化 1. 减少包体积2. 优化页面加载速度3. 减少 setData 调用4. 使用分包加载 代码优化 1. 减少不必要的代码2. 使用条件编译3. 优化图片资源 用户体验优化 1. 优化交互体验2. 预加载数据3. 使用骨架屏 调试与监控 1. …...
Hadoop安装文件解压报错:无法创建符号链接。。。
您可能需要管理员身份运行winRAR; 客户端没有所需的特权; cmd进入该目录下,输入命令(本地解压):start winrar x -y hadoop-2.10.1.tar.gz...
Redis6.2.6下载和安装
简介 Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。Redis 提供了数据结构,例如字符串、散列、列表、集合、带有范围查询的排序集合、位图、超级日志、地理空间索引和流。Redis 内置复制、Lua 脚…...
【AI】神经网络|机器学习——图解Transformer(完整版)
Transformer是一种基于注意力机制的序列模型,最初由Google的研究团队提出并应用于机器翻译任务。与传统的循环神经网络(RNN)和卷积神经网络(CNN)不同,Transformer仅使用自注意力机制(self-attention)来处理输入序列和输出序列,因此可以并行计算,极大地提高了计算效率…...
超过 37000 台 VMwareESXi 服务器可能受到持续攻击威胁
近日,威胁监测平台影子服务器基金会(The Shadowserver Foundation)发布报告,指出超 3.7 万个互联网暴露的威睿(VMware)ESXi 实例存在严重安全隐患,极易受到 CVE-2025-22224 漏洞的攻击。该漏洞属…...
多宠识别:基于计算机视觉的智能宠物管理系统架构解析
一、行业痛点与技术方案演进 在多宠家庭场景中,传统方案面临三大技术瓶颈: 1. 生物特征混淆:同品种/毛色宠物识别准确率低于65% 2. 动态场景适应:进食/奔跑状态下的误检率达30% 3. 数据孤岛问题:离线设备无法实现持续…...
mobaxterm,闪退处理方法
mobaxterm,使用过程中突然闪退, 具体表现为:登录远程服务器成功,开始闪退 登录失败不闪退 一开始以为是,服务器做了控制,后来才发现是mobaxterm软件的问题。 问题解决方法: 勾选工具ssh&…...
文件系统文件管理
文件缓冲区(内核级,OS内部的)存在的意义:系统调用将数据写入缓冲区后函数即可返回,是从内存到内存的,提高了程序的效率。之后将缓冲区数据刷新到硬盘则是操作系统的事了。无论读写,OS都会把数据…...
Vue 实现AI对话和AI绘图(AIGC)人工智能
我司是主要是负责AIGC人工智能化平台的项目,俗称内容创作及智能工具平台。 授人以鱼不如授人以渔 首先我们要明白AIGC中前端需要做什么 会用到哪些技术栈 。 AIGC前端需要用到的技术栈:Vue,Markdown,SSE。就这个三件套。 前沿:有人觉得AI对…...