386 lines
7.8 KiB
Vue
386 lines
7.8 KiB
Vue
|
|
<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>
|