| | |
| | | <template> |
| | | <view> |
| | | <view class="page-container"> |
| | | <!-- 订单信息头部 --> |
| | | <view class="order-header" v-if="order"> |
| | | <view class="header-content"> |
| | | <view class="header-row"> |
| | | <text class="header-label">单据号</text> |
| | | <text class="header-value">{{order.orderNo}}</text> |
| | | </view> |
| | | <view class="header-row"> |
| | | <text class="header-label">单据类型</text> |
| | | <text class="header-value">{{order.docType$ || '-'}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 搜索框 --> |
| | | <view class="search-bar"> |
| | | <uni-search-bar v-model="condition" placeholder=" 扫码 / 输入" bgColor="#EEEEEE" @input="search" /> |
| | | </view> |
| | | <view class="card" v-show="item.enableQty >0" v-for="item in menuList" @click="chose(item)"> |
| | | <view class="tag-item">单据号: {{item.orderNo}}</view> |
| | | <view class="tag-item">物料码: {{item.matnr}}</view> |
| | | <view class="tag-item">物料名称: {{item.maktx}}</view> |
| | | <view class="tag-item">规格: {{item.specs}}</view> |
| | | <view class="tag-item">批次: {{item.batch}}</view> |
| | | <view class="tag-item">数量: {{item.anfme}}</view> |
| | | <view class="tag-item">剩余数量: {{item.enableQty}}</view> |
| | | <uni-search-bar v-model="condition" placeholder=" 扫码 / 输入物料" bgColor="#F5F5F5" @confirm="search" @cancel="onCancelSearch" /> |
| | | </view> |
| | | |
| | | <!-- 明细列表 --> |
| | | <view class="detl-list"> |
| | | <view class="detl-card" v-for="(item, index) in filterList" :key="index" @click="chose(item)"> |
| | | <!-- 卡片头部 --> |
| | | <view class="card-header"> |
| | | <view class="mat-info"> |
| | | <text class="mat-code">{{item.matnr}}</text> |
| | | <text class="mat-name">{{item.maktx || '-'}}</text> |
| | | </view> |
| | | <view class="qty-badge" :class="item.enableQty > 0 ? 'badge-active' : 'badge-done'"> |
| | | <text class="qty-text">剩余 {{item.enableQty}}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 卡片内容 --> |
| | | <view class="card-body"> |
| | | <view class="info-grid"> |
| | | <view class="info-item"> |
| | | <text class="info-label">规格</text> |
| | | <text class="info-value">{{item.specs || '-'}}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">批次</text> |
| | | <text class="info-value">{{item.batch || '-'}}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">订单数量</text> |
| | | <text class="info-value highlight">{{item.anfme}}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">已完成</text> |
| | | <text class="info-value">{{item.anfme - item.enableQty}}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 进度条 --> |
| | | <view class="progress-wrap"> |
| | | <view class="progress-bar"> |
| | | <view class="progress-fill" :style="{width: getProgress(item) + '%'}"></view> |
| | | </view> |
| | | <text class="progress-text">{{getProgress(item)}}%</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 卡片底部 --> |
| | | <view class="card-footer" v-if="item.enableQty > 0"> |
| | | <text class="action-text">点击组托入库</text> |
| | | <uni-icons type="right" size="14" color="#667eea"></uni-icons> |
| | | </view> |
| | | <view class="card-footer card-footer-done" v-else> |
| | | <text class="done-text">已完成</text> |
| | | <uni-icons type="checkmarkempty" size="14" color="#28a745"></uni-icons> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 空状态 --> |
| | | <view class="empty-state" v-if="filterList.length === 0 && !loading"> |
| | | <uni-icons type="info" size="60" color="#CCCCCC"></uni-icons> |
| | | <text class="empty-text">暂无明细数据</text> |
| | | </view> |
| | | |
| | | <!-- 统计信息 --> |
| | | <view class="stats-bar" v-if="menuList.length > 0"> |
| | | <view class="stats-item"> |
| | | <text class="stats-value">{{menuList.length}}</text> |
| | | <text class="stats-label">总明细</text> |
| | | </view> |
| | | <view class="stats-divider"></view> |
| | | <view class="stats-item"> |
| | | <text class="stats-value">{{pendingCount}}</text> |
| | | <text class="stats-label">待处理</text> |
| | | </view> |
| | | <view class="stats-divider"></view> |
| | | <view class="stats-item"> |
| | | <text class="stats-value">{{completedCount}}</text> |
| | | <text class="stats-label">已完成</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | |
| | | export default { |
| | | data() { |
| | | return { |
| | | data:'', |
| | | condition:'', |
| | | data: '', |
| | | condition: '', |
| | | menuList: [], |
| | | order:'', |
| | | order: '', |
| | | baseUrl: '', |
| | | token: '', |
| | | loading: false, |
| | | } |
| | | }, |
| | | computed: { |
| | | // 过滤后的列表(优先显示有剩余数量的) |
| | | filterList() { |
| | | if (!this.condition.trim()) { |
| | | // 排序:有剩余数量的排前面 |
| | | return [...this.menuList].sort((a, b) => { |
| | | if (a.enableQty > 0 && b.enableQty <= 0) return -1; |
| | | if (a.enableQty <= 0 && b.enableQty > 0) return 1; |
| | | return 0; |
| | | }); |
| | | } |
| | | const keyword = this.condition.toLowerCase(); |
| | | return this.menuList.filter(item => { |
| | | return (item.matnr && item.matnr.toLowerCase().includes(keyword)) || |
| | | (item.maktx && item.maktx.toLowerCase().includes(keyword)) || |
| | | (item.batch && item.batch.toLowerCase().includes(keyword)); |
| | | }); |
| | | }, |
| | | // 待处理数量 |
| | | pendingCount() { |
| | | return this.menuList.filter(item => item.enableQty > 0).length; |
| | | }, |
| | | // 已完成数量 |
| | | completedCount() { |
| | | return this.menuList.filter(item => item.enableQty <= 0).length; |
| | | } |
| | | }, |
| | | onLoad() { |
| | | let that = this |
| | | let that = this; |
| | | const eventChannel = this.getOpenerEventChannel(); |
| | | eventChannel.on('data', function(data) { |
| | | that.order = data.data |
| | | that.getOrderNoList(that.order) |
| | | }); |
| | | if (eventChannel) { |
| | | eventChannel.on('data', function(data) { |
| | | that.order = data.data; |
| | | that.getOrderNoList(that.order); |
| | | }); |
| | | } |
| | | }, |
| | | onShow() { |
| | | let that = this |
| | | this.baseUrl = uni.getStorageSync('baseUrl'); |
| | | this.token = uni.getStorageSync('token'); |
| | | that.getOrderNoList(that.order) |
| | | if (this.order) { |
| | | this.getOrderNoList(this.order); |
| | | } |
| | | }, |
| | | methods: { |
| | | search(){ |
| | | let that = this |
| | | // 计算进度 |
| | | getProgress(item) { |
| | | if (!item.anfme || item.anfme === 0) return 0; |
| | | const progress = ((item.anfme - item.enableQty) / item.anfme * 100).toFixed(0); |
| | | return Math.min(100, Math.max(0, progress)); |
| | | }, |
| | | // 搜索 |
| | | search() { |
| | | if (!this.condition.trim()) { |
| | | this.getOrderNoList(this.order); |
| | | return; |
| | | } |
| | | let that = this; |
| | | that.loading = true; |
| | | uni.request({ |
| | | url: that.baseUrl + '/orderDetl/search/pda/auth', |
| | | data: { |
| | | url: that.baseUrl + '/orderDetl/search/pda/auth', |
| | | data: { |
| | | condition: that.condition, |
| | | order: that.order.orderNo |
| | | }, |
| | | // method:"GET", |
| | | header: { |
| | | 'token':uni.getStorageSync('token'), |
| | | }, |
| | | }, |
| | | header: { |
| | | 'token': uni.getStorageSync('token'), |
| | | }, |
| | | success(result) { |
| | | console.log(result); |
| | | var res = result.data |
| | | if (res.code === 200 ) { |
| | | that.menuList = res.data |
| | | // that.save() |
| | | } else if (res.code == 403) { |
| | | uni.showToast({title: res.msg, icon: "none", position: 'top'}) |
| | | var res = result.data; |
| | | if (res.code === 200) { |
| | | that.menuList = res.data || []; |
| | | } else if (res.code === 403) { |
| | | uni.showToast({ title: res.msg, icon: "none", position: 'top' }); |
| | | setTimeout(() => { |
| | | uni.reLaunch({ |
| | | url: '../login/login' |
| | | }); |
| | | uni.reLaunch({ url: '../login/login' }); |
| | | }, 1000); |
| | | } else { |
| | | uni.showToast({title: res.msg, icon: "none",position: 'top'}) |
| | | uni.showToast({ title: res.msg, icon: "none", position: 'top' }); |
| | | } |
| | | }, |
| | | fail() { |
| | | uni.showToast({ title: '搜索请求失败', icon: "none", position: 'top' }); |
| | | }, |
| | | complete() { |
| | | that.loading = false; |
| | | } |
| | | }); |
| | | }, |
| | | // 取消搜索 |
| | | onCancelSearch() { |
| | | this.condition = ''; |
| | | this.getOrderNoList(this.order); |
| | | }, |
| | | // 获取订单明细列表 |
| | | getOrderNoList(order) { |
| | | let that = this |
| | | if (!order || !order.orderNo) return; |
| | | let that = this; |
| | | that.loading = true; |
| | | uni.request({ |
| | | url: uni.getStorageSync('baseUrl') + '/orderDetl/list/pda/auth', |
| | | url: uni.getStorageSync('baseUrl') + '/order/pakin/orderDetl/list/pda/auth', |
| | | method: 'POST', |
| | | data: { |
| | | header: { |
| | | 'token': uni.getStorageSync('token'), |
| | | }, |
| | | data: { |
| | | orderNo: order.orderNo |
| | | }, |
| | | success(res) { |
| | | res = res.data |
| | | that.menuList = res.data |
| | | res = res.data; |
| | | if (res.code === 200) { |
| | | that.menuList = res.data || []; |
| | | } else { |
| | | that.menuList = res.data || []; |
| | | } |
| | | }, |
| | | fail() { |
| | | uni.showToast({ title: '获取明细失败', icon: "none", position: 'top' }); |
| | | }, |
| | | complete() { |
| | | that.loading = false; |
| | | } |
| | | }) |
| | | }); |
| | | }, |
| | | // 选择明细进行组托 |
| | | chose(item) { |
| | | let that = this |
| | | if (item.enableQty <= 0) { |
| | | uni.showToast({ title: '该明细已完成', icon: "none", position: 'top' }); |
| | | return; |
| | | } |
| | | let that = this; |
| | | uni.navigateTo({ |
| | | url: "../order/orderPakin2", |
| | | success: function(res) { |
| | | // 通过eventChannel向被打开页面传送数据 向另外一个页面传递值的 |
| | | res.eventChannel.emit('orderItem', { |
| | | item: item |
| | | }) |
| | | }); |
| | | }, |
| | | events: { |
| | | // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据 另外一个页面传过来的 |
| | | acceptDataFromOpenedPage: function(data) { |
| | | // that.matnr = data.data |
| | | // that.input(that.matnr) |
| | | // 返回后刷新数据 |
| | | }, |
| | | }, |
| | | |
| | | |
| | | }); |
| | | } |
| | | } |
| | |
| | | </script> |
| | | |
| | | <style> |
| | | .card { |
| | | margin: 20rpx; |
| | | padding: 30rpx; |
| | | background-color: #157ec1; |
| | | border-radius: 20rpx; |
| | | color: #FFF; |
| | | .page-container { |
| | | min-height: 100vh; |
| | | background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%); |
| | | padding-bottom: 120rpx; |
| | | } |
| | | .tag-item { |
| | | width: 100%; |
| | | min-height: 60rpx; |
| | | line-height: 2; |
| | | padding-left: 50rpx; |
| | | |
| | | /* 订单头部 */ |
| | | .order-header { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | padding: 16rpx 20rpx; |
| | | } |
| | | |
| | | .header-content { |
| | | background: rgba(255, 255, 255, 0.15); |
| | | border-radius: 10rpx; |
| | | padding: 12rpx 16rpx; |
| | | } |
| | | |
| | | .header-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 4rpx 0; |
| | | } |
| | | |
| | | .header-label { |
| | | font-size: 24rpx; |
| | | color: rgba(255, 255, 255, 0.7); |
| | | } |
| | | |
| | | .header-value { |
| | | font-size: 26rpx; |
| | | color: #ffffff; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* 搜索栏 */ |
| | | .search-bar { |
| | | padding: 0rpx 14rpx; |
| | | background: #ffffff; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06); |
| | | } |
| | | |
| | | /* 明细列表 */ |
| | | .detl-list { |
| | | padding: 0 20rpx; |
| | | } |
| | | |
| | | .detl-card { |
| | | background: #ffffff; |
| | | border-radius: 12rpx; |
| | | margin-top: 12rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06); |
| | | overflow: hidden; |
| | | transition: transform 0.2s ease; |
| | | } |
| | | |
| | | .detl-card:active { |
| | | transform: scale(0.98); |
| | | } |
| | | |
| | | /* 卡片头部 */ |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | padding: 14rpx 16rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .mat-info { |
| | | flex: 1; |
| | | padding-right: 12rpx; |
| | | } |
| | | |
| | | .mat-code { |
| | | font-size: 26rpx; |
| | | color: #303133; |
| | | font-weight: 600; |
| | | display: block; |
| | | } |
| | | |
| | | .mat-name { |
| | | font-size: 22rpx; |
| | | color: #909399; |
| | | margin-top: 4rpx; |
| | | display: block; |
| | | } |
| | | |
| | | .qty-badge { |
| | | padding: 4rpx 12rpx; |
| | | border-radius: 16rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .badge-active { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | } |
| | | |
| | | .badge-done { |
| | | background: #e8f5e9; |
| | | } |
| | | |
| | | .qty-text { |
| | | font-size: 20rpx; |
| | | font-weight: 500; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .badge-done .qty-text { |
| | | color: #28a745; |
| | | } |
| | | |
| | | /* 卡片内容 */ |
| | | .card-body { |
| | | padding: 12rpx 16rpx; |
| | | } |
| | | |
| | | .info-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .info-item { |
| | | width: 50%; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 20rpx; |
| | | color: #909399; |
| | | display: block; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 24rpx; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | display: block; |
| | | margin-top: 2rpx; |
| | | } |
| | | |
| | | .info-value.highlight { |
| | | color: #667eea; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | /* 进度条 */ |
| | | .progress-wrap { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-top: 8rpx; |
| | | } |
| | | |
| | | .progress-bar { |
| | | flex: 1; |
| | | height: 8rpx; |
| | | background: #e8e8e8; |
| | | border-radius: 4rpx; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .progress-fill { |
| | | height: 100%; |
| | | background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 4rpx; |
| | | transition: width 0.3s ease; |
| | | } |
| | | |
| | | .progress-text { |
| | | font-size: 20rpx; |
| | | color: #909399; |
| | | margin-left: 12rpx; |
| | | min-width: 50rpx; |
| | | text-align: right; |
| | | } |
| | | |
| | | /* 卡片底部 */ |
| | | .card-footer { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-end; |
| | | padding: 12rpx 16rpx; |
| | | border-top: 1rpx solid #f0f0f0; |
| | | background: #fafafa; |
| | | } |
| | | |
| | | .card-footer-done { |
| | | background: #f0fff4; |
| | | } |
| | | |
| | | .action-text { |
| | | font-size: 24rpx; |
| | | color: #667eea; |
| | | margin-right: 6rpx; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .done-text { |
| | | font-size: 24rpx; |
| | | color: #28a745; |
| | | margin-right: 6rpx; |
| | | } |
| | | |
| | | /* 空状态 */ |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 60rpx 0; |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 26rpx; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | /* 统计栏 */ |
| | | .stats-bar { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-around; |
| | | background: #ffffff; |
| | | padding: 16rpx 0; |
| | | box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06); |
| | | } |
| | | |
| | | .stats-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | flex: 1; |
| | | } |
| | | |
| | | .stats-value { |
| | | font-size: 32rpx; |
| | | color: #303133; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .stats-label { |
| | | font-size: 20rpx; |
| | | color: #909399; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .stats-divider { |
| | | width: 1rpx; |
| | | height: 50rpx; |
| | | background: #e8e8e8; |
| | | } |
| | | </style> |