| | |
| | | <view class="search-input"> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请输入客户名称搜索" |
| | | v-model="searchForm.customerName" |
| | | placeholder="请输入订单号或产品名称" |
| | | v-model="searchForm.keyword" |
| | | @change="handleQuery" |
| | | clearable |
| | | /> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 生产订单列表 --> |
| | | <view class="ledger-list" v-if="tableData.length > 0"> |
| | | <!-- 列表区域 --> |
| | | <scroll-view scroll-y class="list-container" v-if="tableData.length > 0" @scrolltolower="loadMore"> |
| | | <view v-for="(item, index) in tableData" :key="item.id || index"> |
| | | <view class="ledger-item"> |
| | | <view class="item-header"> |
| | |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.salesContractNo }}</text> |
| | | <text class="item-id">{{ item.npsNo }}</text> |
| | | </view> |
| | | <view class="item-right"> |
| | | <up-tag :text="getStatusText(item.status)" :type="getStatusType(item.status)" size="mini" /> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">录入日期</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客户合同号</text> |
| | | <text class="detail-value">{{ item.customerContractNo }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客户名称</text> |
| | | <text class="detail-value">{{ item.customerName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项目名称</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产品大类</text> |
| | | <text class="detail-value">{{ item.productCategory }}</text> |
| | | <text class="detail-label">产品名称</text> |
| | | <text class="detail-value font-bold">{{ item.productName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">规格型号</text> |
| | | <text class="detail-value">{{ item.specificationModel }}</text> |
| | | <text class="detail-value">{{ item.model || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">数量</text> |
| | | <text class="detail-value">{{ item.quantity }} {{ item.unit }}</text> |
| | | <text class="detail-label">订单数量</text> |
| | | <text class="detail-value">{{ item.quantity || 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">排产数量</text> |
| | | <text class="detail-value highlight">{{ item.schedulingNum }}</text> |
| | | <text class="detail-label">完成进度</text> |
| | | <view class="progress-box"> |
| | | <up-line-progress :percentage="toProgressPercentage(item.completionStatus)" |
| | | :activeColor="progressColor(item.completionStatus)" |
| | | height="10"></up-line-progress> |
| | | <text class="progress-text">{{ item.completeQuantity || 0 }} / {{ item.quantity || 0 }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">完工数量</text> |
| | | <text class="detail-value highlight">{{ item.successNum }}</text> |
| | | <text class="detail-label">计划完成</text> |
| | | <text class="detail-value">{{ formatDate(item.planCompleteTime) }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="item-footer"> |
| | | <view class="action-btns"> |
| | | <up-button type="primary" size="mini" plain text="来源" @click="goSource(item)"></up-button> |
| | | <up-button type="success" size="mini" plain text="领料详情" @click="goPickingDetail(item)"></up-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <up-loadmore :status="loadStatus" v-if="tableData.length >= page.size" /> |
| | | </scroll-view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <text>暂无生产订单数据</text> |
| | | <up-empty mode="data" text="暂无生产订单数据"></up-empty> |
| | | </view> |
| | | </view> |
| | | </template> |
| | |
| | | import { ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import dayjs from "dayjs"; |
| | | import {schedulingListPage} from "@/api/productionManagement/productionOrder.js"; |
| | | import { productOrderListPage } from "@/api/productionManagement/productionOrder.js"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 加载状态 |
| | | const loading = ref(false); |
| | | const loadStatus = ref('loadmore'); |
| | | // 列表数据 |
| | | const tableData = ref([]); |
| | | |
| | | // 分页配置 |
| | | const page = reactive({ |
| | | current: -1, |
| | | size: -1, |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | // 搜索表单数据 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | customerName: "", |
| | | keyword: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // 通用提示函数 |
| | | const showLoadingToast = (message) => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true |
| | | }); |
| | | }; |
| | | |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 格式化日期 |
| | | const formatDate = (date) => { |
| | | return date ? dayjs(date).format('YYYY-MM-DD') : '-'; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | 1: "待开始", |
| | | 2: "进行中", |
| | | 3: "已完成", |
| | | 4: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const typeMap = { |
| | | 1: "primary", |
| | | 2: "warning", |
| | | 3: "success", |
| | | 4: "danger", |
| | | }; |
| | | return typeMap[status] || "info"; |
| | | }; |
| | | |
| | | // 完成进度百分比 |
| | | const toProgressPercentage = (val) => { |
| | | const n = Number(val); |
| | | if (!Number.isFinite(n)) return 0; |
| | | if (n <= 0) return 0; |
| | | if (n >= 100) return 100; |
| | | return Math.round(n); |
| | | }; |
| | | |
| | | // 进度条颜色 |
| | | const progressColor = (percentage) => { |
| | | const p = toProgressPercentage(percentage); |
| | | if (p < 30) return "#f56c6c"; |
| | | if (p < 50) return "#e6a23c"; |
| | | if (p < 80) return "#409eff"; |
| | | return "#67c23a"; |
| | | }; |
| | | |
| | | // 查询列表 |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | tableData.value = []; // 重置列表数据 |
| | | tableData.value = []; |
| | | getList(); |
| | | }; |
| | | |
| | | // 加载更多 |
| | | const loadMore = () => { |
| | | if (loadStatus.value === 'nomore' || loading.value) return; |
| | | page.current++; |
| | | getList(); |
| | | }; |
| | | |
| | | // 获取列表数据 |
| | | const getList = () => { |
| | | loading.value = true; |
| | | showLoadingToast('加载中...'); |
| | | loadStatus.value = 'loading'; |
| | | |
| | | // 构造请求参数 |
| | | const params = { ...searchForm.value, ...page }; |
| | | const params = { |
| | | current: page.current, |
| | | size: page.size, |
| | | npsNo: searchForm.value.keyword, |
| | | productName: searchForm.value.keyword |
| | | }; |
| | | |
| | | schedulingListPage(params).then((res) => { |
| | | productOrderListPage(params).then((res) => { |
| | | loading.value = false; |
| | | closeToast(); |
| | | const records = res.data.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | |
| | | tableData.value = res.data.records || []; |
| | | if (records.length < page.size) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | } |
| | | page.total = res.data.total || 0; |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | closeToast(); |
| | | loadStatus.value = 'loadmore'; |
| | | uni.showToast({ |
| | | title: '加载失败', |
| | | icon: 'error' |
| | |
| | | }); |
| | | }; |
| | | |
| | | // 跳转来源 |
| | | const goSource = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/productionManagement/productionOrder/source?id=${item.id}&productName=${encodeURIComponent(item.productName)}&model=${encodeURIComponent(item.model)}&quantity=${item.quantity}` |
| | | }); |
| | | }; |
| | | |
| | | // 跳转领料详情 |
| | | const goPickingDetail = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/productionManagement/productionOrder/pickingDetail?id=${item.id}&npsNo=${item.npsNo}` |
| | | }); |
| | | }; |
| | | |
| | | // 页面显示时加载数据 |
| | | onShow(() => { |
| | | // 加载列表数据 |
| | | getList(); |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/styles/sales-common.scss'; |
| | | |
| | | // 生产订单页面样式 |
| | | .production-order { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | // 重写部分样式以适配生产订单 |
| | | .list-container { |
| | | flex: 1; |
| | | height: 0; |
| | | } |
| | | |
| | | .ledger-item { |
| | | .detail-value.highlight { |
| | | color: #ff6b35; |
| | | font-weight: 600; |
| | | background: #fff; |
| | | margin: 20rpx; |
| | | padding: 24rpx; |
| | | border-radius: 16rpx; |
| | | box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06); |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding-bottom: 12rpx; |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .document-icon { |
| | | width: 44rpx; |
| | | height: 44rpx; |
| | | background: #3c9cff; |
| | | border-radius: 10rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 20rpx; |
| | | } |
| | | |
| | | .item-id { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 16rpx 0; |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | margin-bottom: 16rpx; |
| | | |
| | | .detail-label { |
| | | font-size: 26rpx; |
| | | color: #999; |
| | | min-width: 140rpx; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 26rpx; |
| | | color: #333; |
| | | text-align: right; |
| | | |
| | | &.font-bold { |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .progress-box { |
| | | flex: 1; |
| | | margin-left: 40rpx; |
| | | |
| | | .progress-text { |
| | | font-size: 22rpx; |
| | | color: #999; |
| | | margin-top: 4rpx; |
| | | display: block; |
| | | text-align: right; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .item-footer { |
| | | padding-top: 20rpx; |
| | | border-top: 1rpx solid #f0f0f0; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | |
| | | .action-btns { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 适配 uView 组件样式 |
| | | :deep(.up-input) { |
| | | background: transparent; |
| | | } |
| | | |
| | | :deep(.up-datetime-picker) { |
| | | width: 100%; |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | </style> |