Files
im-uniapp/pages/workbench/wms/purchase.vue
砂糖 05ff3391c7 feat(hrm): 新增拨款申请功能并修复仓储显示问题
- 添加拨款申请相关组件、API及页面逻辑
- 修复仓储模块列表显示边框和内容插槽问题
- 更新版本记录至5.2.3版本
2026-04-14 09:33:58 +08:00

693 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 采购单列表滚动加载区域 -->
<scroll-view scroll-y @scrolltolower="loadMore" :style="{ height: scrollHeight + 'px' }" class="list-scroll">
<!-- 单据列表容器 -->
<view class="documents-container">
<!-- 单据列表 -->
<view v-if="TaskList.length > 0">
<view class="document-card" v-for="(item, index) in TaskList" :key="index" @click="showDetail(item)">
<!-- 单据头部 -->
<view class="doc-header">
<view class="doc-title">采购单</view>
<uni-tag :type="item.status == 1 ? 'success' : 'warning'"
:text='item.status == 1 ? "已完成" : "未完成"'></uni-tag>
</view>
<!-- 单据编号区域 -->
<view class="doc-number">
<text class="label">编号</text>
<text class="value">{{ item.masterNum }}</text>
</view>
<!-- 单据内容区域 -->
<view class="doc-content">
<view class="doc-row">
<view class="doc-col">
<text class="label">操作时间</text>
<text class="value">{{ parseTime(item.signTime) }}</text>
</view>
</view>
<view class="doc-row">
<view class="doc-col">
<text class="label">操作人</text>
<text class="value">{{ item.signUser }}</text>
</view>
</view>
<view class="doc-row" v-if="item.remark">
<view class="doc-col full-width">
<text class="label">备注</text>
<text class="value remark">{{ item.remark }}</text>
</view>
</view>
</view>
<!-- 单据底部 -->
<view class="doc-footer">
<view class="doc-seal">
<uni-icon type="stamp" size="24" color="#999"></uni-icon>
<text class="seal-text">电子单据</text>
</view>
<uni-icon type="right" size="18" color="#666" class="arrow-icon"></uni-icon>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!loading && TaskList.length === 0">
<uni-icon type="empty" size="60" color="#ccc"></uni-icon>
<text class="empty-text">暂无采购单数据</text>
</view>
</view>
<!-- 加载更多组件 -->
<uni-load-more :status="loadMoreStatus" :content-text="{
more: '上拉加载更多',
loading: '加载中...',
noMore: '没有更多数据了'
}" class="load-more"></uni-load-more>
</scroll-view>
<!-- 详情弹窗 -->
<uni-popup type="bottom" :duration="300" ref="popup" height="700rpx">
<view class="popup-container">
<view style="display: flex; justify-content: flex-end;">
<button @click="submitComplete">执行入库</button>
<button @click="handComplete">完成采购</button>
</view>
<view class="detail-scroll">
<view class="detail-document">
<!-- 明细列表 -->
<uni-list v-if="warehouseTaskList.length > 0" :border="false">
<checkbox-group @change="checkboxChange">
<uni-list-item class="detail-item" v-for="(item, index) in warehouseTaskList" :key="index">
<view class="detail-content" slot="body">
<view class="detail-title">
<view class="">
<text class="item-num">{{ index + 1 }}.</text>
<text class="item-name">{{ item.name }}</text>
<text style="margin-left: 20rpx;">
<text v-if="item.taskStatus == 0">未采购</text>
<text v-if="item.taskStatus == 1">在途</text>
<text v-if="item.taskStatus == 2">已完成</text>
<!-- <text v-if="item.taskStatus == 3">作废</text> -->
</text>
</view>
<checkbox v-if="currentItem.status == 0" :value="item.taskId" :checked="item.checked"
:disabled="item.taskStatus == 2"></checkbox>
</view>
<view class="detail-grid">
<view class="grid-item">
<text class="grid-label">数量</text>
<text class="grid-value">{{ item.taskInventory }}{{ item.unit }}</text>
</view>
<view class="grid-item">
<text class="grid-label">品牌</text>
<text class="grid-value">{{ item.brand || '无' }}</text>
</view>
</view>
<view class="detail-grid">
<view class="grid-item">
<text class="grid-label">型号</text>
<text class="grid-value">{{ item.model || '无' }}</text>
</view>
<view class="grid-item">
<text class="grid-label">规格</text>
<text class="grid-value">{{ item.specifications || '无' }}</text>
</view>
</view>
<view class="detail-remark" v-if="item.remark || item.checked">
<text class="remark-label">备注</text>
<text class="remark-value" v-if="!item.checked">{{ item.remark }}</text>
<input class="remark-value" v-else type="text" v-model="item.remark" placeholder="输入备注" />
</view>
<view class="detail-remark" v-if="item.checked">
<text class="remark-label">采购价格</text>
<input type="number" v-model="item.price" />
</view>
</view>
</uni-list-item>
</checkbox-group>
</uni-list>
<view class="empty-state" v-else>
<uni-icon type="empty" size="50" color="#ccc"></uni-icon>
<text class="empty-text">暂无采购单明细</text>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import {
listOaWarehouseMaster,
updateOaWarehouseMaster
} from "@/api/oa/wms/warehouseMaster";
import {
getOaWarehouseTaskByMasterId,
updateOaWarehouseTaskBatch
} from "@/api/oa/wms/warehouseTask";
export default {
name: "PurchaseOrderList",
data() {
return {
// 采购单列表数据
TaskList: [],
// 详情数据
warehouseTaskList: [],
// 当前选中项
currentItem: {},
// 加载状态
loading: false,
// 滚动区域高度
scrollHeight: 0,
// 分页参数
page: 1,
pageSize: 10,
// 加载更多状态
loadMoreStatus: 'more',
// 查询参数
queryParams: {
signTime: '',
type: 2,
pageNum: 1,
pageSize: 10
}
};
},
onLoad() {
// 计算滚动区域高度
this.calculateScrollHeight();
// 获取初始数据
this.getList();
},
methods: {
/** 计算滚动区域高度 */
calculateScrollHeight() {
const systemInfo = uni.getSystemInfoSync();
const headerHeight = 20; // 头部筛选区域高度
this.scrollHeight = systemInfo.windowHeight - headerHeight;
},
submitComplete() {
const rows = [...this.warehouseTaskList].filter(item => item.checked)
if (rows.length == 0) {
uni.showToast({
title: '请选择采购明细',
icon: 'fail'
})
}
// 前端直接设值(实际项目可调用接口)
rows.forEach(r => {
r.taskStatus = 2
})
// 批量入库采购单
updateOaWarehouseTaskBatch(rows).then(res => {
this.getList();
this.$refs.popup.close()
uni.showToast({
icon: 'none',
title: "入库成功"
})
})
},
checkboxChange: function(e) {
var items = this.warehouseTaskList,
values = e.detail.value;
for (var i = 0, lenI = items.length; i < lenI; ++i) {
const item = items[i]
if (values.includes(item.taskId)) {
this.$set(item, 'checked', true)
console.log('选中', item);
} else {
this.$set(item, 'checked', false)
console.log("取消选中", item)
}
}
},
/** 日期选择变化时触发 */
handleDateChange() {
// 重置分页,重新加载数据
this.page = 1;
this.TaskList = [];
this.getList();
},
handComplete(row) {
this.currentItem.status = 1;
updateOaWarehouseMaster(this.currentItem).then((res) => {
uni.showToast({
icon: 'none',
title: '操作成功'
})
this.$refs.popup.close()
this.getList();
});
},
/** 查询采购单列表 */
getList() {
// 显示加载状态
this.loading = true;
// 设置分页参数
this.queryParams.pageNum = this.page;
this.queryParams.pageSize = this.pageSize;
listOaWarehouseMaster(this.queryParams)
.then((res) => {
const newData = res.rows || [];
// 如果是第一页,直接替换数据;否则追加数据
if (this.page === 1) {
this.TaskList = newData;
} else {
this.TaskList = [...this.TaskList, ...newData];
}
// 更新加载状态
if (newData.length < this.pageSize) {
this.loadMoreStatus = 'noMore'; // 没有更多数据
} else {
this.loadMoreStatus = 'more'; // 还有更多数据
}
})
.catch(() => {
uni.showToast({
title: '加载失败',
icon: 'none',
duration: 2000
});
})
.finally(() => {
this.loading = false;
});
},
/** 加载更多数据 */
loadMore() {
// 如果正在加载中或没有更多数据,则不执行
if (this.loading || this.loadMoreStatus !== 'more') {
return;
}
// 显示加载中状态
this.loadMoreStatus = 'loading';
// 页码加1
this.page++;
// 获取更多数据
this.getList();
},
/** 查看详情 */
showDetail(item) {
this.currentItem = item;
this.$refs.popup.open();
this.loading = true;
// 获取采购单详情
getOaWarehouseTaskByMasterId(item.masterId)
.then((res) => {
this.warehouseTaskList = res.data.map(item => {
return {
...item,
checked: false,
price: 0,
}
}) || [];
})
.catch(() => {
uni.showToast({
title: '获取详情失败',
icon: 'none',
duration: 2000
});
})
.finally(() => {
this.loading = false;
});
},
/** 计算日期差值(天) */
dayDiff(endTime) {
const end = new Date(endTime);
const now = new Date();
end.setHours(0, 0, 0, 0);
now.setHours(0, 0, 0, 0);
return Math.floor((end - now) / (1000 * 60 * 60 * 24));
},
onDateChange(e) {
this.queryParams.signTime = e.detail.value
},
/** 格式化时间 */
parseTime(time, format = '{y}-{m}-{d}') {
if (!time) return '';
const date = new Date(time);
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate()
};
return format.replace(/{(y|m|d)}/g, (match, key) => {
let value = formatObj[key];
if (value < 10) {
value = '0' + value;
}
return value;
});
}
}
};
</script>
<style scoped>
.container {
background-color: #f0f2f5;
min-height: 100vh;
padding: 20rpx;
box-sizing: border-box;
}
/* 滚动区域样式 */
.list-scroll {
width: 100%;
margin: 0 auto;
}
/* 单据容器 */
.documents-container {
/* margin-top: 20rpx; */
display: flex;
flex-direction: column;
gap: 20rpx;
padding-bottom: 30rpx;
}
/* 单据卡片样式 - 核心优化点 */
.document-card {
margin-top: 10rpx;
background-color: #fff;
border-radius: 8rpx;
border: 1px solid #e5e7eb;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
position: relative;
overflow: hidden;
}
/* 单据头部 */
.doc-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
padding-bottom: 15rpx;
border-bottom: 1px dashed #e5e7eb;
}
.doc-title {
font-size: 32rpx;
font-weight: 600;
color: #1d2129;
display: flex;
align-items: center;
}
.doc-title::before {
content: '';
display: inline-block;
width: 8rpx;
height: 24rpx;
background-color: #1677ff;
margin-right: 10rpx;
border-radius: 2rpx;
}
.status-tag {
transform: scale(0.85);
transform-origin: right center;
}
/* 单据编号 */
.doc-number {
padding: 10rpx 0;
margin-bottom: 10rpx;
border-left: 3rpx solid #e5e7eb;
padding-left: 15rpx;
}
.doc-number .label {
color: #86909c;
font-size: 26rpx;
}
.doc-number .value {
color: #1d2129;
font-size: 28rpx;
font-family: monospace;
letter-spacing: 1rpx;
}
/* 单据内容区域 */
.doc-content {
margin-bottom: 15rpx;
}
.doc-row {
display: flex;
margin-bottom: 12rpx;
}
.doc-col {
flex: 1;
}
.doc-col.full-width {
flex: 100%;
}
.label {
color: #86909c;
font-size: 26rpx;
display: inline-block;
width: 140rpx;
}
.value {
color: #1d2129;
font-size: 26rpx;
}
.remark {
color: #4e5969;
line-height: 1.5;
}
/* 单据底部 */
.doc-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 15rpx;
margin-top: 10rpx;
border-top: 1px dashed #e5e7eb;
}
.doc-seal {
display: flex;
align-items: center;
color: #86909c;
font-size: 22rpx;
}
.seal-text {
margin-left: 8rpx;
}
.arrow-icon {
opacity: 0.7;
}
/* 空状态样式 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 150rpx 0;
}
.empty-text {
font-size: 28rpx;
color: #86909c;
margin-top: 20rpx;
}
/* 加载更多样式 */
.load-more {
padding: 30rpx 0;
}
/* 详情弹窗样式 */
.popup-container {
background-color: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
max-height: 1000rpx;
display: flex;
flex-direction: column;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 25rpx;
border-bottom: 1px solid #f2f3f5;
}
.popup-title {
font-size: 30rpx;
font-weight: 600;
color: #1d2129;
}
.close-icon {
color: #86909c;
}
.detail-scroll {
flex: 1;
padding: 20rpx;
height: 100%;
overflow-y: scroll;
}
.detail-document {
background-color: #fafafa;
border-radius: 10rpx;
border: 1px solid #f2f3f5;
padding: 15rpx;
}
.detail-header {
display: flex;
justify-content: space-between;
padding: 10rpx 0;
margin-bottom: 15rpx;
border-bottom: 1px dashed #e5e7eb;
}
.detail-masterNum {
font-size: 26rpx;
color: #1d2129;
font-family: monospace;
}
.detail-date {
font-size: 24rpx;
color: #86909c;
}
.detail-item {
margin-bottom: 15rpx;
background-color: #fff;
border-radius: 8rpx;
border: 1px solid #f2f3f5;
overflow: hidden;
}
.detail-content {
padding: 18rpx;
}
.detail-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15rpx;
padding-bottom: 10rpx;
border-bottom: 1px solid #f2f3f5;
}
.item-num {
display: inline-block;
width: 36rpx;
height: 36rpx;
line-height: 36rpx;
text-align: center;
background-color: #f0f7ff;
color: #1677ff;
border-radius: 50%;
font-size: 22rpx;
margin-right: 10rpx;
}
.item-name {
font-size: 28rpx;
color: #1d2129;
font-weight: 500;
}
.detail-grid {
display: flex;
justify-content: space-between;
margin-bottom: 12rpx;
}
.grid-item {
flex: 1;
font-size: 24rpx;
}
.grid-item:first-child {
padding-right: 10rpx;
}
.grid-item:last-child {
padding-left: 10rpx;
}
.grid-label {
color: #86909c;
}
.grid-value {
color: #4e5969;
}
.detail-remark {
margin-top: 10rpx;
padding-top: 10rpx;
border-top: 1px dashed #f2f3f5;
font-size: 24rpx;
}
.remark-label {
color: #86909c;
}
.remark-value {
color: #4e5969;
line-height: 1.5;
}
</style>