Files
shj-app/scanpages/hj/barcode_search.vue
T

292 lines
6.7 KiB
Vue
Raw Normal View History

2026-08-18 18:05:16 +08:00
<template>
<view class="page-list">
<!-- 关键字搜索 -->
<view class="filter-header">
<view class="search-row">
<u-search
v-model="keyword"
placeholder="请输入条码关键字(至少2位)"
:show-action="false"
shape="square"
bg-color="#f5f6f8"
@change="handleSearch"
@search="handleSearch"
/>
</view>
</view>
<scroll-view
class="list-scroll"
scroll-y
@scrolltolower="loadMore"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="handleRefresh"
>
<view class="list-wrap">
<view
v-for="(item, index) in listData"
:key="item.Id || item.BarCode || index"
class="card-item"
>
<view class="card-header">
<text class="card-code">{{ item.BarCode || '--' }}</text>
</view>
<view class="card-body">
<view class="card-row">
<text class="row-label">材料编码</text>
<text class="row-value">{{ item.MaterialCode || '--' }}</text>
</view>
<view class="card-row">
<text class="row-label">数量</text>
<text class="row-value">{{ formatQuantity(item.Quantity) }}</text>
</view>
</view>
<view class="card-footer">
<view
class="print-btn"
@click.stop="handlePrint(item)"
>
<text class="print-btn__text">打印</text>
</view>
</view>
</view>
<u-empty
v-if="!loading && listData.length === 0"
mode="list"
:text="emptyText"
/>
<view
v-if="loading && listData.length > 0"
class="loading-more"
>
<u-loading
mode="circle"
size="32"
/>
<text>加载中...</text>
</view>
<view
v-if="!loading && !hasMore && listData.length > 0"
class="no-more"
>
<text>— 没有更多了 —</text>
</view>
</view>
</scroll-view>
<!-- 标签预览与打印 -->
<nbd-print-prop-qr
v-model="showPrint"
:labelWidth="50"
:labelHeight="30"
:margin="2"
:barcodeText="printBarcode"
:barcodeTopText="printTop"
:barcodeBottomText="printBottom"
/>
</view>
</template>
<script setup>
/**
* 条码查询:按条码关键字分页查询入库明细,支持标签预览与蓝牙打印
*/
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { reqGetInputDetailBarCodes } from '@/api/base.js'
/** 最少关键字长度(文档建议 2~3) */
const MIN_KEYWORD_LEN = 2
/** 搜索关键字 */
const keyword = ref('')
/** 列表数据 */
const listData = ref([])
/** 加载中 */
const loading = ref(false)
/** 下拉刷新中 */
const refreshing = ref(false)
/** 是否还有更多 */
const hasMore = ref(true)
/** 当前页码(从 1 开始) */
const currentPage = ref(1)
/** 每页条数 */
const pageSize = 20
/** 总页数(接口 pageCount) */
const pageCount = ref(0)
/** 搜索防抖定时器 */
let searchTimer = null
/** 是否展示打印弹窗 */
const showPrint = ref(false)
/** 打印:条码内容 */
const printBarcode = ref('')
/** 打印:条码上方文案 */
const printTop = ref('')
/** 打印:条码下方文案 */
const printBottom = ref('')
/** 空状态文案 */
const emptyText = computed(() => {
const kw = keyword.value.trim()
if (!kw) return '请输入条码关键字查询'
if (kw.length < MIN_KEYWORD_LEN) return `请至少输入${MIN_KEYWORD_LEN}个字符`
return '暂无数据'
})
/**
* 数量展示
* @param {unknown} q
*/
const formatQuantity = (q) => {
if (q === undefined || q === null || q === '') return '--'
return String(q)
}
/**
* 从响应中取出当前页列表
* @param {any} data
* @returns {array}
*/
const pickList = (data) => {
if (!data || typeof data !== 'object') return []
if (Array.isArray(data.getDataList)) return data.getDataList
if (Array.isArray(data.list)) return data.list
if (Array.isArray(data)) return data
return []
}
/** 清空本地列表状态 */
const clearList = () => {
listData.value = []
hasMore.value = false
pageCount.value = 0
refreshing.value = false
loading.value = false
}
/**
* 拉取一页数据;关键字为空或过短时不请求
* @param {boolean} isRefresh
*/
const fetchData = async (isRefresh = false) => {
const kw = keyword.value.trim()
if (!kw || kw.length < MIN_KEYWORD_LEN) {
clearList()
return
}
if (loading.value || (!isRefresh && !hasMore.value)) return
loading.value = true
if (isRefresh) refreshing.value = true
try {
const res = await reqGetInputDetailBarCodes({
barCodeKeyword: kw,
pageIndex: currentPage.value,
pageSize,
})
const data = res?.data ?? {}
const pageData = pickList(data)
const totalPages = Number(data.pageCount) || 0
pageCount.value = totalPages
if (totalPages > 0) {
hasMore.value = currentPage.value < totalPages
} else {
hasMore.value = pageData.length >= pageSize
}
listData.value = isRefresh ? pageData : listData.value.concat(pageData)
} catch (e) {
console.error('条码列表加载失败:', e)
uni.showToast({ title: '加载失败,请重试', icon: 'none' })
} finally {
loading.value = false
refreshing.value = false
}
}
/** 重置到第一页并重新加载 */
const resetAndLoad = () => {
currentPage.value = 1
hasMore.value = true
listData.value = []
fetchData(true)
}
/** 下拉刷新 */
const handleRefresh = () => {
const kw = keyword.value.trim()
if (!kw || kw.length < MIN_KEYWORD_LEN) {
clearList()
return
}
resetAndLoad()
}
/** 关键字变更(防抖);清空或过短时直接清空列表 */
const handleSearch = () => {
if (searchTimer) clearTimeout(searchTimer)
searchTimer = setTimeout(() => {
const kw = keyword.value.trim()
if (!kw || kw.length < MIN_KEYWORD_LEN) {
clearList()
return
}
resetAndLoad()
}, 400)
}
/** 上拉加载更多 */
const loadMore = () => {
if (!loading.value && hasMore.value && keyword.value.trim().length >= MIN_KEYWORD_LEN) {
currentPage.value += 1
fetchData(false)
}
}
/**
* 打开标签预览与打印(与材料详情一致:图内编码为 MaterialCode)
* @param {object} item
*/
const handlePrint = (item) => {
const code = item.MaterialCode || ''
if (!code) {
uni.showToast({ title: '缺少材料编码', icon: 'none' })
return
}
printBarcode.value = code
printTop.value = item.BarCode || ''
printBottom.value = item.Quantity != null && item.Quantity !== ''
? `数量 ${item.Quantity}`
: ''
showPrint.value = true
}
onLoad(() => {
// 空关键字不自动请求
})
</script>
<style lang="scss" scoped>
.card-footer {
display: flex;
justify-content: flex-end;
padding-top: 16rpx;
border-top: 1rpx solid #f0f0f0;
margin-top: 8rpx;
}
.print-btn {
padding: 10rpx 28rpx;
border-radius: 8rpx;
background: #2979ff;
}
.print-btn__text {
font-size: 24rpx;
color: #ffffff;
}
</style>