盘点列表完成

master
lynn 12 months ago
parent 6833f2f332
commit 62d2e2f13f

@ -40,9 +40,22 @@ export function getStocktakingPlanDetail(params) {
}
// 获取盘点计划物资关联列表
export function getStocktakingPlanLinkList(params) {
export function getStocktakingPlanLinkList(data) {
return request({
url: '/api/admin/material-infos-plan-link/index',
method: 'post',
data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
// 获取单个盘点计划详情
export function getStocktakingPlanLinkDetail(params) {
return request({
url: '/api/admin/material-infos-plan-link/show',
method: 'get',
params
})

@ -98,6 +98,9 @@
<template slot-scope="{ row }" slot="area">
{{ row.material_info ? row.material_info.suozaicangku : '-' }}
</template>
<template slot-scope="{ row }" slot="executor">
{{ row.responsible_admin ? row.responsible_admin.name : '-' }}
</template>
<template slot-scope="{ row }" slot="startTime">
{{ row.material_infos_plan ? row.material_infos_plan.start_date : '-' }}
</template>
@ -109,7 +112,7 @@
</template>
<template slot-scope="{ row }" slot="action">
<div style="display: flex; gap: 8px; justify-content: center;">
<Button type="primary" size="small" style="border-radius: 6px;" @click="viewInventoryDetail(row.id)"></Button>
<Button type="primary" size="small" style="border-radius: 6px;" @click="viewInventoryDetail(row)"></Button>
</div>
</template>
</Table>
@ -248,23 +251,23 @@
<div class="detail-item">
<span class="label">盘点单号</span>
<span class="value">{{ detailModal.data.no }}</span>
</div>
</div>
<div class="detail-item">
<span class="label">所属计划</span>
<span class="value">{{ detailModal.data.planName }}</span>
</div>
</div>
<div class="detail-item">
<span class="label">盘点区域</span>
<span class="value">{{ detailModal.data.area }}</span>
</div>
</div>
<div class="detail-item">
<span class="label">执行人</span>
<span class="value">{{ detailModal.data.executor }}</span>
</div>
</div>
<div class="detail-item">
<span class="label">开始时间</span>
<span class="value">{{ detailModal.data.startTime }}</span>
</div>
</div>
<div class="detail-item">
<span class="label">完成时间</span>
<span class="value">{{ detailModal.data.endTime || '-' }}</span>
@ -276,36 +279,56 @@
{{ getInventoryStatusText(detailModal.data.status) }}
</Tag>
</span>
</div>
</div>
<div class="progress-section">
<div class="progress-header">
<h3>盘点进度</h3>
<div class="progress-stats">
<span>已盘点{{ detailModal.data.checked_count || 0 }}</span>
<span>总数量{{ detailModal.data.total_count || 0 }}</span>
<span>完成率{{ detailModal.data.progress || '0%' }}</span>
</div>
</div>
<Progress :percent="detailModal.data.progress_number || 0" :stroke-color="getProgressColor(detailModal.data.progress_number)" />
</div>
<div class="material-info-section">
<h3>物资基本信息</h3>
<div class="material-info-content">
<div class="info-item">
<span class="label">物资名称</span>
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.zichanmingcheng || '-' }}</span>
</div>
<div class="info-item">
<span class="label">物资代码</span>
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.wuzibianma || '-' }}</span>
</div>
<div class="info-item">
<span class="label">规格型号</span>
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.guigexinghao || '-' }}</span>
</div>
<div class="info-item">
<span class="label">计量单位</span>
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.jiliangdanwei || '-' }}</span>
</div>
<div class="info-item">
<span class="label">当前库存</span>
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.inventorys_total || '0' }}</span>
</div>
</div>
</div>
<div class="material-section">
<h3>盘点物资列表</h3>
<div class="history-section">
<h3>盘点历史记录</h3>
<Table
:columns="detailColumns"
:data="detailModal.data.materials || []"
:data="detailModal.data.history || []"
:loading="detailModal.loading"
border
>
<template slot-scope="{ row }" slot="status">
<Tag :color="getInventoryStatusColor(row.status)">{{ getInventoryStatusText(row.status) }}</Tag>
</template>
<template slot-scope="{ row }" slot="diff_quantity">
<span :style="{ color: row.diff_quantity > 0 ? '#19be6b' : row.diff_quantity < 0 ? '#ed4014' : '#515a6e' }">
{{ row.diff_quantity > 0 ? '+' + row.diff_quantity : row.diff_quantity }}
</span>
<template slot-scope="{ row }" slot="photos">
<div class="photo-list" v-if="row.files && row.files.length">
<div class="photo-item" v-for="(file, index) in row.files.slice(0, 3)" :key="index">
<img :src="file.url" @click="previewImage(file.url)" />
</div>
<div class="photo-more" v-if="row.files.length > 3">
+{{ row.files.length - 3 }}
</div>
</div>
<span v-else>-</span>
</template>
</Table>
<div class="pagination-container">
@ -319,17 +342,22 @@
:total="detailModal.total"
/>
</div>
</div>
</div>
</div>
<template slot="footer">
<Button @click="detailModal.visible = false">关闭</Button>
</template>
</Modal>
<!-- 预览弹窗 -->
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview">
<img :src="previewUrl" class="image-preview-large" />
</div>
</div>
</template>
<script>
import { saveStocktakingPlan, getStocktakingPlanList, deleteStocktakingPlan, getStocktakingPlanLinkList } from '@/api/system/stocktaking'
import { saveStocktakingPlan, getStocktakingPlanList, deleteStocktakingPlan, getStocktakingPlanLinkList, getStocktakingPlanLinkDetail } from '@/api/system/stocktaking'
import { getStorehouseTypeList } from '@/api/system/storehouseType'
import { getparameteritem } from '@/api/system/dictionary'
import { index as getWarehouseList } from '@/api/system/baseForm'
@ -388,7 +416,11 @@ export default {
slot: 'area',
key: 'material_info.suozaicangku'
},
{ title: '执行人', key: 'executor' },
{
title: '执行人',
slot: 'executor',
key: 'responsible_admin.name'
},
{
title: '开始时间',
slot: 'startTime',
@ -491,24 +523,40 @@ export default {
total: 0
},
detailColumns: [
{ title: '物资名称', key: 'zichanmingcheng', minWidth: 150 },
{ title: '规格型号', key: 'guigexinghao', minWidth: 120 },
{ title: '单位', key: 'jiliangdanwei', width: 80 },
{ title: '当前库存', key: 'rukushuliang', width: 100 },
{ title: '盘点数量', key: 'check_quantity', width: 100 },
{
title: '差异数量',
slot: 'diff_quantity',
key: 'diff_quantity',
width: 100
title: '盘点日期',
key: 'check_date',
width: 180,
align: 'center'
},
{
title: '盘点数量',
key: 'check_num',
width: 100,
align: 'center'
},
{
title: '状态',
slot: 'status',
key: 'status',
width: 100
width: 100,
align: 'center'
},
{
title: '备注',
key: 'remark',
minWidth: 200,
tooltip: true,
align: 'center'
},
{
title: '照片',
slot: 'photos',
width: 200,
align: 'center'
}
]
],
previewUrl: ''
}
},
mounted() {
@ -720,33 +768,43 @@ export default {
},
async searchList() {
this.listLoading = true
this.listLoading = true;
try {
const params = {
keyword: this.listSearch.keyword,
page: this.listSearch.pageIndex,
page_size: this.listSearch.pageSize,
const formData = new FormData();
formData.append('page', this.listSearch.pageIndex);
formData.append('page_size', this.listSearch.pageSize);
//
if (this.listSearch.keyword) {
formData.append('keyword', this.listSearch.keyword);
}
//
if (this.listSearch.planId) {
params.filter = JSON.stringify([{ key: 'material_infos_plan_id', op: 'eq', value: this.listSearch.planId }])
const filterIndex = this.listSearch.keyword ? 1 : 0;
formData.append(`filter[${filterIndex}][key]`, 'material_infos_plan_id');
formData.append(`filter[${filterIndex}][op]`, 'eq');
formData.append(`filter[${filterIndex}][value]`, this.listSearch.planId);
}
//
if (this.listSearch.status) {
params.filter = params.filter || '[]'
let filterArr = JSON.parse(params.filter)
filterArr.push({ key: 'status', op: 'eq', value: this.listSearch.status })
params.filter = JSON.stringify(filterArr)
const filterIndex = (this.listSearch.keyword ? 1 : 0) + (this.listSearch.planId ? 1 : 0);
formData.append(`filter[${filterIndex}][key]`, 'status');
formData.append(`filter[${filterIndex}][op]`, 'eq');
formData.append(`filter[${filterIndex}][value]`, this.listSearch.status);
}
const res = await getStocktakingPlanLinkList(params)
if (res && res.list.data) {
this.inventoryList = res.list.data
this.listTotal = res.list.total || 0
const res = await getStocktakingPlanLinkList(formData);
if (res && res.list) {
this.inventoryList = res.list.data;
this.listTotal = res.list.total;
}
} catch (e) {
this.$Message.error('获取盘点列表失败')
} catch (error) {
this.$Message.error('获取盘点列表失败');
console.error('获取盘点列表失败:', error);
} finally {
this.listLoading = false
this.listLoading = false;
}
},
resetListSearch() {
@ -768,57 +826,44 @@ export default {
this.listSearch.pageIndex = 1
this.searchList()
},
async viewInventoryDetail(id) {
async viewInventoryDetail(row) {
this.detailModal.loading = true;
this.detailModal.visible = true;
this.detailModal.pageIndex = 1;
try {
//
const mockData = {
no: 'PD202403150001',
planName: '2024年第一季度盘点计划',
area: 'A区仓库',
executor: '张三',
startTime: '2024-03-15 09:00:00',
endTime: '2024-03-15 17:00:00',
status: '1',
checked_count: 8,
total_count: 10,
progress: '80%',
progress_number: 80,
materials: Array.from({ length: 25 }, (_, index) => ({
id: index + 1,
zichanmingcheng: `物资${index + 1}`,
guigexinghao: `型号${index + 1}`,
jiliangdanwei: '个',
rukushuliang: Math.floor(Math.random() * 10) + 1,
check_quantity: Math.floor(Math.random() * 10) + 1,
diff_quantity: Math.floor(Math.random() * 10) - 5,
status: Math.random() > 0.5 ? '1' : '0'
}))
};
//
const start = (this.detailModal.pageIndex - 1) * this.detailModal.pageSize;
const end = start + this.detailModal.pageSize;
const pagedMaterials = mockData.materials.slice(start, end);
//
this.detailModal.data = {
id: row.id,
no: row.no,
planName: row.material_infos_plan ? row.material_infos_plan.name : '-',
area: row.material_info ? row.material_info.suozaicangku : '-',
executor: row.responsible_admin ? row.responsible_admin.name : '-',
startTime: row.material_infos_plan ? row.material_infos_plan.start_date : '-',
endTime: row.material_infos_plan ? row.material_infos_plan.end_date : '-',
status: row.status,
material_info: row.material_info || {},
history: []
};
this.detailModal.data = {
...mockData,
materials: pagedMaterials
};
this.detailModal.total = mockData.materials.length;
try {
//
const res = await request({
url: '/api/admin/material-infos/show',
method: 'get',
params: {
id: row.material_info.id
}
});
//
// const res = await getStocktakingPlanLinkList({
// id,
// page: this.detailModal.pageIndex,
// page_size: this.detailModal.pageSize
// });
// if (res && res.data) {
// this.detailModal.data = res.data;
// this.detailModal.total = res.total || 0;
// }
if (res) {
//
this.detailModal.data = {
...this.detailModal.data,
material_info: res || {},
history: res.material_infos_plan_link || []
};
this.detailModal.total = res.material_infos_plan_link ? res.material_infos_plan_link.length : 0;
}
} catch (error) {
this.$Message.error('获取盘点任务详情失败');
console.error('获取盘点任务详情失败:', error);
@ -834,9 +879,6 @@ export default {
this.searchMaterials()
},
isSelected(item) {
// console.log('isSelected called', item.id)
// console.log('materialModal.selectedMaterialIds', this.materialModal.selectedMaterialIds)
// console.log('this.materialModal.selectedMaterialIds.has(item.id)', this.materialModal.selectedMaterialIds.has(item.id))
return this.materialModal.selectedMaterialIds.has(item.id);
},
handleSelect(selection, item) {
@ -861,28 +903,6 @@ export default {
});
}
},
// console.log('handleSelectionChange called', selection);
// //
// if (this.materialModal.isInitialLoad) {
// this.materialModal.isInitialLoad = false;
// return;
// }
// //
// const planId = this.materialModal.currentPlanId;
// if (planId) {
// //
// this.$nextTick(() => {
// if (this.$refs.materialTable) {
// this.materialList.forEach(row => {
// this.$refs.materialTable.toggleRowSelection(row, this.materialModal.selectedMaterialIds.has(row.id));
// });
// }
// });
// }
// },
handleSelectAll(selection) {
console.log('handleSelectAll called', selection);
@ -1129,12 +1149,6 @@ export default {
this.$Message.error('物资关联失败');
}
},
getProgressColor(percent) {
if (percent >= 100) return '#19be6b';
if (percent >= 60) return '#2d8cf0';
if (percent >= 30) return '#ff9900';
return '#ed4014';
},
handleDetailPageChange(page) {
this.detailModal.pageIndex = page;
this.viewInventoryDetail(this.detailModal.data.id);
@ -1143,6 +1157,12 @@ export default {
this.detailModal.pageSize = size;
this.detailModal.pageIndex = 1;
this.viewInventoryDetail(this.detailModal.data.id);
},
previewImage(url) {
this.previewUrl = url;
},
closePreview() {
this.previewUrl = '';
}
},
watch: {
@ -1341,33 +1361,38 @@ export default {
}
}
.progress-section {
.material-info-section {
margin-bottom: 30px;
.progress-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
h3 {
margin: 0;
font-size: 16px;
color: #17233d;
}
h3 {
margin: 0 0 15px 0;
font-size: 16px;
color: #17233d;
}
.material-info-content {
background: #f8f8f9;
padding: 20px;
border-radius: 8px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
.progress-stats {
display: flex;
gap: 20px;
span {
.info-item {
.label {
font-weight: bold;
color: #666;
margin-right: 10px;
}
.value {
color: #333;
}
}
}
}
.material-section {
.history-section {
h3 {
margin: 0 0 15px 0;
font-size: 16px;
@ -1375,4 +1400,47 @@ export default {
}
}
}
.photo-list {
display: flex;
gap: 8px;
align-items: center;
.photo-item {
width: 40px;
height: 40px;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.photo-more {
color: #666;
font-size: 12px;
}
}
.image-preview-modal {
position: fixed;
z-index: 2000;
left: 0; top: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.7);
display: flex;
align-items: center;
justify-content: center;
}
.image-preview-large {
max-width: 90vw;
max-height: 90vh;
border-radius: 8px;
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
background: #fff;
}
</style>

Loading…
Cancel
Save