292 lines
6.7 KiB
Vue
292 lines
6.7 KiB
Vue
<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
|
||
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>
|