Files
klp-mono/apps/hand-factory/pages/todo/index.vue
王文昊 eeff149c17 refactor(todo page): 统一数据字段映射与key绑定
1.  将列表循环的key从item.actionId改为item.coilId,确保唯一标识正确
2.  新增mapDataFields方法统一处理后端字段到前端字段的映射
3.  替换fetchList中原有的直接赋值逻辑,使用映射后的新数据
4.  简化handleViewRecord和handleViewDetail中的coilId获取逻辑
2026-05-23 16:50:46 +08:00

502 lines
12 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="todo-container">
<!-- 自定义导航栏 -->
<view class="custom-nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<text class="nav-title">待办事项</text>
</view>
</view>
<!-- Tab切换 -->
<view class="tab-bar">
<view
v-for="tab in tabs"
:key="tab.key"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="handleTabChange(tab.key)"
>
<text class="tab-text">{{ tab.label }}</text>
<text v-if="tab.badge > 0" class="tab-badge">{{ tab.badge }}</text>
</view>
</view>
<!-- 筛选栏 -->
<filter-bar
:loading="loading"
:total="total"
@search="handleSearch"
@reset="handleReset"
/>
<!-- 列表内容 -->
<scroll-view
scroll-y
class="list-container"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
>
<!-- 待贴标签列表 -->
<view v-if="activeTab === 'label'" class="coil-list">
<coil-card
v-for="item in list"
:key="item.coilId"
:data="item"
@relabel="handleRelabel"
@view-record="handleViewRecord"
@view-detail="handleViewDetail"
/>
</view>
<!-- 其他Tab占位 -->
<view v-else class="placeholder-page">
<text class="placeholder-icon">🚧</text>
<text class="placeholder-text">功能开发中</text>
</view>
<!-- 空状态 -->
<view v-if="activeTab === 'label' && list.length === 0 && !loading" class="empty-state">
<text class="empty-icon">📭</text>
<text class="empty-text">暂无待办事项</text>
</view>
<!-- 加载状态 -->
<view v-if="loading && list.length === 0" class="loading-state">
<uni-load-more status="loading" />
</view>
<!-- 加载更多 -->
<view v-if="activeTab === 'label' && list.length > 0" class="load-more-wrapper" @click="onLoadMore">
<uni-load-more
:status="loadMoreStatus"
:content-text="{ contentdown: '点击加载更多', contentrefresh: '加载中...', contentnomore: '没有更多了' }"
/>
</view>
</scroll-view>
<!-- 记录弹窗 -->
<record-popup ref="recordPopup" />
<!-- 重贴标签弹窗 -->
<relabel-popup ref="relabelPopup" @success="handleRelabelSuccess" />
</view>
</template>
<script>
import { listMaterialCoil } from '@/api/wms/coil'
import FilterBar from './components/filter-bar.vue'
import CoilCard from './components/coil-card.vue'
import RecordPopup from './components/record-popup.vue'
import RelabelPopup from './components/relabel-popup.vue'
export default {
components: {
FilterBar,
CoilCard,
RecordPopup,
RelabelPopup
},
data() {
return {
statusBarHeight: 0,
tabs: [
{ key: 'label', label: '待贴标签', badge: 0 },
{ key: 'inspect', label: '检验任务', badge: 0 },
{ key: 'approval', label: '质保书审批', badge: 0 },
{ key: 'other', label: '其他代办', badge: 0 }
],
activeTab: 'label',
list: [],
loading: false,
refreshing: false,
query: {
pageNum: 1,
pageSize: 10,
enterCoilNo: '',
currentCoilNo: '',
itemName: '',
itemSpecification: '',
itemMaterial: '',
itemManufacturer: '',
hasTransferType: true // 待贴标签只显示有调拨类型的钢卷
},
total: 0
}
},
created() {
// 获取状态栏高度
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight || 0
},
computed: {
loadMoreStatus() {
if (this.loading) return 'loading'
if (this.list.length >= this.total) return 'noMore'
return 'more'
}
},
onLoad() {
this.fetchList()
},
methods: {
// 切换Tab
handleTabChange(key) {
this.activeTab = key
if (key === 'label') {
this.fetchList()
}
},
// 数据字段映射 - 将后端字段映射到前端使用的字段
// 后端 WmsMaterialCoilVo 字段coilId, enterCoilNo, currentCoilNo,
// itemName, specification, material, manufacturer,
// actualWarehouseName, warehouseName, remark, transferType
mapDataFields(row) {
return {
// 钢卷ID
coilId: row.coilId,
// 钢卷号
enterCoilNo: row.enterCoilNo || '-',
currentCoilNo: row.currentCoilNo || '-',
// 产品信息(注意后端使用 specification/material/manufacturer非 item 前缀)
itemName: row.itemName || '-',
itemSpecification: row.specification || '-',
itemMaterial: row.material || '-',
itemManufacturer: row.manufacturer || '-',
// 库区信息
actualWarehouseName: row.actualWarehouseName || row.warehouseName || '-',
// 其他字段
remark: row.remark || '-',
transferType: row.transferType || '',
// 改判原因(后端 WmsMaterialCoilVo 无此字段,仅通过 listWithRejudge 接口传入)
changeReason: row.changeReason || ''
}
},
// 获取列表数据
async fetchList(isLoadMore = false) {
if (this.loading) return
this.loading = true
try {
console.log('开始获取列表,查询参数:', this.query)
const res = await listMaterialCoil(this.query)
console.log('获取列表响应:', res)
const rows = res.rows || []
this.total = res.total || 0
// 映射数据字段
const mappedRows = rows.map(row => this.mapDataFields(row))
console.log('映射后的数据:', { mappedRows, total: this.total })
if (isLoadMore) {
this.list = [...this.list, ...mappedRows]
} else {
this.list = mappedRows
}
// 更新待贴标签数量
const labelTab = this.tabs.find(t => t.key === 'label')
if (labelTab) {
labelTab.badge = this.total
}
} catch (error) {
console.error('获取列表失败:', error)
uni.showToast({
title: '获取数据失败: ' + (error.message || '未知错误'),
icon: 'none'
})
} finally {
this.loading = false
this.refreshing = false
}
},
// 搜索
handleSearch(form) {
this.query = {
...this.query,
...form,
pageNum: 1
}
this.fetchList()
},
// 重置
handleReset() {
this.query = {
pageNum: 1,
pageSize: 10,
enterCoilNo: '',
currentCoilNo: '',
itemName: '',
itemSpecification: '',
itemMaterial: '',
itemManufacturer: '',
hasTransferType: true
}
this.fetchList()
},
// 下拉刷新
onRefresh() {
this.refreshing = true
this.query.pageNum = 1
this.fetchList()
},
// 加载更多
onLoadMore() {
console.log('触发加载更多', {
listLength: this.list.length,
total: this.total,
loading: this.loading,
pageNum: this.query.pageNum
})
if (this.list.length >= this.total || this.loading) {
console.log('加载更多被阻止:已到最后一页或正在加载中')
return
}
this.query.pageNum++
console.log('加载第', this.query.pageNum, '页')
this.fetchList(true)
},
// 重贴标签
handleRelabel(coilInfo) {
this.$refs.relabelPopup.open(coilInfo)
},
// 重贴标签成功回调
handleRelabelSuccess() {
this.fetchList()
},
// 查看记录
handleViewRecord(coilInfo) {
const coilId = coilInfo.coilId
if (!coilId) {
uni.showToast({
title: '无法获取钢卷ID',
icon: 'none'
})
return
}
this.$refs.recordPopup.open(coilId)
},
// 查看详情
handleViewDetail(coilInfo) {
const coilId = coilInfo.coilId
if (!coilId) {
uni.showToast({
title: '无法获取钢卷ID',
icon: 'none'
})
return
}
uni.navigateTo({
url: `/pages/todo/coil-detail?coilId=${coilId}`
})
}
}
}
</script>
<style scoped lang="scss">
.todo-container {
min-height: 100vh;
background: #f5f7fa;
display: flex;
flex-direction: column;
.custom-nav-bar {
background: #ffffff;
border-bottom: 1rpx solid #f0f0f0;
.nav-content {
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
}
}
.tab-bar {
display: flex;
background: #ffffff;
padding: 0 10rpx;
border-bottom: 1rpx solid #f0f0f0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.tab-item {
flex: 0 0 auto;
min-width: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx 16rpx;
position: relative;
.tab-text {
font-size: 26rpx;
color: #666666;
white-space: nowrap;
line-height: 1.2;
}
.tab-badge {
position: absolute;
top: 8rpx;
right: 8rpx;
min-width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
background: #ff4d4f;
color: #ffffff;
font-size: 20rpx;
border-radius: 16rpx;
padding: 0 8rpx;
}
&.active {
.tab-text {
color: #1a73e8;
font-weight: 600;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background: #1a73e8;
border-radius: 2rpx;
}
}
}
}
.list-container {
flex: 1;
padding: 20rpx;
box-sizing: border-box;
.coil-list {
padding-bottom: 40rpx;
}
.load-more-wrapper {
padding: 30rpx 0;
display: flex;
justify-content: center;
align-items: center;
&:active {
opacity: 0.7;
}
}
.placeholder-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
.placeholder-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.placeholder-text {
font-size: 32rpx;
color: #999999;
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
.empty-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 32rpx;
color: #999999;
}
}
.loading-state {
padding: 100rpx 0;
}
}
}
/* 浏览器环境适配 */
@media screen and (min-width: 768px) {
.todo-container {
max-width: 750rpx;
margin: 0 auto;
.tab-bar {
.tab-item {
min-width: 160rpx;
.tab-text {
font-size: 28rpx;
}
}
}
}
}
/* H5 浏览器特定样式 */
/* #ifdef H5 */
.todo-container {
.tab-bar {
.tab-item {
cursor: pointer;
&:hover {
background: rgba(26, 115, 232, 0.05);
}
}
}
.coil-card {
cursor: pointer;
&:hover {
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.1);
}
}
}
/* #endif */
</style>