ry_app/pages/batchDeleteProduct/batchDeleteProduct.vue

380 lines
9.2 KiB
Vue
Raw Normal View History

2026-01-22 14:50:26 +08:00
<template>
<view class="container">
<!-- 顶部导航栏 -->
<view class="navbar">
<view class="nav-back" @tap="goBack">
<uni-icons type="back" size="18" color="#fff"></uni-icons>
</view>
<view class="nav-title">批量删除</view>
<view class="nav-more">
<uni-icons type="more-filled" size="18" color="#fff"></uni-icons>
</view>
</view>
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<uni-icons type="search" size="18" color="#999"></uni-icons>
<input
type="text"
placeholder="搜索商品名称、条码"
placeholder-class="placeholder"
v-model="searchText"
@confirm="onSearch"
/>
<uni-icons type="scan" size="18" color="#999"></uni-icons>
</view>
<view class="filter-btn" @tap="showFilter = !showFilter">
<uni-icons type="filter" size="18" color="#999"></uni-icons>
<text>筛选</text>
</view>
</view>
<!-- 商品列表 -->
<scroll-view class="goods-list" scroll-y="true">
<!-- 空列表提示 -->
<view v-if="goodsList.length === 0" class="empty-tip">
<text>暂无商品数据</text>
</view>
<!-- 商品项 -->
<view
class="goods-item"
v-for="item in goodsList"
:key="item.id"
>
<view class="checkbox-wrap" @tap="toggleSelect(item.id)">
<view
class="custom-checkbox"
:class="{ checked: selectedIds.includes(String(item.id)) }"
>
<text v-if="selectedIds.includes(String(item.id))" class="check-icon"></text>
</view>
</view>
<view class="goods-info">
<image
:src="item.mainImage ? 'http://193.112.94.36:8081' + item.mainImage : '/static/default-goods.png'"
class="goods-img"
mode="aspectFill"
/>
<view class="goods-details">
<text class="goods-name">{{ item.productName || '未命名商品' }}</text>
<text class="goods-barcode">{{ item.productBarCode || '无条码' }}</text>
<text class="goods-price">{{ item.storePrice ? Number(item.storePrice).toFixed(2) : '0.00' }}</text>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="bottom-bar" v-if="goodsList.length > 0">
<view class="select-all" @tap="toggleSelectAll">
<view class="custom-checkbox" :class="{ checked: isAllSelected }">
<text v-if="isAllSelected" class="check-icon"></text>
</view>
<text>全选 (已选{{ selectedIds.length }})</text>
</view>
<button
class="delete-btn"
:class="{ disabled: selectedIds.length === 0 }"
:disabled="selectedIds.length === 0"
@tap="onDeleteSelected"
>
删除商品
</button>
</view>
</view>
</template>
<script>
import { getProductList as fetchProductList, batchDeleteProduct } from '@/api/product'
import { getToken, getStoreId } from '@/utils/auth'
export default {
data() {
return {
searchText: '',
showFilter: false,
storeId: null,
goodsList: [],
selectedIds: []
}
},
computed: {
allCheckboxValues() {
return this.goodsList.map(item => ({
text: '',
value: String(item.id)
}));
},
isAllSelected() {
return this.goodsList.length > 0 && this.selectedIds.length === this.goodsList.length;
}
},
onLoad() {
const storeId = getStoreId();
if (storeId) {
this.storeId = storeId;
this.getProductList();
} else {
uni.showToast({ title: '请先选择门店', icon: 'none', duration: 1500 });
setTimeout(() => uni.navigateBack(), 1500);
}
},
methods: {
goBack() {
uni.navigateBack();
},
// 加载商品列表
async getProductList(searchParams = {}) {
try {
if (this.storeId) {
searchParams.storeId = this.storeId;
}
const res = await fetchProductList(searchParams);
if (res && res.code === 200 && Array.isArray(res.data)) {
this.goodsList = res.data;
this.selectedIds = [];
} else {
uni.showToast({ title: res?.msg || '获取商品失败', icon: 'none' });
}
} catch (error) {
console.error('获取商品异常:', error);
uni.showToast({ title: '网络错误', icon: 'none' });
}
},
// 搜索
onSearch() {
const keyword = this.searchText.trim();
if (!keyword) {
this.getProductList();
return;
}
const searchParams = /^\d+$/.test(keyword)
? { productBarCode: keyword }
: { productName: keyword };
this.getProductList(searchParams);
},
// 单个商品选中/取消
toggleSelect(id) {
const idStr = String(id);
const index = this.selectedIds.indexOf(idStr);
if (index > -1) {
this.selectedIds.splice(index, 1);
} else {
this.selectedIds.push(idStr);
}
},
// 全选/取消全选
toggleSelectAll() {
if (this.isAllSelected) {
this.selectedIds = [];
} else {
this.selectedIds = this.goodsList.map(item => String(item.id));
}
},
// 批量删除
async onDeleteSelected() {
if (this.selectedIds.length === 0) return;
uni.showModal({
title: '确认删除',
content: `确定删除选中的${this.selectedIds.length}个商品?删除后不可恢复!`,
success: async (res) => {
if (res.confirm) {
try {
const idsToDelete = this.selectedIds.map(id => Number(id));
const result = await batchDeleteProduct(idsToDelete);
if (result && result.code === 200) {
uni.showToast({ title: '删除成功', icon: 'success' });
this.getProductList();
} else {
uni.showToast({ title: result?.msg || '删除失败', icon: 'none' });
}
} catch (error) {
console.error('删除异常:', error);
uni.showToast({ title: '网络错误', icon: 'none' });
}
}
}
});
}
}
}
</script>
<style scoped>
/* 全局容器 */
.container {
background-color: #f5f5f5;
min-height: 100vh;
padding-bottom: 80rpx;
}
/* 导航栏 */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
background-color: #e63946;
color: #fff;
}
.nav-title { font-size: 18px; font-weight: 600; }
/* 搜索栏 */
.search-bar {
display: flex;
align-items: center;
padding: 12px 16px;
background-color: #fff;
}
.search-input {
flex: 1;
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 8px;
padding: 10px 12px;
margin-right: 12px;
}
.search-input input {
flex: 1;
margin-left: 8px;
font-size: 14px;
border: none;
outline: none;
}
.placeholder { color: #999; }
.filter-btn {
display: flex;
align-items: center;
font-size: 14px;
color: #666;
}
.filter-btn text { margin-left: 4px; }
/* 商品列表 */
.goods-list {
height: calc(100vh - 200px);
padding: 16px;
}
.empty-tip {
text-align: center;
padding: 40px 0;
color: #999;
font-size: 14px;
}
.goods-item {
display: flex;
align-items: center;
background-color: #fff;
border-radius: 8px;
padding: 12px;
margin-bottom: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
/* 复选框容器 */
.checkbox-wrap {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
}
/* 自定义复选框样式 */
.custom-checkbox {
width: 28rpx;
height: 28rpx;
border-radius: 4rpx;
border: 2rpx solid #ddd;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.custom-checkbox.checked {
background-color: #e63946;
border-color: #e63946;
}
.check-icon {
color: #fff;
font-size: 18rpx;
font-weight: bold;
}
/* 商品信息 */
.goods-info {
display: flex;
align-items: center;
flex: 1;
}
.goods-img {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
margin-right: 12px;
}
.goods-details { flex: 1; }
.goods-name {
display: block;
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.goods-barcode {
font-size: 12px;
color: #999;
display: block;
margin-bottom: 4px;
}
.goods-price {
font-size: 12px;
color: #e63946;
font-weight: 600;
}
/* 底部操作栏 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background-color: #fff;
border-top: 1px solid #eee;
z-index: 99;
}
.select-all {
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.select-all text { margin-left: 6px; }
.delete-btn {
padding: 10px 24px;
border-radius: 8px;
font-size: 16px;
background-color: #e63946;
color: #fff;
border: none;
}
.delete-btn.disabled {
background-color: #ccc;
color: #999;
cursor: not-allowed;
}
</style>