5 changed files with 1178 additions and 2 deletions
-
57src/api/warehouse/countingWIP.js
-
10src/router/index.js
-
4src/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