| | |
| | | <text class="detail-value">{{ item.model || '无' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">机台名称</text> |
| | | <text class="detail-value">{{ item.deviceName || '无' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">工序名称 / 计量单位</text> |
| | | <text class="detail-value">{{ item.processName || '无' }} / {{ item.unit || '无' }}</text> |
| | | </view> |
| | |
| | | ></up-line-progress> |
| | | </view> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">实际开始</text> |
| | | <text class="detail-value">{{ item.startProductTime || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">实际结束</text> |
| | | <text class="detail-value">{{ item.endProductTime || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="item-actions" v-if="!isEnded(item)"> |
| | | <up-button |
| | | text="开始报工" |
| | | size="mini" |
| | | type="primary" |
| | | :disabled="!canStartProduction(item) || startSubmittingId === item.id" |
| | | @click.stop="handleStartProduction(item)" |
| | | /> |
| | | <up-button |
| | | text="结束报工" |
| | | text="报工" |
| | | size="mini" |
| | | type="success" |
| | | :disabled="!canEndProduction(item)" |
| | |
| | | </view> |
| | | </up-popup> |
| | | |
| | | <!-- 结束报工弹窗 --> |
| | | <!-- 报工弹窗 --> |
| | | <up-popup |
| | | v-model:show="endReportVisible" |
| | | mode="bottom" |
| | |
| | | <view class="report-modal"> |
| | | <view class="modal-header"> |
| | | <view class="modal-header-left"> |
| | | <text class="modal-title">结束报工</text> |
| | | <text class="modal-title">报工</text> |
| | | <text class="modal-subtitle" v-if="endReportRow"> |
| | | {{ endReportRow.workOrderNo || '-' }} · {{ endReportRow.deviceName || '-' }} |
| | | </text> |
| | |
| | | </view> |
| | | |
| | | <scroll-view class="modal-content" scroll-y> |
| | | <view class="report-summary" v-if="endReportRow"> |
| | | <!-- <view class="report-summary" v-if="endReportRow"> |
| | | <view class="summary-left"> |
| | | <text class="summary-title">{{ endReportRow.productName || '-' }}</text> |
| | | <text class="summary-sub">{{ endReportRow.processName || '-' }} · {{ endReportRow.model || '-' }}</text> |
| | |
| | | <text class="summary-num">{{ endReportForm.planQuantity || '0' }}</text> |
| | | <text class="summary-label">待生产</text> |
| | | </view> |
| | | </view> |
| | | </view> --> |
| | | |
| | | <up-form :model="endReportForm" labelWidth="120"> |
| | | <view class="form-section"> |
| | |
| | | <up-input v-model="endReportForm.planQuantity" disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="本次生产数量" required> |
| | | <up-input v-model="endReportForm.quantity" disabled /> |
| | | <up-input v-model="endReportForm.quantity" /> |
| | | </up-form-item> |
| | | <up-form-item label="补产数量"> |
| | | <up-input v-model="endReportForm.replenishQty" type="number" placeholder="请输入" /> |
| | |
| | | <up-form-item label="报废数量"> |
| | | <up-input v-model="endReportForm.scrapQty" type="number" placeholder="请输入" /> |
| | | </up-form-item> |
| | | <up-form-item label="开始时间" required> |
| | | <up-input |
| | | v-model="endReportForm.startTime" |
| | | readonly |
| | | placeholder="请选择开始时间" |
| | | @click="startTimePickerVisible = true" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <up-form-item label="结束时间" required> |
| | | <up-input |
| | | v-model="endReportForm.endTime" |
| | | readonly |
| | | placeholder="请选择结束时间" |
| | | @click="endTimePickerVisible = true" |
| | | /> |
| | | </up-form-item> |
| | | </view> |
| | | |
| | | <view class="form-section"> |
| | | <up-form-item label="机台" required> |
| | | <up-input |
| | | v-model="endReportForm.deviceName" |
| | | readonly |
| | | placeholder="请选择" |
| | | @click="openDevicePicker" |
| | | suffixIcon="arrow-down" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="班组人员"> |
| | | <up-input |
| | | v-model="teamDisplayText" |
| | |
| | | @select="onAuditSelect" |
| | | @close="auditPickerVisible = false" |
| | | /> |
| | | <up-action-sheet |
| | | :show="devicePickerVisible" |
| | | :actions="deviceActions" |
| | | title="选择机台" |
| | | @select="onDeviceSelect" |
| | | @close="devicePickerVisible = false" |
| | | /> |
| | | |
| | | <!-- 时间选择器 --> |
| | | <up-datetime-picker |
| | |
| | | import { ref, reactive, toRefs, computed, getCurrentInstance } from "vue"; |
| | | import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app"; |
| | | import { productWorkOrderPage, addProductMain, startProduction, getProductWorkOrderById,getProductionProductMain } from "@/api/productionManagement/productionReporting.js"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import FilesDia from "./components/filesDia.vue"; |
| | |
| | | quantity: "", |
| | | replenishQty: "0", |
| | | scrapQty: "0", |
| | | deviceLedgerId: "", |
| | | deviceName: "", |
| | | teamList: [], |
| | | startTime: "", |
| | | endTime: "", |
| | |
| | | const userOptions = ref([]); |
| | | const auditPickerVisible = ref(false); |
| | | const auditActions = computed(() => userOptions.value.map(u => ({ name: u.name, value: u.value }))); |
| | | |
| | | const deviceOptions = ref([]); |
| | | const devicePickerVisible = ref(false); |
| | | const deviceActions = computed(() => deviceOptions.value.map(d => ({ name: d?.deviceName || "", value: d?.id }))); |
| | | |
| | | const teamPickerVisible = ref(false); |
| | | const teamCheckedIds = ref([]); |
| | |
| | | } |
| | | }; |
| | | |
| | | const ensureDeviceOptions = async () => { |
| | | if (deviceOptions.value.length > 0) return; |
| | | try { |
| | | const res = await getDeviceLedger(); |
| | | deviceOptions.value = res?.data || []; |
| | | } catch { |
| | | deviceOptions.value = []; |
| | | } |
| | | }; |
| | | |
| | | const getList = () => { |
| | | console.log(searchForm.value); |
| | | if (loading.value) return; |
| | | loading.value = true; |
| | | |
| | |
| | | |
| | | const canEndProduction = (row) => { |
| | | if (!row?.id) return false; |
| | | if (getPendingQty(row) <= 0) return false; |
| | | if (isEnded(row)) return false; |
| | | return isStarted(row); |
| | | |
| | | return true; |
| | | }; |
| | | |
| | | // 判断是否可以开始报工 |
| | |
| | | // 根据userIds判断是否有用户可以报工 |
| | | const canStartProductionByUserIds = (userId, row) => { |
| | | const team = row?.userIds || ""; |
| | | return team.includes(userId); |
| | | if (!userId) return true; |
| | | if (!team) return true; |
| | | return String(team).includes(String(userId)); |
| | | }; |
| | | |
| | | const handleStartProduction = (row) => { |
| | |
| | | endReportRow.value = row; |
| | | await ensureUserInfo(); |
| | | await ensureUserOptions(); |
| | | await ensureDeviceOptions(); |
| | | |
| | | endReportForm.planQuantity = String(getPendingQty(row)); |
| | | endReportForm.quantity = String(getPendingQty(row)); |
| | | endReportForm.replenishQty = "0"; |
| | | endReportForm.scrapQty = "0"; |
| | | endReportForm.deviceLedgerId = ""; |
| | | endReportForm.deviceName = ""; |
| | | endReportForm.teamList = []; |
| | | teamCheckedIds.value = []; |
| | | |
| | |
| | | endTimeValue.value = Date.now(); |
| | | endReportForm.endTime = formatDateTime(endTimeValue.value); |
| | | |
| | | if (row?.deviceName) { |
| | | const matched = deviceOptions.value.find(d => String(d?.deviceName || "") === String(row.deviceName)); |
| | | if (matched) { |
| | | endReportForm.deviceLedgerId = String(matched.id ?? ""); |
| | | endReportForm.deviceName = String(matched.deviceName ?? ""); |
| | | } |
| | | } |
| | | |
| | | endReportVisible.value = true; |
| | | }; |
| | | |
| | |
| | | endReportForm.auditUserId = String(e?.value ?? ""); |
| | | endReportForm.auditUserName = String(e?.name ?? ""); |
| | | auditPickerVisible.value = false; |
| | | }; |
| | | |
| | | const openDevicePicker = async () => { |
| | | await ensureDeviceOptions(); |
| | | devicePickerVisible.value = true; |
| | | }; |
| | | |
| | | const onDeviceSelect = (e) => { |
| | | endReportForm.deviceLedgerId = String(e?.value ?? ""); |
| | | endReportForm.deviceName = String(e?.name ?? ""); |
| | | devicePickerVisible.value = false; |
| | | }; |
| | | |
| | | const openTeamPicker = async () => { |
| | |
| | | }; |
| | | |
| | | const onStartTimeConfirm = (e) => { |
| | | endReportForm.startTime = formatDateTime(e.value); |
| | | const start = e.value; |
| | | endReportForm.startTime = formatDateTime(start); |
| | | startTimePickerVisible.value = false; |
| | | |
| | | // 👉 自动给结束时间 +1小时 |
| | | const end = start + 60 * 60 * 1000; |
| | | endTimeValue.value = end; |
| | | endReportForm.endTime = formatDateTime(end); |
| | | }; |
| | | |
| | | const onEndTimeConfirm = (e) => { |
| | | endReportForm.endTime = formatDateTime(e.value); |
| | | const end = e.value; |
| | | |
| | | if (end <= startTimeValue.value) { |
| | | showToast("结束时间必须大于开始时间"); |
| | | return; |
| | | } |
| | | |
| | | endReportForm.endTime = formatDateTime(end); |
| | | endTimePickerVisible.value = false; |
| | | }; |
| | | |
| | |
| | | showToast("报废数量必须为大于等于0的整数"); |
| | | return; |
| | | } |
| | | if (!endReportForm.startTime) { |
| | | showToast("请选择开始时间"); |
| | | return; |
| | | } |
| | | |
| | | if (!endReportForm.endTime) { |
| | | showToast("请选择结束时间"); |
| | | return; |
| | | } |
| | | |
| | | const start = new Date(endReportForm.startTime).getTime(); |
| | | const end = new Date(endReportForm.endTime).getTime(); |
| | | |
| | | if (end <= start) { |
| | | showToast("结束时间必须大于开始时间"); |
| | | return; |
| | | } |
| | | |
| | | if (!endReportForm.auditUserId) { |
| | | showToast("请选择审核人"); |
| | | return; |
| | | } |
| | | |
| | | if (!endReportForm.deviceLedgerId) { |
| | | showToast("请选择机台"); |
| | | return; |
| | | } |
| | | |
| | |
| | | userName: endReportForm.userName || userStore.nickName, |
| | | auditUserId: endReportForm.auditUserId, |
| | | auditUserName: endReportForm.auditUserName, |
| | | deviceLedgerId: endReportForm.deviceLedgerId, |
| | | deviceName: endReportForm.deviceName, |
| | | }; |
| | | const res = await addProductMain(submitData); |
| | | if (res?.code === 200) { |
| | | showToast("结束报工成功"); |
| | | showToast("报工成功"); |
| | | closeEndReport(); |
| | | handleQuery(); |
| | | } else { |
| | | showToast(res?.msg || "结束报工失败"); |
| | | showToast(res?.msg || "报工失败"); |
| | | } |
| | | } catch { |
| | | showToast("结束报工失败"); |
| | | showToast("报工失败"); |
| | | } finally { |
| | | uni.hideLoading(); |
| | | } |
| | |
| | | } |
| | | |
| | | .qr-popup { |
| | | width: 620rpx; |
| | | max-width: 88vw; |
| | | max-height: 80vh; |
| | | width: 90vw; |
| | | max-width: 900rpx; |
| | | max-height: 85vh; // 原来 80 → 加大 |
| | | padding: 32rpx 28rpx; |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | |
| | | text-align: center; |
| | | } |
| | | .transfer-records-list { |
| | | max-height: 420rpx; |
| | | max-height: 500rpx; |
| | | overflow: auto; |
| | | } |
| | | .transfer-record-item { |
| | |
| | | border-radius: 16rpx; |
| | | background: #f7f8fa; |
| | | } |
| | | |
| | | .transfer-record-item + .transfer-record-item { |
| | | margin-top: 16rpx; |
| | | } |
| | |
| | | margin-top: 18rpx; |
| | | } |
| | | .transfer-record-cell { |
| | | width: calc(50% - 8rpx); |
| | | min-width: 0; |
| | | width: 100%; // 原来一行两个 → 改成一行一个 |
| | | } |
| | | .transfer-record-cell.is-wide { |
| | | width: 100%; |