Files
shj-app/scanpages/hj/weld_joint_search.vue
T
2026-08-18 18:05:16 +08:00

386 lines
7.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="search-page">
<!-- 顶部:输入框内扫码(类似淘宝搜索) -->
<view class="search-bar">
<view class="search-bar__input-wrap">
<u-icon
name="scan"
:size="28"
color="#2979ff"
@click="handleScan"
/>
<input
class="search-bar__input"
v-model="keyword"
type="text"
confirm-type="search"
:placeholder="placeholder"
:focus="autoFocus"
@input="handleSearchInput"
@confirm="handleSearchConfirm"
/>
<u-icon
v-if="keyword"
name="close-circle-fill"
:size="28"
color="#ccc"
@click="handleClear"
/>
</view>
</view>
<!-- 结果列表 -->
<scroll-view
class="result-scroll"
scroll-y
@scrolltolower="loadMore"
>
<view class="result-list">
<view
v-for="(item, index) in listData"
:key="item.WeldJointId || item.Id || index"
class="result-item"
@click="handleSelect(item)"
>
<view class="result-item__main">
<text class="result-item__code">{{ item.WeldJointCode || '--' }}</text>
<text
v-if="item.PipelineCode"
class="result-item__sub"
>管线 {{ item.PipelineCode }}</text>
</view>
<u-icon
name="arrow-right"
:size="24"
color="#ccc"
/>
</view>
<u-empty
v-if="!loading && searched && listData.length === 0"
mode="search"
text="未找到焊口"
/>
<view
v-if="!searched && !loading"
class="result-hint"
>
<text>扫码或输入焊口/管线编号搜索</text>
</view>
<view
v-if="loading && listData.length > 0"
class="loading-more"
>
<u-loading
mode="circle"
size="28"
/>
<text>加载中...</text>
</view>
<view
v-if="!loading && !hasMore && listData.length > 0"
class="no-more"
>
<text>— 没有更多了 —</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
/**
* 焊口搜索中间页:扫码进入业务页,或关键字搜索后点选进入
* 入参:target(业务页路径)、title(导航标题)
*/
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { storeToRefs } from 'pinia'
import { useUserStore, useMenuStore } from '@/store'
import { getUrlParam } from '@/utils/request.js'
import { reqPipeJointWeldedJoints } from '@/api/hj.js'
const userStore = useUserStore()
const menuStore = useMenuStore()
const { currentProject } = storeToRefs(userStore)
/** 业务目标页,如 /scanpages/hj/material_sampling */
const targetPath = ref('')
const placeholder = ref('搜索焊口')
const autoFocus = ref(true)
const keyword = ref('')
const listData = ref([])
const loading = ref(false)
const searched = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = 20
let searchTimer = null
/**
* 从接口响应取出列表
* @param {any} data
*/
const pickList = (data) => {
if (!data) return []
if (Array.isArray(data)) return data
if (Array.isArray(data.getDataList)) return data.getDataList
if (Array.isArray(data.list)) return data.list
if (Array.isArray(data.records)) return data.records
if (Array.isArray(data.Items)) return data.Items
return []
}
const clearList = () => {
listData.value = []
hasMore.value = false
searched.value = false
}
/**
* 拉取焊口列表
* @param {boolean} isRefresh
*/
const loadList = async (isRefresh = true) => {
const kw = keyword.value.trim()
const projectId = currentProject.value?.ProjectId
if (!projectId) {
uni.showToast({ title: '请先选择项目', icon: 'none' })
return
}
if (!kw) {
clearList()
return
}
if (loading.value) return
if (!isRefresh && !hasMore.value) return
loading.value = true
if (isRefresh) {
currentPage.value = 1
hasMore.value = true
listData.value = []
}
try {
const res = await reqPipeJointWeldedJoints({
projectId,
keyword: kw,
pageIndex: currentPage.value,
pageSize,
})
const data = res?.data ?? {}
const pageData = pickList(data)
const totalPages = Number(data.pageCount) || 0
const totalCount = Number(data.totalCount) || 0
if (totalPages > 0) {
hasMore.value = currentPage.value < totalPages
} else if (totalCount > 0) {
hasMore.value = listData.value.length + pageData.length < totalCount
} else {
hasMore.value = pageData.length >= pageSize
}
listData.value = isRefresh ? pageData : listData.value.concat(pageData)
searched.value = true
} catch (e) {
console.error('焊口搜索失败:', e)
if (isRefresh) listData.value = []
searched.value = true
} finally {
loading.value = false
}
}
const handleSearchInput = () => {
if (searchTimer) clearTimeout(searchTimer)
searchTimer = setTimeout(() => {
const kw = keyword.value.trim()
if (!kw) {
clearList()
return
}
loadList(true)
}, 400)
}
const handleSearchConfirm = () => {
if (searchTimer) clearTimeout(searchTimer)
loadList(true)
}
const handleClear = () => {
keyword.value = ''
clearList()
}
const loadMore = () => {
if (!loading.value && hasMore.value && keyword.value.trim()) {
currentPage.value += 1
loadList(false)
}
}
/**
* 进入业务页
* @param {string} id WeldJointId
*/
const goTarget = (id) => {
if (!id) {
uni.showToast({ title: '缺少焊口信息', icon: 'none' })
return
}
if (!targetPath.value) {
uni.showToast({ title: '缺少目标页面', icon: 'none' })
return
}
if (!menuStore.canAccessPath(targetPath.value)) {
uni.showToast({ title: '当前岗位无此功能权限', icon: 'none' })
return
}
uni.navigateTo({
url: `${targetPath.value}?id=${encodeURIComponent(id)}`
})
}
/**
* 点选搜索结果
* @param {object} item
*/
const handleSelect = (item) => {
const id = item.WeldJointId || item.Id || item.id
goTarget(id)
}
/**
* 扫码:与首页菜单扫码一致,解析 id 后进入业务页
*/
const handleScan = () => {
uni.scanCode({
scanType: ['datamatrix', 'barCode', 'qrCode', 'wxCode', 'pdf417'],
success: (res) => {
const result = res.result || ''
let id = getUrlParam(result, 'id')
if (!id) {
id = String(result).trim()
}
if (!id) {
uni.showToast({ title: '未识别到焊口', icon: 'none' })
return
}
goTarget(id)
},
fail: (err) => {
if (err?.errMsg && String(err.errMsg).includes('cancel')) return
uni.showToast({ title: '扫码失败,请重试', icon: 'none' })
}
})
}
onLoad((opt) => {
targetPath.value = decodeURIComponent(opt.target || '')
const title = opt.title ? decodeURIComponent(opt.title) : '选择焊口'
uni.setNavigationBarTitle({ title })
if (!targetPath.value) {
uni.showToast({ title: '入口参数错误', icon: 'none' })
}
})
</script>
<style lang="scss" scoped>
.search-page {
min-height: 100vh;
background: #f5f6f8;
display: flex;
flex-direction: column;
}
.search-bar {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #fff;
position: sticky;
top: 0;
z-index: 10;
&__input-wrap {
flex: 1;
height: 72rpx;
display: flex;
align-items: center;
gap: 12rpx;
padding: 0 20rpx;
background: #f5f6f8;
border-radius: 36rpx;
}
&__input {
flex: 1;
font-size: 28rpx;
color: #333;
height: 72rpx;
}
}
.result-scroll {
flex: 1;
height: 0;
}
.result-list {
padding: 16rpx 24rpx 40rpx;
}
.result-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 24rpx;
margin-bottom: 16rpx;
background: #fff;
border-radius: 16rpx;
&__main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 8rpx;
}
&__code {
font-size: 30rpx;
font-weight: 600;
color: #1a2332;
font-family: monospace;
}
&__sub {
font-size: 24rpx;
color: #999;
}
}
.result-hint {
padding: 80rpx 0;
text-align: center;
font-size: 26rpx;
color: #bbb;
}
.loading-more,
.no-more {
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
padding: 24rpx;
font-size: 24rpx;
color: #999;
}
</style>