Files
shj-app/scanpages/hj/weld_joint_search.vue
2026-08-20 17:53:59 +08:00

458 lines
9.2 KiB
Vue
Raw Permalink 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="filter-panel">
<view class="filter-panel__row">
<view
class="filter-panel__scan"
@click="handleScan"
>
<u-icon
name="scan"
:size="36"
color="#2979ff"
/>
</view>
<view class="filter-panel__fields">
<view class="filter-panel__item filter-panel__item--pipe">
<input
class="filter-panel__input"
v-model="pipelineCode"
type="text"
placeholder="管线号"
confirm-type="search"
@confirm="handleSearch"
/>
</view>
<view class="filter-panel__item filter-panel__item--joint">
<input
class="filter-panel__input"
v-model="weldJointCode"
type="text"
placeholder="焊口号"
confirm-type="search"
@confirm="handleSearch"
/>
</view>
</view>
</view>
<view class="filter-panel__actions">
<view
class="filter-panel__btn filter-panel__btn--reset"
@click="handleReset"
>重置</view>
<view
class="filter-panel__btn filter-panel__btn--search"
@click="handleSearch"
>查询</view>
</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 pipelineCode = ref('')
const weldJointCode = ref('')
const listData = ref([])
const loading = ref(false)
const searched = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = 20
/**
* 是否有可搜索条件
*/
const hasKeyword = () => !!(pipelineCode.value.trim() || weldJointCode.value.trim())
/**
* 从接口响应取出列表
* @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 projectId = currentProject.value?.ProjectId
if (!projectId) {
uni.showToast({ title: '请先选择项目', icon: 'none' })
return
}
if (!hasKeyword()) {
uni.showToast({ title: '请输入管线号或焊口号', icon: 'none' })
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,
pipelineCode: pipelineCode.value.trim() || undefined,
weldJointCode: weldJointCode.value.trim() || undefined,
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 handleSearch = () => {
loadList(true)
}
/** 重置条件与结果 */
const handleReset = () => {
pipelineCode.value = ''
weldJointCode.value = ''
clearList()
}
const loadMore = () => {
if (!loading.value && hasMore.value && hasKeyword()) {
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;
}
.filter-panel {
flex-shrink: 0;
padding: 16rpx 24rpx;
background: #fff;
position: sticky;
top: 0;
z-index: 10;
&__row {
display: flex;
align-items: center;
gap: 16rpx;
}
&__scan {
flex-shrink: 0;
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f5f6f8;
border-radius: 16rpx;
}
&__fields {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
gap: 12rpx;
}
&__item {
min-width: 0;
height: 72rpx;
display: flex;
align-items: center;
padding: 0 16rpx;
background: #f5f6f8;
border-radius: 12rpx;
&--pipe {
flex: 1;
}
&--joint {
flex: none;
width: 140rpx;
}
}
&__input {
flex: 1;
min-width: 0;
height: 72rpx;
font-size: 28rpx;
color: #333;
}
&__actions {
display: flex;
align-items: center;
gap: 16rpx;
margin-top: 16rpx;
}
&__btn {
flex: 1;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
border-radius: 12rpx;
&--reset {
color: #666;
background: #f5f6f8;
}
&--search {
color: #fff;
background: #2979ff;
}
}
}
.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>