| | |
| | | </view> |
| | | <text class="item-id">{{ item.bomNo || "-" }}</text> |
| | | </view> |
| | | <up-tag :text="'V' + (item.version || '1.0')" |
| | | type="primary" |
| | | size="mini" /> |
| | | <view class="item-right"> |
| | | <up-tag :text="'V' + (item.version || '1.0')" |
| | | type="primary" |
| | | size="mini" /> |
| | | <up-tag :text="isEnabled(item) ? '启用' : '停用'" |
| | | :type="isEnabled(item) ? 'success' : 'info'" |
| | | size="mini" /> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | |
| | | size="small" |
| | | type="primary" |
| | | @click="goStructure(item)">查看详情</up-button> |
| | | <up-button class="action-btn" |
| | | size="small" |
| | | :type="isEnabled(item) ? 'warning' : 'success'" |
| | | @click="toggleStatus(item)">{{ isEnabled(item) ? "停用" : "启用" }}</up-button> |
| | | <up-button class="action-btn" |
| | | size="small" |
| | | type="primary" |
| | | plain |
| | | @click="openKitCheck(item)">齐套分析</up-button> |
| | | </view> |
| | | </view> |
| | | <up-loadmore :status="pageStatus" /> |
| | |
| | | <up-empty text="暂无BOM数据" |
| | | mode="list"></up-empty> |
| | | </view> |
| | | |
| | | <!-- 齐套分析弹窗 --> |
| | | <up-popup :show="showKitPopup" |
| | | mode="bottom" |
| | | round="10" |
| | | @close="showKitPopup = false"> |
| | | <view class="kit-popup"> |
| | | <view class="popup-header"> |
| | | <text class="popup-title">齐套分析 - {{ currentBom.bomNo || "" }}</text> |
| | | </view> |
| | | <view class="kit-form"> |
| | | <text class="kit-form-label">需求数量</text> |
| | | <view class="kit-form-input"> |
| | | <up-number-box v-model="demandQty" |
| | | :min="0" |
| | | :step="1"></up-number-box> |
| | | </view> |
| | | <up-button type="primary" |
| | | size="small" |
| | | :loading="kitLoading" |
| | | text="分析" |
| | | @click="handleKitCheck"></up-button> |
| | | </view> |
| | | <scroll-view scroll-y |
| | | class="kit-result"> |
| | | <view v-if="kitResult.length > 0"> |
| | | <view v-for="(row, index) in kitResult" |
| | | :key="index" |
| | | class="kit-item"> |
| | | <view class="kit-item-header"> |
| | | <text class="kit-name">{{ row.productName || "-" }}</text> |
| | | <text class="kit-model">{{ row.model || "-" }}</text> |
| | | <up-tag v-if="num(row.shortageQty) > 0" |
| | | text="缺口" |
| | | type="error" |
| | | size="mini" /> |
| | | </view> |
| | | <view class="kit-grid"> |
| | | <view class="kit-cell"> |
| | | <text class="kit-label">单位</text> |
| | | <text class="kit-value">{{ row.unit || "-" }}</text> |
| | | </view> |
| | | <view class="kit-cell"> |
| | | <text class="kit-label">需求数量</text> |
| | | <text class="kit-value">{{ num(row.requiredQty) }}</text> |
| | | </view> |
| | | <view class="kit-cell"> |
| | | <text class="kit-label">可用库存</text> |
| | | <text class="kit-value">{{ num(row.availableQty) }}</text> |
| | | </view> |
| | | <view class="kit-cell"> |
| | | <text class="kit-label">缺口数量</text> |
| | | <text class="kit-value" |
| | | :class="{ danger: num(row.shortageQty) > 0 }">{{ num(row.shortageQty) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="kit-empty"> |
| | | <up-empty mode="data" |
| | | :text="kitDone ? '库存齐套,无缺口' : '请输入需求数量后点击分析'"></up-empty> |
| | | </view> |
| | | </scroll-view> |
| | | <view class="filter-actions"> |
| | | <up-button type="primary" |
| | | size="default" |
| | | :disabled="!kitDone" |
| | | text="生成采购需求" |
| | | @click="handleGenerateDemand"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { reactive, ref } from "vue"; |
| | | import { onReachBottom, onShow } from "@dcloudio/uni-app"; |
| | | import { listPage } from "@/api/productionManagement/bom"; |
| | | import { |
| | | listPage, |
| | | changeStatus, |
| | | kitCheck, |
| | | generateDemand, |
| | | } from "@/api/productionManagement/bom"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | const queryParams = reactive({ |
| | | productName: "", |
| | |
| | | size: 3, |
| | | total: 0, |
| | | }); |
| | | |
| | | // 后端不返回 status 时按启用展示(兼容老数据) |
| | | const isEnabled = item => |
| | | item.status === undefined || |
| | | item.status === null || |
| | | Number(item.status) === 1; |
| | | |
| | | // decimal 可能是字符串,统一转数字兜底 |
| | | const num = val => Number(val) || 0; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | uni.showToast({ title: "查询失败", icon: "error" }); |
| | | // 错误提示由 request 统一处理 |
| | | pageStatus.value = "loadmore"; |
| | | }); |
| | | }; |
| | |
| | | item.remark || "" |
| | | )}&version=${encodeURIComponent(item.version || 1)}`, |
| | | }); |
| | | }; |
| | | |
| | | // 启用/停用,status=1 时后端自动停用同规格其它版本 |
| | | const toggleStatus = item => { |
| | | const enabled = isEnabled(item); |
| | | const nextStatus = enabled ? 0 : 1; |
| | | const actionText = enabled ? "停用" : "启用"; |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: `确定${actionText}该BOM版本吗?`, |
| | | success: res => { |
| | | if (!res.confirm) return; |
| | | uni.showLoading({ title: "处理中...", mask: true }); |
| | | changeStatus({ id: item.id, status: nextStatus }) |
| | | .then(() => { |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: `${actionText}成功`, icon: "none" }); |
| | | handleSearch(); |
| | | }) |
| | | .catch(() => { |
| | | uni.hideLoading(); |
| | | }); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // --- 齐套分析 --- |
| | | const showKitPopup = ref(false); |
| | | const kitLoading = ref(false); |
| | | const kitDone = ref(false); |
| | | const currentBom = ref({}); |
| | | const demandQty = ref(1); |
| | | const kitResult = ref([]); |
| | | |
| | | const openKitCheck = item => { |
| | | currentBom.value = item; |
| | | demandQty.value = 1; |
| | | kitResult.value = []; |
| | | kitDone.value = false; |
| | | showKitPopup.value = true; |
| | | }; |
| | | |
| | | const handleKitCheck = () => { |
| | | const qty = Number(demandQty.value); |
| | | if (!Number.isFinite(qty) || qty <= 0) { |
| | | uni.showToast({ title: "需求数量必须大于0", icon: "none" }); |
| | | return; |
| | | } |
| | | kitLoading.value = true; |
| | | kitCheck({ bomId: currentBom.value.id, demandQty: qty }) |
| | | .then(res => { |
| | | kitResult.value = res?.data || []; |
| | | kitDone.value = true; |
| | | }) |
| | | .catch(() => { |
| | | // 错误提示由 request 统一处理 |
| | | kitResult.value = []; |
| | | kitDone.value = false; |
| | | }) |
| | | .finally(() => { |
| | | kitLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleGenerateDemand = () => { |
| | | uni.showLoading({ title: "处理中...", mask: true }); |
| | | generateDemand({ |
| | | bomId: currentBom.value.id, |
| | | demandQty: Number(demandQty.value), |
| | | }) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: String(res?.data ?? "采购需求生成完成"), |
| | | showCancel: false, |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | uni.hideLoading(); |
| | | }); |
| | | }; |
| | | |
| | | onReachBottom(() => { |
| | |
| | | } |
| | | |
| | | .action-btn { |
| | | width: calc(50% - 15rpx); |
| | | width: calc(33.33% - 10rpx); |
| | | margin: 0 !important; |
| | | margin-bottom: 15rpx !important; |
| | | } |
| | | |
| | | // --- 齐套分析弹窗 --- |
| | | .kit-popup { |
| | | padding-bottom: 20rpx; |
| | | } |
| | | |
| | | .kit-form { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | padding: 20rpx 30rpx; |
| | | |
| | | .kit-form-label { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | .kit-form-input { |
| | | flex: 1; |
| | | } |
| | | } |
| | | |
| | | .kit-result { |
| | | height: 600rpx; |
| | | padding: 0 30rpx; |
| | | } |
| | | |
| | | .kit-item { |
| | | border: 1rpx solid #f0f0f0; |
| | | border-radius: 12rpx; |
| | | padding: 16rpx; |
| | | margin-bottom: 16rpx; |
| | | } |
| | | |
| | | .kit-item-header { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12rpx; |
| | | margin-bottom: 12rpx; |
| | | |
| | | .kit-name { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .kit-model { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | flex: 1; |
| | | } |
| | | } |
| | | |
| | | .kit-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | |
| | | .kit-cell { |
| | | width: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 8rpx; |
| | | |
| | | .kit-label { |
| | | font-size: 24rpx; |
| | | color: #777; |
| | | min-width: 110rpx; |
| | | } |
| | | |
| | | .kit-value { |
| | | font-size: 24rpx; |
| | | color: #333; |
| | | |
| | | &.danger { |
| | | color: #ee0a24; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .kit-empty { |
| | | padding: 40rpx 0; |
| | | } |
| | | </style> |