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

458 lines
9.2 KiB
Vue
Raw Normal View History

2026-08-18 18:05:16 +08:00
<template>
<view class="search-page">
2026-08-20 17:53:59 +08:00
<!-- 顶部固定筛选:扫码 + 管线号/焊口号 + 重置/查询 -->
<view class="filter-panel">
<view class="filter-panel__row">
<view
class="filter-panel__scan"
2026-08-18 18:05:16 +08:00
@click="handleScan"
2026-08-20 17:53:59 +08:00
>
<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>
2026-08-18 18:05:16 +08:00
</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"
>
2026-08-20 17:53:59 +08:00
<text>扫码,或输入管线号 / 焊口号后点击查询</text>
2026-08-18 18:05:16 +08:00
</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>
/**
2026-08-20 17:53:59 +08:00
* 焊口搜索中间页:扫码进入业务页,或按管线号/焊口号搜索后点选进入
2026-08-18 18:05:16 +08:00
* 入参: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('')
2026-08-20 17:53:59 +08:00
const pipelineCode = ref('')
const weldJointCode = ref('')
2026-08-18 18:05:16 +08:00
const listData = ref([])
const loading = ref(false)
const searched = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = 20
2026-08-20 17:53:59 +08:00
/**
* 是否有可搜索条件
*/
const hasKeyword = () => !!(pipelineCode.value.trim() || weldJointCode.value.trim())
2026-08-18 18:05:16 +08:00
/**
* 从接口响应取出列表
* @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
}
2026-08-20 17:53:59 +08:00
if (!hasKeyword()) {
uni.showToast({ title: '请输入管线号或焊口号', icon: 'none' })
2026-08-18 18:05:16 +08:00
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,
2026-08-20 17:53:59 +08:00
pipelineCode: pipelineCode.value.trim() || undefined,
weldJointCode: weldJointCode.value.trim() || undefined,
2026-08-18 18:05:16 +08:00
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
}
}
2026-08-20 17:53:59 +08:00
/** 点击查询 */
const handleSearch = () => {
2026-08-18 18:05:16 +08:00
loadList(true)
}
2026-08-20 17:53:59 +08:00
/** 重置条件与结果 */
const handleReset = () => {
pipelineCode.value = ''
weldJointCode.value = ''
2026-08-18 18:05:16 +08:00
clearList()
}
const loadMore = () => {
2026-08-20 17:53:59 +08:00
if (!loading.value && hasMore.value && hasKeyword()) {
2026-08-18 18:05:16 +08:00
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;
}
2026-08-20 17:53:59 +08:00
.filter-panel {
flex-shrink: 0;
2026-08-18 18:05:16 +08:00
padding: 16rpx 24rpx;
background: #fff;
position: sticky;
top: 0;
z-index: 10;
2026-08-20 17:53:59 +08:00
&__row {
display: flex;
align-items: center;
gap: 16rpx;
}
&__scan {
flex-shrink: 0;
width: 72rpx;
2026-08-18 18:05:16 +08:00
height: 72rpx;
display: flex;
align-items: center;
2026-08-20 17:53:59 +08:00
justify-content: center;
background: #f5f6f8;
border-radius: 16rpx;
}
&__fields {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
2026-08-18 18:05:16 +08:00
gap: 12rpx;
2026-08-20 17:53:59 +08:00
}
&__item {
min-width: 0;
height: 72rpx;
display: flex;
align-items: center;
padding: 0 16rpx;
2026-08-18 18:05:16 +08:00
background: #f5f6f8;
2026-08-20 17:53:59 +08:00
border-radius: 12rpx;
&--pipe {
flex: 1;
}
&--joint {
flex: none;
width: 140rpx;
}
2026-08-18 18:05:16 +08:00
}
&__input {
flex: 1;
2026-08-20 17:53:59 +08:00
min-width: 0;
height: 72rpx;
2026-08-18 18:05:16 +08:00
font-size: 28rpx;
color: #333;
2026-08-20 17:53:59 +08:00
}
&__actions {
display: flex;
align-items: center;
gap: 16rpx;
margin-top: 16rpx;
}
&__btn {
flex: 1;
2026-08-18 18:05:16 +08:00
height: 72rpx;
2026-08-20 17:53:59 +08:00
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
border-radius: 12rpx;
&--reset {
color: #666;
background: #f5f6f8;
}
&--search {
color: #fff;
background: #2979ff;
}
2026-08-18 18:05:16 +08:00
}
}
.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>