|
@@ -12,7 +12,13 @@
|
|
|
<view class="inventory-section">
|
|
<view class="inventory-section">
|
|
|
<view class="section-head">
|
|
<view class="section-head">
|
|
|
<text class="section-title">盘点清单</text>
|
|
<text class="section-title">盘点清单</text>
|
|
|
- <text class="section-count">{{ inventoryList.length }} 种</text>
|
|
|
|
|
|
|
+ <text class="section-count">{{ searchKeyword ? filteredInventoryList.length + ' / ' + inventoryList.length : inventoryList.length }} 种</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="search-bar">
|
|
|
|
|
+ <view class="search-icon"></view>
|
|
|
|
|
+ <input class="search-input" v-model="searchKeyword" placeholder="搜索商品名称或编码" confirm-type="search" />
|
|
|
|
|
+ <view class="search-clear" v-if="searchKeyword" @click="searchKeyword = ''"><text>✕</text></view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<view class="empty-state" v-if="inventoryList.length === 0">
|
|
<view class="empty-state" v-if="inventoryList.length === 0">
|
|
@@ -20,14 +26,18 @@
|
|
|
<text class="empty-text">暂无商品库存数据</text>
|
|
<text class="empty-text">暂无商品库存数据</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <view class="inventory-card" v-for="(item, index) in inventoryList" :key="index">
|
|
|
|
|
|
|
+ <view class="empty-state" v-else-if="inventoryList.length > 0 && filteredInventoryList.length === 0">
|
|
|
|
|
+ <text class="empty-text">未找到匹配商品</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="inventory-card" v-for="({ item, idx }, i) in filteredInventoryList" :key="idx">
|
|
|
<view class="item-row">
|
|
<view class="item-row">
|
|
|
<image v-if="item.product_image" :src="item.product_image" class="item-img" mode="aspectFill" />
|
|
<image v-if="item.product_image" :src="item.product_image" class="item-img" mode="aspectFill" />
|
|
|
<view v-else class="item-img-placeholder"><text>无图</text></view>
|
|
<view v-else class="item-img-placeholder"><text>无图</text></view>
|
|
|
<view class="item-info">
|
|
<view class="item-info">
|
|
|
<view class="item-name-row">
|
|
<view class="item-name-row">
|
|
|
<text class="item-name">{{ item.product_name || item.productName || '未知商品' }}</text>
|
|
<text class="item-name">{{ item.product_name || item.productName || '未知商品' }}</text>
|
|
|
- <view class="stock-badge" :class="getDiffClass(item, index)">{{ getDiffLabel(item, index) }}</view>
|
|
|
|
|
|
|
+ <view class="stock-badge" :class="getDiffClass(item, idx)">{{ getDiffLabel(item, idx) }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="item-meta">
|
|
<view class="item-meta">
|
|
|
<text class="item-code" v-if="item.product_code || item.productCode">{{ item.product_code || item.productCode }}</text>
|
|
<text class="item-code" v-if="item.product_code || item.productCode">{{ item.product_code || item.productCode }}</text>
|
|
@@ -38,12 +48,12 @@
|
|
|
</view>
|
|
</view>
|
|
|
<view class="audit-row">
|
|
<view class="audit-row">
|
|
|
<view class="qty-control">
|
|
<view class="qty-control">
|
|
|
- <view class="qty-btn" @click="decrease(index)"><text>-</text></view>
|
|
|
|
|
- <input class="qty-input" type="number" v-model.number="auditItems[index].newStock" />
|
|
|
|
|
- <view class="qty-btn" @click="increase(index)"><text>+</text></view>
|
|
|
|
|
|
|
+ <view class="qty-btn" @click="decrease(idx)"><text>-</text></view>
|
|
|
|
|
+ <input class="qty-input" type="number" v-model.number="auditItems[idx].newStock" />
|
|
|
|
|
+ <view class="qty-btn" @click="increase(idx)"><text>+</text></view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="quick-btn" @click="setToStandard(item, index)"><text>设标</text></view>
|
|
|
|
|
- <view class="quick-btn quick-reset" @click="setToCurrentStock(item, index)"><text>重置</text></view>
|
|
|
|
|
|
|
+ <view class="quick-btn" @click="setToStandard(item, idx)"><text>设标</text></view>
|
|
|
|
|
+ <view class="quick-btn quick-reset" @click="setToCurrentStock(item, idx)"><text>重置</text></view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
@@ -79,6 +89,19 @@ const deviceName = ref('');
|
|
|
const inventoryList = ref<any[]>([]);
|
|
const inventoryList = ref<any[]>([]);
|
|
|
const auditItems = ref<AuditInput[]>([]);
|
|
const auditItems = ref<AuditInput[]>([]);
|
|
|
const submitting = ref(false);
|
|
const submitting = ref(false);
|
|
|
|
|
+const searchKeyword = ref('');
|
|
|
|
|
+
|
|
|
|
|
+const filteredInventoryList = computed(() => {
|
|
|
|
|
+ const kw = searchKeyword.value.trim().toLowerCase();
|
|
|
|
|
+ return inventoryList.value
|
|
|
|
|
+ .map((item, idx) => ({ item, idx }))
|
|
|
|
|
+ .filter(({ item }) => {
|
|
|
|
|
+ if (!kw) return true;
|
|
|
|
|
+ const name = (item.product_name || item.productName || '').toLowerCase();
|
|
|
|
|
+ const code = (item.product_code || item.productCode || '').toLowerCase();
|
|
|
|
|
+ return name.includes(kw) || code.includes(kw);
|
|
|
|
|
+ });
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
const changedCount = computed(() =>
|
|
const changedCount = computed(() =>
|
|
|
auditItems.value.filter(i => i.productId != null && i.newStock !== i.originalStock).length
|
|
auditItems.value.filter(i => i.productId != null && i.newStock !== i.originalStock).length
|
|
@@ -188,6 +211,14 @@ onMounted(async () => {
|
|
|
.section-head { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0 12rpx; }
|
|
.section-head { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0 12rpx; }
|
|
|
.section-title { font-size: $font-size-md; font-weight: 700; color: $text-color-primary; }
|
|
.section-title { font-size: $font-size-md; font-weight: 700; color: $text-color-primary; }
|
|
|
.section-count { font-size: $font-size-sm; color: $text-color-muted; }
|
|
.section-count { font-size: $font-size-sm; color: $text-color-muted; }
|
|
|
|
|
+.search-bar { display: flex; align-items: center; background: $bg-color-card; border: 1rpx solid $border-color; border-radius: $radius-base; padding: 0 16rpx; height: 60rpx; margin-bottom: 12rpx; }
|
|
|
|
|
+.search-icon { width: 28rpx; height: 28rpx; border: 2.5rpx solid $text-color-muted; border-radius: 50%; position: relative; flex-shrink: 0; margin-right: 10rpx;
|
|
|
|
|
+ &::after { content: ''; position: absolute; bottom: -2rpx; right: -4rpx; width: 2.5rpx; height: 10rpx; background: $text-color-muted; border-radius: 1rpx; transform: rotate(-45deg); }
|
|
|
|
|
+}
|
|
|
|
|
+.search-input { flex: 1; font-size: $font-size-sm; color: $text-color-primary; height: 100%; }
|
|
|
|
|
+.search-clear { width: 36rpx; height: 36rpx; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: $bg-color-secondary; flex-shrink: 0; margin-left: 8rpx;
|
|
|
|
|
+ text { font-size: 20rpx; color: $text-color-muted; line-height: 1; }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
.empty-state { display: flex; flex-direction: column; align-items: center; padding: 80rpx 0; }
|
|
.empty-state { display: flex; flex-direction: column; align-items: center; padding: 80rpx 0; }
|
|
|
.empty-icon-box { width: 96rpx; height: 96rpx; background: $bg-color-secondary; border-radius: $radius-lg; display: flex; align-items: center; justify-content: center; margin-bottom: $spacing-2; }
|
|
.empty-icon-box { width: 96rpx; height: 96rpx; background: $bg-color-secondary; border-radius: $radius-lg; display: flex; align-items: center; justify-content: center; margin-bottom: $spacing-2; }
|