feat: 添加待办事项模块及相关功能
新增待办事项页面,包含钢卷列表展示、筛选功能、钢卷详情页,支持重贴标签操作和查看改判/调拨记录,同时添加底部 tab 栏入口和对应图标资源
This commit is contained in:
19
apps/hand-factory/api/wms/todo.js
Normal file
19
apps/hand-factory/api/wms/todo.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询改判记录(根据钢卷ID)
|
||||
export function listChangeHistory(coilId) {
|
||||
return request({
|
||||
url: '/wms/coilQualityRejudge/list',
|
||||
method: 'get',
|
||||
params: { coilId }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询调拨记录(根据钢卷ID)
|
||||
export function listTransferHistory(coilId) {
|
||||
return request({
|
||||
url: '/wms/transferOrderItem/list',
|
||||
method: 'get',
|
||||
params: { coilId }
|
||||
})
|
||||
}
|
||||
@@ -100,6 +100,19 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "发货"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/todo/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "待办事项",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/todo/coil-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "钢卷详情"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
@@ -116,24 +129,18 @@
|
||||
"selectedIconPath": "/static/images/tabbar/home_.png",
|
||||
"iconPath": "/static/images/tabbar/home.png"
|
||||
},
|
||||
{
|
||||
"text": "待办",
|
||||
"pagePath": "pages/todo/index",
|
||||
"selectedIconPath": "/static/images/tabbar/todo_.png",
|
||||
"iconPath": "/static/images/tabbar/todo.png"
|
||||
},
|
||||
{
|
||||
"text": "扫码",
|
||||
"pagePath": "pages/easycode/easycode",
|
||||
"selectedIconPath": "/static/images/tabbar/work_.png",
|
||||
"iconPath": "/static/images/tabbar/work.png"
|
||||
},
|
||||
// {
|
||||
// "text": "收货",
|
||||
// "pagePath": "pages/receive/receive",
|
||||
// "selectedIconPath": "/static/images/tabbar/receive_.png",
|
||||
// "iconPath": "/static/images/tabbar/receive.png"
|
||||
// },
|
||||
// {
|
||||
// "text": "查找",
|
||||
// "pagePath": "pages/search/search",
|
||||
// "selectedIconPath": "/static/images/tabbar/search_.png",
|
||||
// "iconPath": "/static/images/tabbar/search.png"
|
||||
// },
|
||||
{
|
||||
"text": "报餐",
|
||||
"pagePath": "pages/meal/meal",
|
||||
|
||||
387
apps/hand-factory/pages/todo/coil-detail.vue
Normal file
387
apps/hand-factory/pages/todo/coil-detail.vue
Normal file
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<view class="detail-container">
|
||||
<!-- 头部信息卡片 -->
|
||||
<view class="header-card">
|
||||
<view class="header-title">
|
||||
<text class="title-text">钢卷信息</text>
|
||||
<text class="coil-no">{{ coilInfo.currentCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="header-subtitle">
|
||||
<text class="subtitle-text">入场钢卷号: {{ coilInfo.enterCoilNo || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基础信息 -->
|
||||
<view class="info-section">
|
||||
<view class="section-title">
|
||||
<text class="title-icon">📋</text>
|
||||
<text class="title-text">基础信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">厂家卷号</text>
|
||||
<text class="value">{{ coilInfo.factoryCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">物料类型</text>
|
||||
<text class="value">{{ coilInfo.itemType || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">产品名称</text>
|
||||
<text class="value">{{ coilInfo.itemName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">规格</text>
|
||||
<text class="value">{{ coilInfo.specification || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">材质</text>
|
||||
<text class="value">{{ coilInfo.material || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">厂家</text>
|
||||
<text class="value">{{ coilInfo.manufacturer || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 库存信息 -->
|
||||
<view class="info-section">
|
||||
<view class="section-title">
|
||||
<text class="title-icon">📦</text>
|
||||
<text class="title-text">库存信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">逻辑库区</text>
|
||||
<text class="value">{{ coilInfo.warehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">实际库区</text>
|
||||
<text class="value">{{ coilInfo.actualWarehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">净重(吨)</text>
|
||||
<text class="value">{{ coilInfo.netWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">毛重(吨)</text>
|
||||
<text class="value">{{ coilInfo.grossWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">长度(米)</text>
|
||||
<text class="value">{{ coilInfo.length || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">质量状态</text>
|
||||
<text class="value" :class="getQualityClass(coilInfo.qualityStatus)">
|
||||
{{ coilInfo.qualityStatus || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加工信息 -->
|
||||
<view class="info-section">
|
||||
<view class="section-title">
|
||||
<text class="title-icon">⚙️</text>
|
||||
<text class="title-text">加工信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">表面处理</text>
|
||||
<text class="value">{{ coilInfo.surfaceTreatment || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">镀层质量</text>
|
||||
<text class="value">{{ coilInfo.coatingWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">包装要求</text>
|
||||
<text class="value">{{ coilInfo.packaging || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">切边</text>
|
||||
<text class="value">{{ coilInfo.cuttingEdge || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">班组</text>
|
||||
<text class="value">{{ coilInfo.team || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">备注</text>
|
||||
<text class="value">{{ coilInfo.remark || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作信息 -->
|
||||
<view class="info-section">
|
||||
<view class="section-title">
|
||||
<text class="title-icon">👤</text>
|
||||
<text class="title-text">操作信息</text>
|
||||
</view>
|
||||
<view class="info-list">
|
||||
<view class="list-item">
|
||||
<text class="label">创建人</text>
|
||||
<text class="value">{{ coilInfo.creator || '-' }}</text>
|
||||
</view>
|
||||
<view class="list-item">
|
||||
<text class="label">创建时间</text>
|
||||
<text class="value">{{ coilInfo.createTime || '-' }}</text>
|
||||
</view>
|
||||
<view class="list-item">
|
||||
<text class="label">更新人</text>
|
||||
<text class="value">{{ coilInfo.updater || '-' }}</text>
|
||||
</view>
|
||||
<view class="list-item">
|
||||
<text class="label">更新时间</text>
|
||||
<text class="value">{{ coilInfo.updateTime || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="footer-bar" v-if="actionId">
|
||||
<button class="btn btn-primary" @click="handleComplete">
|
||||
<text class="btn-icon">✓</text>
|
||||
<text class="btn-text">完成待办</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMaterialCoil } from '@/api/wms/coil'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
coilId: null,
|
||||
actionId: null,
|
||||
coilInfo: {},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.coilId = options.coilId
|
||||
this.actionId = options.actionId
|
||||
if (this.coilId) {
|
||||
this.fetchDetail()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '参数错误',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchDetail() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getMaterialCoil(this.coilId)
|
||||
this.coilInfo = res.data || {}
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取详情失败: ' + (error.message || '未知错误'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
getQualityClass(status) {
|
||||
if (!status) return ''
|
||||
const statusMap = {
|
||||
'A': 'quality-a',
|
||||
'B': 'quality-b',
|
||||
'C': 'quality-c',
|
||||
'D': 'quality-d'
|
||||
}
|
||||
return statusMap[status] || ''
|
||||
},
|
||||
|
||||
async handleComplete() {
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.detail-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
padding-bottom: 140rpx;
|
||||
|
||||
.header-card {
|
||||
background: linear-gradient(135deg, #1a73e8 0%, #4285f4 100%);
|
||||
padding: 40rpx 30rpx;
|
||||
|
||||
.header-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.coil-no {
|
||||
font-size: 36rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
.subtitle-text {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
background: #ffffff;
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.title-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 20rpx;
|
||||
|
||||
.info-item {
|
||||
width: 50%;
|
||||
padding: 16rpx 10rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
|
||||
&.quality-a {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
&.quality-b {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
&.quality-c {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
&.quality-d {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-list {
|
||||
padding: 0 30rpx;
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: #1a73e8;
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 12rpx;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
238
apps/hand-factory/pages/todo/components/coil-card.vue
Normal file
238
apps/hand-factory/pages/todo/components/coil-card.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<view class="coil-card">
|
||||
<!-- 头部信息 -->
|
||||
<view class="card-header">
|
||||
<view class="header-item">
|
||||
<text class="label">入场钢卷号</text>
|
||||
<text class="value">{{ data.enterCoilNo || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体信息 -->
|
||||
<view class="card-body">
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">当前钢卷号</text>
|
||||
<text class="value highlight">{{ data.currentCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">产品类型</text>
|
||||
<text class="value">{{ data.itemName || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">实际库区</text>
|
||||
<text class="value">{{ data.actualWarehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">备注</text>
|
||||
<text class="value">{{ data.remark || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">调拨类型</text>
|
||||
<text class="value tag" v-if="data.transferType">{{ data.transferType }}</text>
|
||||
<text class="value" v-else>-</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">改判原因</text>
|
||||
<text class="value">{{ data.changeReason || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="card-footer" v-if="showActions">
|
||||
<view class="action-btn primary" @click.stop="handleRelabel">
|
||||
<text class="btn-icon">🏷️</text>
|
||||
<text class="btn-text">重贴标签</text>
|
||||
</view>
|
||||
<view class="action-btn" @click.stop="handleViewRecord">
|
||||
<text class="btn-icon">📋</text>
|
||||
<text class="btn-text">查看记录</text>
|
||||
</view>
|
||||
<view class="action-btn" @click.stop="handleViewDetail">
|
||||
<text class="btn-icon">👁️</text>
|
||||
<text class="btn-text">查看详情</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CoilCard',
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
},
|
||||
showActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRelabel() {
|
||||
this.$emit('relabel', this.data)
|
||||
},
|
||||
handleViewRecord() {
|
||||
this.$emit('view-record', this.data)
|
||||
},
|
||||
handleViewDetail() {
|
||||
this.$emit('view-detail', this.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.coil-card {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #1a73e8 0%, #4285f4 100%);
|
||||
padding: 24rpx 30rpx;
|
||||
|
||||
.header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 24rpx 30rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.label {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
line-height: 1.4;
|
||||
|
||||
&.highlight {
|
||||
color: #1a73e8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.tag {
|
||||
display: inline-block;
|
||||
background: #e8f0fe;
|
||||
color: #1a73e8;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 24rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
padding: 20rpx 30rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
gap: 16rpx;
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 16rpx 0;
|
||||
border-radius: 8rpx;
|
||||
background: #f5f5f5;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
background: #1a73e8;
|
||||
|
||||
.btn-text {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* H5 浏览器特定样式 */
|
||||
/* #ifdef H5 */
|
||||
.coil-card {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
.action-btn {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
317
apps/hand-factory/pages/todo/components/filter-bar.vue
Normal file
317
apps/hand-factory/pages/todo/components/filter-bar.vue
Normal file
@@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<view class="filter-bar">
|
||||
<view class="filter-header" @click="toggleExpand">
|
||||
<view class="filter-title">
|
||||
<text class="title-icon">🔍</text>
|
||||
<text class="title-text">筛选条件</text>
|
||||
</view>
|
||||
<view class="filter-toggle">
|
||||
<text class="toggle-text">{{ isExpanded ? '收起' : '展开' }}</text>
|
||||
<text class="toggle-icon" :class="{ 'expanded': isExpanded }">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-content" v-show="isExpanded">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="item-label">入场卷号</text>
|
||||
<input
|
||||
class="item-input"
|
||||
v-model="form.enterCoilNo"
|
||||
placeholder="请输入入场钢卷号"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="item-label">当前卷号</text>
|
||||
<input
|
||||
class="item-input"
|
||||
v-model="form.currentCoilNo"
|
||||
placeholder="请输入当前钢卷号"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="item-label">产品名称</text>
|
||||
<input
|
||||
class="item-input"
|
||||
v-model="form.itemName"
|
||||
placeholder="请选择物料"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="item-label">规格</text>
|
||||
<input
|
||||
class="item-input"
|
||||
v-model="form.itemSpecification"
|
||||
placeholder="请选择规格"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="item-label">材质</text>
|
||||
<input
|
||||
class="item-input"
|
||||
v-model="form.itemMaterial"
|
||||
placeholder="请选择材质"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="item-label">厂家</text>
|
||||
<input
|
||||
class="item-input"
|
||||
v-model="form.itemManufacturer"
|
||||
placeholder="请选择厂家"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-actions">
|
||||
<button class="btn btn-reset" @click="handleReset" :disabled="loading">
|
||||
<text v-if="!loading">重置</text>
|
||||
<text v-else>重置中...</text>
|
||||
</button>
|
||||
<button class="btn btn-search" @click="handleSearch" :disabled="loading">
|
||||
<text v-if="!loading">搜索</text>
|
||||
<text v-else>搜索中...</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 筛选结果统计 -->
|
||||
<view v-if="showResult" class="filter-result">
|
||||
<text class="result-text">找到 {{ total }} 条记录</text>
|
||||
<text class="result-close" @click="hideResult">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FilterBar',
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false,
|
||||
showResult: false,
|
||||
form: {
|
||||
enterCoilNo: '',
|
||||
currentCoilNo: '',
|
||||
itemName: '',
|
||||
itemSpecification: '',
|
||||
itemMaterial: '',
|
||||
itemManufacturer: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleExpand() {
|
||||
this.isExpanded = !this.isExpanded
|
||||
},
|
||||
handleReset() {
|
||||
this.form = {
|
||||
enterCoilNo: '',
|
||||
currentCoilNo: '',
|
||||
itemName: '',
|
||||
itemSpecification: '',
|
||||
itemMaterial: '',
|
||||
itemManufacturer: ''
|
||||
}
|
||||
this.showResult = false
|
||||
this.$emit('reset')
|
||||
// 显示重置成功提示
|
||||
uni.showToast({
|
||||
title: '已重置',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
},
|
||||
handleSearch() {
|
||||
this.showResult = true
|
||||
this.isExpanded = false // 搜索后自动收起筛选栏
|
||||
this.$emit('search', { ...this.form })
|
||||
},
|
||||
hideResult() {
|
||||
this.showResult = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.filter-bar {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
overflow: hidden;
|
||||
|
||||
.filter-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.filter-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.toggle-text {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
transition: transform 0.2s;
|
||||
|
||||
&.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
padding: 24rpx 30rpx;
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex: 1;
|
||||
|
||||
.item-label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.item-input {
|
||||
height: 72rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
|
||||
&::placeholder {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
padding-top: 24rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.btn-reset {
|
||||
background: #f5f5f5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
&.btn-search {
|
||||
background: #1a73e8;
|
||||
color: #ffffff;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-result {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
background: #e8f4fd;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.result-text {
|
||||
font-size: 26rpx;
|
||||
color: #1a73e8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.result-close {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
padding: 4rpx 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
446
apps/hand-factory/pages/todo/components/record-popup.vue
Normal file
446
apps/hand-factory/pages/todo/components/record-popup.vue
Normal file
@@ -0,0 +1,446 @@
|
||||
<template>
|
||||
<uni-popup ref="popup" type="bottom" :safe-area="true">
|
||||
<view class="popup-container">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">{{ title }}</text>
|
||||
<text class="popup-close" @click="close">✕</text>
|
||||
</view>
|
||||
|
||||
<view class="popup-content">
|
||||
<!-- Tab切换 -->
|
||||
<view class="tab-bar">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'change' }"
|
||||
@click="activeTab = 'change'"
|
||||
>
|
||||
<text class="tab-text">改判记录</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'transfer' }"
|
||||
@click="activeTab = 'transfer'"
|
||||
>
|
||||
<text class="tab-text">调拨记录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 改判记录列表 -->
|
||||
<scroll-view
|
||||
v-show="activeTab === 'change'"
|
||||
scroll-y
|
||||
class="record-list"
|
||||
:style="{ height: scrollHeight + 'px' }"
|
||||
@scrolltolower="onLoadMoreChange"
|
||||
>
|
||||
<view v-if="changeRecords.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📋</text>
|
||||
<text class="empty-text">暂无改判记录</text>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
v-for="(record, index) in changeRecords"
|
||||
:key="index"
|
||||
class="record-item"
|
||||
>
|
||||
<view class="record-header">
|
||||
<text class="record-type">{{ getRejudgeTypeText(record.rejudgeType) }}</text>
|
||||
<text class="record-time">{{ formatTime(record.createTime) }}</text>
|
||||
</view>
|
||||
<view class="record-body">
|
||||
<view class="record-row">
|
||||
<text class="label">原值:</text>
|
||||
<text class="value old">{{ record.originalValue }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">新值:</text>
|
||||
<text class="value new">{{ record.newValue }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">改判原因:</text>
|
||||
<text class="value">{{ record.rejudgeReason }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">操作人:</text>
|
||||
<text class="value">{{ record.createBy }}</text>
|
||||
</view>
|
||||
<view class="record-row" v-if="record.remark">
|
||||
<text class="label">备注:</text>
|
||||
<text class="value">{{ record.remark }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 调拨记录列表 -->
|
||||
<scroll-view
|
||||
v-show="activeTab === 'transfer'"
|
||||
scroll-y
|
||||
class="record-list"
|
||||
:style="{ height: scrollHeight + 'px' }"
|
||||
@scrolltolower="onLoadMoreTransfer"
|
||||
>
|
||||
<view v-if="transferRecords.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📋</text>
|
||||
<text class="empty-text">暂无调拨记录</text>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
v-for="(record, index) in transferRecords"
|
||||
:key="index"
|
||||
class="record-item"
|
||||
>
|
||||
<view class="record-header">
|
||||
<text class="record-type">{{ getTransferTypeText(record.transferType) }}</text>
|
||||
<text class="record-time">{{ formatTime(record.createTime) }}</text>
|
||||
</view>
|
||||
<view class="record-body">
|
||||
<view class="record-row">
|
||||
<text class="label">调拨单号:</text>
|
||||
<text class="value">{{ record.transferOrderNo }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">原库区:</text>
|
||||
<text class="value">{{ record.fromWarehouseName }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">目标库区:</text>
|
||||
<text class="value new">{{ record.toWarehouseName }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">调拨数量:</text>
|
||||
<text class="value">{{ record.transferQuantity }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="label">操作人:</text>
|
||||
<text class="value">{{ record.createBy }}</text>
|
||||
</view>
|
||||
<view class="record-row" v-if="record.remark">
|
||||
<text class="label">备注:</text>
|
||||
<text class="value">{{ record.remark }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listChangeHistory, listTransferHistory } from '@/api/wms/todo'
|
||||
|
||||
export default {
|
||||
name: 'RecordPopup',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '查看记录'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'change',
|
||||
changeRecords: [],
|
||||
transferRecords: [],
|
||||
coilId: null,
|
||||
scrollHeight: 400 // 默认滚动区域高度
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.calcScrollHeight()
|
||||
},
|
||||
methods: {
|
||||
open(coilId) {
|
||||
this.coilId = coilId
|
||||
this.activeTab = 'change'
|
||||
this.$refs.popup.open()
|
||||
// 延迟计算高度和加载数据,确保弹窗已渲染
|
||||
this.$nextTick(() => {
|
||||
this.calcScrollHeight()
|
||||
this.fetchRecords()
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.$refs.popup.close()
|
||||
},
|
||||
async fetchRecords() {
|
||||
if (!this.coilId) return
|
||||
|
||||
uni.showLoading({ title: '加载中...' })
|
||||
try {
|
||||
// 获取改判记录
|
||||
const changeRes = await listChangeHistory(this.coilId)
|
||||
this.changeRecords = changeRes.rows || []
|
||||
|
||||
// 获取调拨记录
|
||||
const transferRes = await listTransferHistory(this.coilId)
|
||||
this.transferRecords = transferRes.rows || []
|
||||
|
||||
// 显示记录数量提示
|
||||
const totalCount = this.changeRecords.length + this.transferRecords.length
|
||||
if (totalCount > 0) {
|
||||
uni.showToast({
|
||||
title: `找到 ${totalCount} 条记录`,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取记录失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取记录失败: ' + (error.message || '未知错误'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
},
|
||||
|
||||
// 获取改判类型文本
|
||||
getRejudgeTypeText(type) {
|
||||
const typeMap = {
|
||||
'quality': '质量改判',
|
||||
'specification': '规格改判',
|
||||
'material': '材质改判',
|
||||
'other': '其他改判'
|
||||
}
|
||||
return typeMap[type] || '改判记录'
|
||||
},
|
||||
|
||||
// 获取调拨类型文本
|
||||
getTransferTypeText(type) {
|
||||
const typeMap = {
|
||||
'in': '入库调拨',
|
||||
'out': '出库调拨',
|
||||
'move': '库内调拨',
|
||||
'return': '退货调拨'
|
||||
}
|
||||
return typeMap[type] || '调拨记录'
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(time) {
|
||||
if (!time) return '-'
|
||||
const date = new Date(time)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hour = String(date.getHours()).padStart(2, '0')
|
||||
const minute = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`
|
||||
},
|
||||
|
||||
// 计算滚动区域高度
|
||||
calcScrollHeight() {
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
const windowHeight = systemInfo.windowHeight
|
||||
const safeAreaBottom = systemInfo.safeAreaInsets?.bottom || 0
|
||||
const tabBarHeight = 50 // TabBar高度
|
||||
|
||||
// 弹窗最大高度为屏幕高度的80%,减去底部安全区域和TabBar
|
||||
const popupMaxHeight = windowHeight * 0.8 - safeAreaBottom - tabBarHeight
|
||||
|
||||
// 减去:弹窗头部(约60px) + Tab栏(约50px)
|
||||
const headerHeight = 60
|
||||
const tabHeight = 50
|
||||
|
||||
this.scrollHeight = popupMaxHeight - headerHeight - tabHeight - 20 // 20px为底部padding
|
||||
|
||||
console.log('计算滚动高度:', {
|
||||
windowHeight,
|
||||
safeAreaBottom,
|
||||
tabBarHeight,
|
||||
popupMaxHeight,
|
||||
scrollHeight: this.scrollHeight
|
||||
})
|
||||
},
|
||||
|
||||
// 加载更多改判记录
|
||||
onLoadMoreChange() {
|
||||
console.log('加载更多改判记录')
|
||||
// 如果需要分页加载,可以在这里实现
|
||||
},
|
||||
|
||||
// 加载更多调拨记录
|
||||
onLoadMoreTransfer() {
|
||||
console.log('加载更多调拨记录')
|
||||
// 如果需要分页加载,可以在这里实现
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.popup-container {
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: constant(safe-area-inset-bottom);
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
font-size: 36rpx;
|
||||
color: #999999;
|
||||
padding: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
position: relative;
|
||||
|
||||
.tab-text {
|
||||
font-size: 30rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.tab-text {
|
||||
color: #1a73e8;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 4rpx;
|
||||
background: #1a73e8;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.record-list {
|
||||
flex: 1;
|
||||
padding: 20rpx 30rpx;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.record-item {
|
||||
background: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.record-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 1rpx solid #eeeeee;
|
||||
gap: 20rpx;
|
||||
|
||||
.record-type {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1a73e8;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.record-body {
|
||||
.record-row {
|
||||
display: flex;
|
||||
margin-bottom: 12rpx;
|
||||
align-items: flex-start;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
width: 140rpx;
|
||||
flex-shrink: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
line-height: 1.5;
|
||||
|
||||
&.old {
|
||||
color: #999999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
&.new {
|
||||
color: #1a73e8;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
222
apps/hand-factory/pages/todo/components/relabel-popup.vue
Normal file
222
apps/hand-factory/pages/todo/components/relabel-popup.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<uni-popup ref="popup" type="center" :safe-area="true">
|
||||
<view class="popup-container">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">重贴标签</text>
|
||||
<text class="popup-close" @click="close">✕</text>
|
||||
</view>
|
||||
|
||||
<view class="popup-content">
|
||||
<!-- 钢卷信息展示 -->
|
||||
<view class="coil-info">
|
||||
<view class="info-row">
|
||||
<text class="label">入场钢卷号:</text>
|
||||
<text class="value">{{ coilInfo.enterCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">当前钢卷号:</text>
|
||||
<text class="value highlight">{{ coilInfo.currentCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">产品名称:</text>
|
||||
<text class="value">{{ coilInfo.itemName || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作人信息 -->
|
||||
<view class="operator-info">
|
||||
<text class="label">操作人:</text>
|
||||
<text class="value">{{ operatorName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="popup-footer">
|
||||
<button class="btn btn-cancel" @click="close">取消</button>
|
||||
<button class="btn btn-confirm" @click="handleConfirm" :disabled="loading">
|
||||
{{ loading ? '提交中...' : '确认重贴' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { relabelCoil, completeTodoAction } from '@/api/wms/todo'
|
||||
|
||||
export default {
|
||||
name: 'RelabelPopup',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
coilInfo: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
operatorName() {
|
||||
return this.$store.state.user.nickName || this.$store.state.user.name || '未知'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(coilInfo) {
|
||||
this.coilInfo = coilInfo
|
||||
this.$refs.popup.open()
|
||||
},
|
||||
close() {
|
||||
this.$refs.popup.close()
|
||||
},
|
||||
async handleConfirm() {
|
||||
this.loading = true
|
||||
try {
|
||||
// 1. 提交重贴标签操作
|
||||
await relabelCoil({
|
||||
coilId: this.coilInfo.coilId,
|
||||
currentCoilNo: this.coilInfo.currentCoilNo,
|
||||
operator: this.operatorName,
|
||||
operateTime: new Date().toISOString()
|
||||
})
|
||||
|
||||
// 2. 完成待办操作
|
||||
if (this.coilInfo.actionId) {
|
||||
await completeTodoAction(this.coilInfo.actionId)
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: '重贴标签成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
this.close()
|
||||
this.$emit('success')
|
||||
} catch (error) {
|
||||
console.error('重贴标签失败:', error)
|
||||
uni.showToast({
|
||||
title: '操作失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.popup-container {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
width: 80vw;
|
||||
max-width: 600rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
font-size: 36rpx;
|
||||
color: #999999;
|
||||
padding: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
padding: 30rpx;
|
||||
|
||||
.coil-info {
|
||||
background: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
width: 160rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
|
||||
&.highlight {
|
||||
color: #1a73e8;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operator-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-footer {
|
||||
display: flex;
|
||||
padding: 20rpx 30rpx 40rpx;
|
||||
gap: 20rpx;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
font-size: 30rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.btn-cancel {
|
||||
background: #f5f5f5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
&.btn-confirm {
|
||||
background: #1a73e8;
|
||||
color: #ffffff;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
473
apps/hand-factory/pages/todo/index.vue
Normal file
473
apps/hand-factory/pages/todo/index.vue
Normal file
@@ -0,0 +1,473 @@
|
||||
<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.actionId"
|
||||
: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()
|
||||
}
|
||||
},
|
||||
|
||||
// 获取列表数据
|
||||
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
|
||||
|
||||
console.log('解析后的数据:', { rows, total: this.total })
|
||||
|
||||
if (isLoadMore) {
|
||||
this.list = [...this.list, ...rows]
|
||||
} else {
|
||||
this.list = rows
|
||||
}
|
||||
|
||||
// 更新待贴标签数量
|
||||
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 || coilInfo.materialCoilId
|
||||
if (!coilId) {
|
||||
uni.showToast({
|
||||
title: '无法获取钢卷ID',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$refs.recordPopup.open(coilId)
|
||||
},
|
||||
|
||||
// 查看详情
|
||||
handleViewDetail(coilInfo) {
|
||||
const coilId = coilInfo.coilId || coilInfo.materialCoilId
|
||||
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>
|
||||
BIN
apps/hand-factory/static/images/tabbar/todo.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/todo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
apps/hand-factory/static/images/tabbar/todo_.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/todo_.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
Reference in New Issue
Block a user