5 changed files with 1178 additions and 2 deletions
-
57src/api/warehouse/countingWIP.js
-
10src/router/index.js
-
2src/views/main.vue
-
741src/views/modules/warehouse/countingDetail.vue
-
368src/views/modules/warehouse/countingList.vue
@ -0,0 +1,57 @@ |
|||
import http from '@/utils/httpRequest' |
|||
|
|||
/** |
|||
* 查询当天的盘点单列表 |
|||
*/ |
|||
export function countingWIPTodayList(data) { |
|||
return http({ |
|||
url: http.adornUrl('/warehouse/countingWIP/todayList'), |
|||
method: 'post', |
|||
data: http.adornData(data) |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* 根据盘点单号查询WIP数据 |
|||
*/ |
|||
export function countingWIPList(data) { |
|||
return http({ |
|||
url: http.adornUrl('/warehouse/countingWIP/wipList'), |
|||
method: 'post', |
|||
data: http.adornData(data) |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* 扫描标签 |
|||
*/ |
|||
export function countingWIPScanLabel(data) { |
|||
return http({ |
|||
url: http.adornUrl('/warehouse/countingWIP/scanLabel'), |
|||
method: 'post', |
|||
data: http.adornData(data) |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* 更新盘点数量 |
|||
*/ |
|||
export function countingWIPUpdateCheckedQty(data) { |
|||
return http({ |
|||
url: http.adornUrl('/warehouse/countingWIP/updateCheckedQty'), |
|||
method: 'post', |
|||
data: http.adornData(data) |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* 确认盘点 |
|||
*/ |
|||
export function countingWIPConfirmCounting(data) { |
|||
return http({ |
|||
url: http.adornUrl('/warehouse/countingWIP/confirmCounting'), |
|||
method: 'post', |
|||
data: http.adornData(data) |
|||
}) |
|||
} |
|||
|
|||
@ -0,0 +1,741 @@ |
|||
<template> |
|||
<div class="pda-container"> |
|||
<!-- 头部栏 --> |
|||
<div class="header-bar"> |
|||
<div class="header-left" @click="$router.back()"> |
|||
<i class="el-icon-arrow-left"></i> |
|||
<span>盘点</span> |
|||
</div> |
|||
<div class="header-right" @click="$router.push({ path: '/' })"> |
|||
首页 |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 搜索框 --> |
|||
<div class="search-container"> |
|||
<el-input |
|||
clearable |
|||
class="compact-input" |
|||
v-model="scanCode" |
|||
placeholder="请扫描标签条码" |
|||
prefix-icon="el-icon-search" |
|||
@keyup.enter.native="handleScan" |
|||
ref="scanInput" |
|||
/> |
|||
<div class="mode-switch"> |
|||
<el-switch |
|||
class="custom-switch" |
|||
v-model="isRemoveMode" |
|||
active-color="#ff4949" |
|||
inactive-color="#13ce66"> |
|||
</el-switch> |
|||
<span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span> |
|||
<span v-else class="switch-text2">{{ '添加' }}</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 盘点单信息卡片 --> |
|||
<div class="material-info-card" v-if="reportId"> |
|||
<div class="card-details"> |
|||
<div class="detail-item"> |
|||
<div class="detail-label">盘点单号</div> |
|||
<div class="detail-value">{{ reportId }}</div> |
|||
</div> |
|||
<div class="detail-item"> |
|||
<div class="detail-label">标签张数</div> |
|||
<div class="detail-value"> |
|||
<span class="highlight">{{ checkedCount }}</span> |
|||
<span class="separator">/</span> |
|||
<span class="total-value">{{ totalCount }}</span> |
|||
</div> |
|||
</div> |
|||
<div class="detail-item"> |
|||
<div class="detail-label">物料总数</div> |
|||
<div class="detail-value"> |
|||
<span class="highlight">{{ checkedQty }}</span> |
|||
<span class="separator">/</span> |
|||
<span class="total-value">{{ totalQty }}</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 盘点信息确认标题 --> |
|||
<div class="section-title"> |
|||
<div class="title-left"> |
|||
<i class="el-icon-circle-check"></i> |
|||
<span>盘点信息确认</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 标签列表 --> |
|||
<div class="label-list"> |
|||
<div class="list-header"> |
|||
<div class="col-no">NO.</div> |
|||
<div class="col-label">标签条码</div> |
|||
<div class="col-roll-qty">标签数量</div> |
|||
<div class="col-status">盘点状态</div> |
|||
<div class="col-qty">盘点数量</div> |
|||
</div> |
|||
|
|||
<div class="list-body"> |
|||
<div |
|||
v-for="(item, index) in wipList" |
|||
:key="index" |
|||
class="list-item" |
|||
:class="'status-' + getStatusClass(item.checkedStatus)"> |
|||
<div class="col-no">{{ wipList.length - index }}</div> |
|||
<div class="col-label">{{ item.rollNo }}</div> |
|||
<div class="col-roll-qty">{{ item.rollQty }}</div> |
|||
<div class="col-status"> |
|||
<span class="status-badge" :class="'badge-' + getStatusClass(item.checkedStatus)"> |
|||
{{ item.checkedStatus }} |
|||
</span> |
|||
</div> |
|||
<div class="col-qty"> |
|||
<el-input-number |
|||
v-model="item.checkedQty" |
|||
:min="0" |
|||
:precision="2" |
|||
size="mini" |
|||
controls-position="right" |
|||
@blur="updateQty(item)" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 空状态 --> |
|||
<div v-if="wipList.length === 0 && !loading" class="empty-labels"> |
|||
<p>暂无扫描标签</p> |
|||
</div> |
|||
|
|||
<!-- 加载状态 --> |
|||
<div v-if="loading" class="loading-labels"> |
|||
<i class="el-icon-loading"></i> |
|||
<p>加载中...</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 底部操作按钮 --> |
|||
<div class="bottom-actions"> |
|||
<button class="action-btn secondary" @click="handleConfirm" :disabled="confirmLoading"> |
|||
<i v-if="confirmLoading" class="el-icon-loading"></i> |
|||
<span v-else>确定</span> |
|||
</button> |
|||
<button class="action-btn secondary" style="margin-left: 10px;" @click="$router.back()"> |
|||
取消 |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { |
|||
countingWIPList, |
|||
countingWIPScanLabel, |
|||
countingWIPUpdateCheckedQty, |
|||
countingWIPConfirmCounting |
|||
} from '@/api/warehouse/countingWIP.js' |
|||
|
|||
export default { |
|||
name: 'CountingDetail', |
|||
data() { |
|||
return { |
|||
site: '', |
|||
buNo: '', |
|||
reportId: '', |
|||
scanCode: '', |
|||
wipList: [], |
|||
loading: false, |
|||
confirmLoading: false, |
|||
isRemoveMode: false, |
|||
totalCount: 0, |
|||
totalQty: 0, |
|||
checkedCount: 0, |
|||
checkedQty: 0 |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.site = this.$route.query.site |
|||
this.buNo = this.$route.query.buNo |
|||
this.reportId = this.$route.query.reportId |
|||
|
|||
// 从路由参数获取统计数据 |
|||
this.totalCount = parseInt(this.$route.query.totalCount) || 0 |
|||
this.totalQty = parseFloat(this.$route.query.totalQty) || 0 |
|||
this.checkedCount = parseInt(this.$route.query.checkedCount) || 0 |
|||
this.checkedQty = parseFloat(this.$route.query.checkedQty) || 0 |
|||
|
|||
if (!this.site || !this.reportId) { |
|||
this.$message.error('参数错误') |
|||
this.$router.go(-1) |
|||
return |
|||
} |
|||
|
|||
// 聚焦输入框 |
|||
this.$nextTick(() => { |
|||
this.focusInput() |
|||
}) |
|||
|
|||
this.getWIPList() |
|||
}, |
|||
methods: { |
|||
// 获取WIP列表 |
|||
getWIPList() { |
|||
this.loading = true |
|||
const params = { |
|||
site: this.site, |
|||
buNo: this.buNo, |
|||
reportId: this.reportId |
|||
} |
|||
countingWIPList(params).then(({ data }) => { |
|||
if (data && data.code === 0) { |
|||
this.wipList = data.wipList || [] |
|||
// 如果后端返回了统计数据,则更新(用于刷新) |
|||
if (data.totalCount !== undefined) { |
|||
this.totalCount = data.totalCount || 0 |
|||
this.totalQty = data.totalQty || 0 |
|||
this.checkedCount = data.checkedCount || 0 |
|||
this.checkedQty = data.checkedQty || 0 |
|||
} |
|||
} else { |
|||
this.$message.error(data.msg || '查询失败') |
|||
} |
|||
this.loading = false |
|||
}).catch(() => { |
|||
this.loading = false |
|||
this.$message.error('查询失败') |
|||
}) |
|||
}, |
|||
|
|||
// 扫描处理 |
|||
handleScan() { |
|||
if (!this.scanCode || this.scanCode.trim() === '') { |
|||
this.$message.warning('请输入标签条码') |
|||
return |
|||
} |
|||
|
|||
if (this.isRemoveMode) { |
|||
this.handleRemove() |
|||
} else { |
|||
this.scanLabel(this.scanCode.trim()) |
|||
} |
|||
}, |
|||
|
|||
// 移除 |
|||
handleRemove() { |
|||
if (!this.scanCode || this.scanCode.trim() === '') { |
|||
this.$message.warning('请输入标签条码') |
|||
return |
|||
} |
|||
|
|||
const rollNo = this.scanCode.trim() |
|||
const index = this.wipList.findIndex(item => item.rollNo === rollNo) |
|||
|
|||
if (index !== -1) { |
|||
this.wipList.splice(index, 1) |
|||
this.$message.success('移除成功') |
|||
this.scanCode = '' |
|||
this.focusInput() |
|||
} else { |
|||
this.$message.warning('未找到该标签') |
|||
} |
|||
}, |
|||
|
|||
// 扫描标签 |
|||
scanLabel(rollNo) { |
|||
const params = { |
|||
site: this.site, |
|||
reportId: this.reportId, |
|||
rollNo: rollNo |
|||
} |
|||
|
|||
countingWIPScanLabel(params).then(({ data }) => { |
|||
if (data && data.code === 0) { |
|||
this.$message.success('扫描成功') |
|||
// 将新扫描的数据添加到列表顶部(显示在底部) |
|||
this.wipList.push(data.data) |
|||
this.scanCode = '' |
|||
this.focusInput() |
|||
} else { |
|||
this.$message.error(data.msg || '扫描失败') |
|||
} |
|||
}).catch(() => { |
|||
this.$message.error('扫描失败') |
|||
}) |
|||
}, |
|||
|
|||
// 更新盘点数量 |
|||
updateQty(item) { |
|||
const params = { |
|||
site: this.site, |
|||
reportId: this.reportId, |
|||
rollNo: item.rollNo, |
|||
checkedQty: parseFloat(item.checkedQty) || 0 |
|||
} |
|||
|
|||
countingWIPUpdateCheckedQty(params).then(({ data }) => { |
|||
if (data && data.code === 0) { |
|||
// 更新成功,不需要提示 |
|||
} else { |
|||
this.$message.error(data.msg || '更新失败') |
|||
} |
|||
}).catch(() => { |
|||
this.$message.error('更新失败') |
|||
}) |
|||
}, |
|||
|
|||
// 确认盘点 |
|||
handleConfirm() { |
|||
if (this.wipList.length === 0) { |
|||
this.$message.warning('没有需要确认的盘点数据') |
|||
return |
|||
} |
|||
|
|||
this.$confirm('确定要提交盘点数据吗?提交后将无法修改。', '确认盘点', { |
|||
confirmButtonText: '确定', |
|||
cancelButtonText: '取消', |
|||
type: 'warning' |
|||
}).then(() => { |
|||
this.confirmLoading = true |
|||
const params = { |
|||
site: this.site, |
|||
reportId: this.reportId |
|||
} |
|||
|
|||
countingWIPConfirmCounting(params).then(({ data }) => { |
|||
this.confirmLoading = false |
|||
if (data && data.code === 0) { |
|||
this.$message.success('盘点确认成功') |
|||
setTimeout(() => { |
|||
this.$router.go(-1) |
|||
}, 1500) |
|||
} else { |
|||
this.$message.error(data.msg || '确认失败') |
|||
} |
|||
}).catch(() => { |
|||
this.confirmLoading = false |
|||
this.$message.error('确认失败') |
|||
}) |
|||
}).catch(() => { |
|||
// 取消 |
|||
}) |
|||
}, |
|||
|
|||
// 获取状态样式类 |
|||
getStatusClass(status) { |
|||
if (status === '正常') return 'normal' |
|||
if (status === '盘盈') return 'surplus' |
|||
if (status === '盘亏') return 'loss' |
|||
return '' |
|||
}, |
|||
|
|||
// 聚焦输入框 |
|||
focusInput() { |
|||
this.$nextTick(() => { |
|||
if (this.$refs.scanInput) { |
|||
this.$refs.scanInput.focus() |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.pda-container { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
background: #f5f5f5; |
|||
} |
|||
|
|||
/* 头部栏 */ |
|||
.header-bar { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 8px 16px; |
|||
background: #17B3A3; |
|||
color: white; |
|||
height: 40px; |
|||
min-height: 40px; |
|||
} |
|||
|
|||
.header-left { |
|||
display: flex; |
|||
align-items: center; |
|||
cursor: pointer; |
|||
font-size: 16px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.header-left i { |
|||
margin-right: 8px; |
|||
font-size: 18px; |
|||
} |
|||
|
|||
.header-right { |
|||
cursor: pointer; |
|||
font-size: 16px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
/* 搜索容器 */ |
|||
.search-container { |
|||
padding: 12px 16px; |
|||
background: white; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 12px; |
|||
} |
|||
|
|||
.compact-input { |
|||
flex: 1; |
|||
} |
|||
|
|||
.mode-switch { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 8px; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.switch-text, |
|||
.switch-text2 { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
min-width: 32px; |
|||
} |
|||
|
|||
.switch-text { |
|||
color: #ff4949; |
|||
} |
|||
|
|||
.switch-text2 { |
|||
color: #13ce66; |
|||
} |
|||
|
|||
/* 物料信息卡片 */ |
|||
.material-info-card { |
|||
margin: 12px 16px; |
|||
padding: 16px; |
|||
background: white; |
|||
border-radius: 8px; |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
.card-details { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: flex-start; |
|||
gap: 8px; |
|||
} |
|||
|
|||
.detail-item { |
|||
flex: 1; |
|||
text-align: center; |
|||
min-width: 0; |
|||
} |
|||
|
|||
.detail-label { |
|||
font-size: 12px; |
|||
color: #999; |
|||
margin-bottom: 6px; |
|||
line-height: 1.2; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.detail-value { |
|||
font-size: 16px; |
|||
color: #333; |
|||
line-height: 1.2; |
|||
font-weight: 500; |
|||
word-break: break-all; |
|||
} |
|||
|
|||
.detail-value .highlight { |
|||
color: #17B3A3; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.detail-value .separator { |
|||
color: #333; |
|||
margin: 0 2px; |
|||
} |
|||
|
|||
.detail-value .total-value { |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
/* 区块标题 */ |
|||
.section-title { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 12px 16px; |
|||
background: white; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
} |
|||
|
|||
.title-left { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 8px; |
|||
font-size: 14px; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
|
|||
.title-left i { |
|||
font-size: 16px; |
|||
color: #17B3A3; |
|||
} |
|||
|
|||
/* 标签列表 */ |
|||
.label-list { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
margin: 0 16px 12px; |
|||
background: white; |
|||
border-radius: 8px; |
|||
overflow: hidden; |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
.list-header { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 10px 12px; |
|||
background: #f8f9fa; |
|||
border-bottom: 1px solid #e0e0e0; |
|||
font-size: 12px; |
|||
font-weight: bold; |
|||
color: #666; |
|||
} |
|||
|
|||
.list-body { |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.list-item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 10px 12px; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
font-size: 12px; |
|||
transition: background 0.2s; |
|||
} |
|||
|
|||
.list-item:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.list-item.status-normal { |
|||
border-left: 3px solid #67C23A; |
|||
} |
|||
|
|||
.list-item.status-surplus { |
|||
border-left: 3px solid #E6A23C; |
|||
} |
|||
|
|||
.list-item.status-loss { |
|||
border-left: 3px solid #F56C6C; |
|||
} |
|||
|
|||
.col-no { |
|||
width: 40px; |
|||
text-align: center; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.col-label { |
|||
flex: 1; |
|||
min-width: 80px; |
|||
padding: 0 8px; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.col-roll-qty { |
|||
width: 70px; |
|||
text-align: center; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.col-status { |
|||
width: 60px; |
|||
text-align: center; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.col-qty { |
|||
width: 100px; |
|||
text-align: right; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.status-badge { |
|||
display: inline-block; |
|||
padding: 2px 6px; |
|||
border-radius: 10px; |
|||
font-size: 11px; |
|||
font-weight: 500; |
|||
color: white; |
|||
} |
|||
|
|||
.badge-normal { |
|||
background: #67C23A; |
|||
} |
|||
|
|||
.badge-surplus { |
|||
background: #E6A23C; |
|||
} |
|||
|
|||
.badge-loss { |
|||
background: #F56C6C; |
|||
} |
|||
|
|||
/* 空状态 */ |
|||
.empty-labels { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 40px 20px; |
|||
color: #999; |
|||
} |
|||
|
|||
.empty-labels p { |
|||
font-size: 14px; |
|||
margin: 0; |
|||
} |
|||
|
|||
/* 加载状态 */ |
|||
.loading-labels { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 40px 20px; |
|||
color: #17B3A3; |
|||
} |
|||
|
|||
.loading-labels i { |
|||
font-size: 24px; |
|||
margin-bottom: 12px; |
|||
animation: spin 1s linear infinite; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
from { transform: rotate(0deg); } |
|||
to { transform: rotate(360deg); } |
|||
} |
|||
|
|||
.loading-labels p { |
|||
font-size: 14px; |
|||
margin: 0; |
|||
} |
|||
|
|||
/* 底部操作按钮 */ |
|||
.bottom-actions { |
|||
display: flex; |
|||
justify-content: center; |
|||
padding: 12px 16px; |
|||
background: white; |
|||
border-top: 1px solid #e0e0e0; |
|||
} |
|||
|
|||
.action-btn { |
|||
flex: 1; |
|||
padding: 12px 20px; |
|||
border: none; |
|||
border-radius: 6px; |
|||
font-size: 15px; |
|||
font-weight: 500; |
|||
cursor: pointer; |
|||
transition: all 0.2s; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
gap: 6px; |
|||
} |
|||
|
|||
.action-btn.secondary { |
|||
background: #17B3A3; |
|||
color: white; |
|||
} |
|||
|
|||
.action-btn.secondary:active { |
|||
background: #149a8b; |
|||
} |
|||
|
|||
.action-btn:disabled { |
|||
opacity: 0.6; |
|||
cursor: not-allowed; |
|||
} |
|||
|
|||
.action-btn i { |
|||
font-size: 14px; |
|||
} |
|||
|
|||
/* 响应式设计 */ |
|||
@media (max-width: 360px) { |
|||
.header-bar { |
|||
padding: 8px 12px; |
|||
} |
|||
|
|||
.search-container { |
|||
padding: 8px 12px; |
|||
} |
|||
|
|||
.material-info-card { |
|||
margin: 8px 12px; |
|||
padding: 10px 12px; |
|||
} |
|||
|
|||
.label-list { |
|||
margin: 0 12px 8px; |
|||
} |
|||
|
|||
.list-header, |
|||
.list-item { |
|||
padding: 8px 10px; |
|||
} |
|||
|
|||
.col-no { |
|||
width: 35px; |
|||
} |
|||
|
|||
.col-label { |
|||
min-width: 70px; |
|||
} |
|||
|
|||
.col-roll-qty { |
|||
width: 60px; |
|||
} |
|||
|
|||
.col-status { |
|||
width: 50px; |
|||
} |
|||
|
|||
.col-qty { |
|||
width: 85px; |
|||
} |
|||
|
|||
.bottom-actions { |
|||
padding: 10px 12px; |
|||
} |
|||
|
|||
.action-btn { |
|||
padding: 10px 16px; |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,368 @@ |
|||
<template> |
|||
<div class="pda-container"> |
|||
<!-- 头部栏 --> |
|||
<div class="header-bar"> |
|||
<div class="header-left" @click="$router.back()"> |
|||
<i class="el-icon-arrow-left"></i> |
|||
<span>仓库盘点</span> |
|||
</div> |
|||
<div class="header-right" @click="$router.push({ path: '/' })"> |
|||
首页 |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 搜索框 --> |
|||
<div class="search-container"> |
|||
<el-input |
|||
clearable |
|||
v-model="searchValue" |
|||
placeholder="请扫描盘点单号" |
|||
prefix-icon="el-icon-search" |
|||
@keyup.enter.native="onSearch" |
|||
ref="searchInput" |
|||
/> |
|||
</div> |
|||
|
|||
<!-- 盘点单列表 --> |
|||
<div class="content-area"> |
|||
<div |
|||
v-for="item in list" |
|||
:key="item.reportId" |
|||
class="inbound-card" |
|||
@click="goToDetail(item)"> |
|||
<div class="card-details"> |
|||
<div class="detail-item"> |
|||
<div class="detail-label">盘点单号</div> |
|||
<div class="detail-value">{{ item.reportId }}</div> |
|||
</div> |
|||
<div class="detail-item"> |
|||
<div class="detail-label">标签张数</div> |
|||
<div class="detail-value"> |
|||
<span class="qualified">{{ item.checkedCount }}</span> |
|||
<span class="separator">/</span> |
|||
<span class="total">{{ item.totalCount }}</span> |
|||
</div> |
|||
</div> |
|||
<div class="detail-item"> |
|||
<div class="detail-label">物料总数</div> |
|||
<div class="detail-value"> |
|||
<span class="qualified">{{ item.checkedQty }}</span> |
|||
<span class="separator">/</span> |
|||
<span class="total">{{ item.totalQty }}</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer" v-if="item.statusDesc"> |
|||
<span class="status-tag" :class="'status-' + item.status">{{ item.statusDesc }}</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 空状态 --> |
|||
<div v-if="list.length === 0 && !loading" class="empty-state"> |
|||
<i class="el-icon-box"></i> |
|||
<p>暂无盘点单</p> |
|||
</div> |
|||
|
|||
<!-- 加载状态 --> |
|||
<div v-if="loading" class="loading-state"> |
|||
<i class="el-icon-loading"></i> |
|||
<p>加载中...</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { countingWIPTodayList } from '@/api/warehouse/countingWIP.js' |
|||
|
|||
export default { |
|||
name: 'CountingList', |
|||
data() { |
|||
return { |
|||
searchValue: '', |
|||
list: [], |
|||
loading: false |
|||
} |
|||
}, |
|||
mounted() { |
|||
// 聚焦搜索框 |
|||
this.$nextTick(() => { |
|||
if (this.$refs.searchInput) { |
|||
this.$refs.searchInput.focus() |
|||
} |
|||
}) |
|||
// 加载数据 |
|||
this.getList() |
|||
}, |
|||
methods: { |
|||
// 搜索 |
|||
onSearch() { |
|||
this.getList() |
|||
}, |
|||
|
|||
// 获取列表 |
|||
getList() { |
|||
this.loading = true |
|||
const params = { |
|||
reportId: this.searchValue |
|||
} |
|||
countingWIPTodayList(params).then(({ data }) => { |
|||
if (data && data.code === 0) { |
|||
this.list = data.list || [] |
|||
} else { |
|||
this.$message.error(data.msg || '查询失败') |
|||
} |
|||
this.loading = false |
|||
}).catch(() => { |
|||
this.loading = false |
|||
this.$message.error('查询失败') |
|||
}) |
|||
}, |
|||
|
|||
// 跳转到明细页 |
|||
goToDetail(item) { |
|||
this.$router.push({ |
|||
name: 'countingDetail', |
|||
query: { |
|||
site: item.site, |
|||
buNo: item.buNo, |
|||
reportId: item.reportId, |
|||
totalCount: item.totalCount, |
|||
totalQty: item.totalQty, |
|||
checkedCount: item.checkedCount, |
|||
checkedQty: item.checkedQty |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.pda-container { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
background: #f5f5f5; |
|||
} |
|||
|
|||
/* 头部栏 */ |
|||
.header-bar { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 8px 16px; |
|||
background: #17B3A3; |
|||
color: white; |
|||
height: 40px; |
|||
min-height: 40px; |
|||
} |
|||
|
|||
.header-left { |
|||
display: flex; |
|||
align-items: center; |
|||
cursor: pointer; |
|||
font-size: 16px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.header-left i { |
|||
margin-right: 8px; |
|||
font-size: 18px; |
|||
} |
|||
|
|||
.header-right { |
|||
cursor: pointer; |
|||
font-size: 16px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
/* 搜索容器 */ |
|||
.search-container { |
|||
padding: 12px 16px; |
|||
background: white; |
|||
} |
|||
|
|||
/* 内容区域 */ |
|||
.content-area { |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
padding: 12px 16px; |
|||
} |
|||
|
|||
/* 入库卡片 */ |
|||
.inbound-card { |
|||
background: white; |
|||
border-radius: 8px; |
|||
margin-bottom: 12px; |
|||
padding: 16px; |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|||
cursor: pointer; |
|||
transition: all 0.2s ease; |
|||
} |
|||
|
|||
.inbound-card:hover { |
|||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
|||
transform: translateY(-1px); |
|||
} |
|||
|
|||
.inbound-card:active { |
|||
transform: translateY(0); |
|||
} |
|||
|
|||
/* 卡片详情 */ |
|||
.card-details { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: flex-start; |
|||
gap: 8px; |
|||
padding-bottom: 12px; |
|||
} |
|||
|
|||
.detail-item { |
|||
flex: 1; |
|||
text-align: center; |
|||
min-width: 0; |
|||
} |
|||
|
|||
.detail-label { |
|||
font-size: 12px; |
|||
color: #999; |
|||
margin-bottom: 6px; |
|||
line-height: 1.2; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.detail-value { |
|||
font-size: 16px; |
|||
color: #333; |
|||
line-height: 1.2; |
|||
font-weight: 500; |
|||
word-break: break-all; |
|||
} |
|||
|
|||
.detail-value .qualified { |
|||
color: #17B3A3; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.detail-value .separator { |
|||
color: #333; |
|||
margin: 0 2px; |
|||
} |
|||
|
|||
.detail-value .total { |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
/* 卡片底部 */ |
|||
.card-footer { |
|||
padding-top: 8px; |
|||
border-top: 1px solid #f0f0f0; |
|||
text-align: right; |
|||
} |
|||
|
|||
.status-tag { |
|||
display: inline-block; |
|||
padding: 3px 10px; |
|||
border-radius: 12px; |
|||
font-size: 11px; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.status-0 { |
|||
background-color: #909399; |
|||
} |
|||
|
|||
.status-1 { |
|||
background-color: #409EFF; |
|||
} |
|||
|
|||
.status-2 { |
|||
background-color: #E6A23C; |
|||
} |
|||
|
|||
.status-3 { |
|||
background-color: #67C23A; |
|||
} |
|||
|
|||
/* 空状态 */ |
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 60px 20px; |
|||
color: #999; |
|||
} |
|||
|
|||
.empty-state i { |
|||
font-size: 48px; |
|||
margin-bottom: 16px; |
|||
} |
|||
|
|||
.empty-state p { |
|||
font-size: 14px; |
|||
margin: 0; |
|||
} |
|||
|
|||
/* 加载状态 */ |
|||
.loading-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 60px 20px; |
|||
color: #17B3A3; |
|||
} |
|||
|
|||
.loading-state i { |
|||
font-size: 24px; |
|||
margin-bottom: 12px; |
|||
animation: spin 1s linear infinite; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
from { transform: rotate(0deg); } |
|||
to { transform: rotate(360deg); } |
|||
} |
|||
|
|||
.loading-state p { |
|||
font-size: 14px; |
|||
margin: 0; |
|||
} |
|||
|
|||
/* 响应式设计 */ |
|||
@media (max-width: 360px) { |
|||
.header-bar { |
|||
padding: 8px 12px; |
|||
} |
|||
|
|||
.search-container { |
|||
padding: 8px 12px; |
|||
} |
|||
|
|||
.content-area { |
|||
padding: 8px 12px; |
|||
} |
|||
|
|||
.inbound-card { |
|||
padding: 12px; |
|||
} |
|||
|
|||
.card-details { |
|||
flex-wrap: wrap; |
|||
gap: 6px; |
|||
} |
|||
|
|||
.detail-item { |
|||
flex: 0 0 48%; |
|||
margin-bottom: 6px; |
|||
min-width: 60px; |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue