| | |
| | | <template> |
| | | <view class="work-order"> |
| | | <!-- 通用页面头部 --> |
| | | <PageHeader title="生产工单" @back="goBack" /> |
| | | |
| | | |
| | | <!-- 搜索区域 --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请输入工单编号搜索" |
| | | v-model="searchForm.workOrderNo" |
| | | @confirm="handleQuery" |
| | | clearable |
| | | /> |
| | | <up-input class="search-text" |
| | | v-model="data.searchForm.workOrderNo" |
| | | placeholder="工单编号" |
| | | @confirm="handleQuery" |
| | | clearable /> |
| | | </view> |
| | | |
| | | <view class="search-input"> |
| | | <up-input class="search-text" |
| | | v-model="data.searchForm.npsNo" |
| | | placeholder="生产订单号" |
| | | @confirm="handleQuery" |
| | | clearable /> |
| | | </view> |
| | | <view class="filter-button" @click="handleQuery"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | <view class="switch-row"> |
| | | <text class="switch-label">仅看我的</text> |
| | | <up-switch v-model="filterMine" @change="handleQuery" size="18" /> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | <!-- 工单列表 --> |
| | | <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore"> |
| | | <view v-for="(item, index) in tableData" :key="item.id || index" class="ledger-item"> |
| | |
| | | <text class="item-tag tag-type">{{ item.workOrderType }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | <up-divider></up-divider> |
| | | |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">生产订单号</text> |
| | | <text class="detail-value">{{ item.npsNo || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产品名称</text> |
| | | <text class="detail-value">{{ item.productName }}</text> |
| | |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">工序名称</text> |
| | | <text class="detail-value">{{ item.processName }}</text> |
| | | <text class="detail-value">{{ item.operationName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">需求/完成数量</text> |
| | | <text class="detail-value">{{ item.planQuantity }} / {{ item.completeQuantity }} {{ item.unit }}</text> |
| | | </view> |
| | | |
| | | |
| | | <view class="progress-section"> |
| | | <text class="detail-label">完成进度</text> |
| | | <view class="progress-bar"> |
| | | <up-line-progress |
| | | :percentage="toProgressPercentage(item.completionStatus)" |
| | | <up-line-progress |
| | | :percentage="toProgressPercentage(item.completionStatus)" |
| | | activeColor="#2979ff" |
| | | :showText="true" |
| | | ></up-line-progress> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | <view class="detail-row"> |
| | | <text class="detail-label">计划开始</text> |
| | | <text class="detail-value">{{ item.planStartTime }}</text> |
| | |
| | | <text class="detail-label">计划结束</text> |
| | | <text class="detail-value">{{ item.planEndTime }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">实际开始</text> |
| | | <text class="detail-value">{{ item.actualStartTime || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">实际结束</text> |
| | | <text class="detail-value">{{ item.actualEndTime || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="item-actions"> |
| | | <up-button v-if="showStartReport(item)" |
| | | class="action-btn" |
| | | size="small" |
| | | type="primary" |
| | | @click="handleStartWork(item)">开始报工</up-button> |
| | | <up-button v-if="showEndReport(item)" |
| | | class="action-btn" |
| | | size="small" |
| | | type="success" |
| | | @click="goReport(item)">结束报工</up-button> |
| | | </view> |
| | | </view> |
| | | <up-loadmore :status="loadStatus" /> |
| | | </scroll-view> |
| | | |
| | | |
| | | <view v-else-if="!loading" class="no-data"> |
| | | <up-empty mode="data" text="暂无工单数据"></up-empty> |
| | | </view> |
| | | |
| | | <!-- 流转卡弹窗 --> |
| | | <up-popup :show="transferCardVisible" mode="center" @close="transferCardVisible = false" round="10"> |
| | | <view class="qr-popup"> |
| | | <text class="qr-title">工单流转卡二维码</text> |
| | | <view class="qr-box"> |
| | | <geek-qrcode |
| | | v-if="transferCardRowData" |
| | | :val="String(transferCardRowData.id)" |
| | | :size="200" |
| | | /> |
| | | </view> |
| | | <text class="qr-info" v-if="transferCardRowData">{{ transferCardRowData.workOrderNo }}</text> |
| | | <up-button text="关闭" @click="transferCardVisible = false" style="margin-top: 20px;"></up-button> |
| | | </view> |
| | | </up-popup> |
| | | |
| | | <!-- 附件组件 --> |
| | | <FilesDia ref="workOrderFilesRef" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { ref, reactive } from "vue"; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js"; |
| | | import { productWorkOrderPage, startWork } from "@/api/productionManagement/workOrder.js"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import FilesDia from "./components/filesDia.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | |
| | | const loading = ref(false); |
| | | const tableData = ref([]); |
| | | const loadStatus = ref('loadmore'); |
| | | const transferCardVisible = ref(false); |
| | | const transferCardRowData = ref(null); |
| | | const workOrderFilesRef = ref(null); |
| | | const filterMine = ref(false); |
| | | |
| | | const page = reactive({ |
| | | current: 1, |
| | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | workOrderNo: "", |
| | | npsNo: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | const isCompleted = row => { |
| | | const status = Number(row?.completionStatus); |
| | | return Number.isFinite(status) && status >= 100; |
| | | }; |
| | | |
| | | const canOperate = row => !row.endOrder && !isCompleted(row); |
| | | |
| | | const showStartReport = row => canOperate(row) && !row.actualStartTime; |
| | | |
| | | const showEndReport = row => canOperate(row) && !!row.actualStartTime; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | |
| | | const getList = () => { |
| | | if (loading.value) return; |
| | | loading.value = true; |
| | | |
| | | const params = { ...searchForm.value, ...page }; |
| | | |
| | | |
| | | const params = { ...data.searchForm, ...page }; |
| | | if (filterMine.value) { |
| | | params.filterMine = true; |
| | | } |
| | | |
| | | productWorkOrderPage(params).then((res) => { |
| | | loading.value = false; |
| | | const records = res.data.records || []; |
| | | tableData.value = page.current === 1 ? records : [...tableData.value, ...records]; |
| | | page.total = res.data.total; |
| | | |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | |
| | | return Math.round(n); |
| | | }; |
| | | |
| | | const showTransferCard = (row) => { |
| | | transferCardRowData.value = row; |
| | | transferCardVisible.value = true; |
| | | const handleStartWork = (row) => { |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: "确认开始报工?", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | startWork({ |
| | | productionOperationTaskId: row.id, |
| | | userId: userStore.id, |
| | | }) |
| | | .then(() => { |
| | | uni.showToast({ title: "开始报工成功" }); |
| | | handleQuery(); |
| | | }) |
| | | .catch(() => { |
| | | uni.showToast({ title: "开始报工失败", icon: "error" }); |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | const openWorkOrderFiles = (row) => { |
| | | workOrderFilesRef.value?.openDialog(row); |
| | | const goReport = (row) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/productionManagement/productionReport/index?orderRow=${encodeURIComponent( |
| | | JSON.stringify(row) |
| | | )}`, |
| | | }); |
| | | }; |
| | | |
| | | onShow(() => { |
| | |
| | | gap: 10px; |
| | | padding: 12px 0; |
| | | border-top: 1px solid #f5f5f5; |
| | | |
| | | :deep(.up-button) { |
| | | |
| | | .action-btn { |
| | | margin: 0; |
| | | width: auto; |
| | | } |
| | | } |
| | | |
| | | .qr-popup { |
| | | padding: 30px; |
| | | background-color: #fff; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .qr-title { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .qr-box { |
| | | padding: 20px; |
| | | background-color: #fff; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); |
| | | border-radius: 8px; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .qr-info { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | } |
| | | |