| | |
| | | import type { MesMdWorkstationApi } from '#/api/mes/md/workstation'; |
| | | import type { PendingTask, WorkbenchTabItem, WorkbenchTabKey } from './types'; |
| | | |
| | | import { computed, ref, watch } from 'vue'; |
| | | import { computed, onMounted, ref, watch } from 'vue'; |
| | | |
| | | import { confirm } from '@vben/common-ui'; |
| | | import { MesProWorkRecordTypeEnum } from '@vben/constants'; |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | |
| | | import { Button } from 'ant-design-vue'; |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | clockInWorkRecord, |
| | | clockOutWorkRecord, |
| | | getMyWorkRecord, |
| | | } from '#/api/mes/pro/workrecord'; |
| | | import { getPendingFeedbackPage } from '#/api/mes/pro/task'; |
| | | import { router } from '#/router'; |
| | | import { useAccess } from '@vben/access'; |
| | | |
| | | import TaskListPanel from './components/TaskListPanel.vue'; |
| | | import WorkstationPanel from './components/WorkstationPanel.vue'; |
| | |
| | | // --- Tab 配置 --- |
| | | const tabItems: WorkbenchTabItem[] = [ |
| | | { key: 'detail', label: '工单详情', icon: 'lucide:file-text' }, |
| | | { key: 'feedback', label: '报工', icon: 'lucide:edit-3' }, |
| | | { key: 'history', label: '报工记录', icon: 'lucide:history' }, |
| | | { key: 'feedback', label: '报工', icon: 'lucide:edit-3', auth: ['mes:pro-feedback:create'] }, |
| | | { key: 'history', label: '报工记录', icon: 'lucide:history' ,auth: ['mes:pro-feedback:query']}, |
| | | { key: 'record-param', label: '生产记录参数', icon: 'lucide:file-cog' }, |
| | | { key: 'product-issue', label: '领料', icon: 'lucide:clipboard-list' }, |
| | | { key: 'return-issue', label: '退料', icon: 'lucide:rotate-ccw' }, |
| | | ]; |
| | | |
| | | const { hasAccessByCodes } = useAccess(); |
| | | |
| | | const availableTabs = computed(() => { |
| | | return tabItems.filter((tab) => { |
| | | if (!tab.auth || tab.auth.length === 0) return true; |
| | | return hasAccessByCodes(tab.auth); |
| | | }); |
| | | }); |
| | | |
| | | const activeTab = ref<WorkbenchTabKey>('detail'); |
| | | const activeTabLabel = computed( |
| | | () => tabItems.find((t) => t.key === activeTab.value)?.label ?? '', |
| | | () => availableTabs.value.find((t) => t.key === activeTab.value)?.label ?? '', |
| | | ); |
| | | |
| | | // 当可用 tab 变化时,如果当前选中的 tab 不可用,则自动选择第一个可用的 tab |
| | | watch( |
| | | availableTabs, |
| | | (tabs) => { |
| | | if (tabs.length > 0 && !tabs.find((t) => t.key === activeTab.value)) { |
| | | activeTab.value = tabs[0]!.key; |
| | | } |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | // --- 工作站 --- |
| | |
| | | function handleWorkstationSelect(ws: MesMdWorkstationApi.Workstation) { |
| | | workstation.value = ws; |
| | | } |
| | | |
| | | // --- 上工/下工 --- |
| | | const isClockIn = ref(false); |
| | | |
| | | async function loadClockStatus() { |
| | | const record = await getMyWorkRecord(); |
| | | isClockIn.value = record?.type === MesProWorkRecordTypeEnum.CLOCK_IN; |
| | | } |
| | | |
| | | /** 上工:默认使用当前选中的工作站,弹窗确认 */ |
| | | async function handleClockIn() { |
| | | if (!workstationId.value) return; |
| | | try { |
| | | await confirm( |
| | | `确认上工到当前工作站「${workstation.value?.code} - ${workstation.value?.name}」?`, |
| | | ); |
| | | } catch { |
| | | return; |
| | | } |
| | | await clockInWorkRecord(workstationId.value); |
| | | message.success('上工成功'); |
| | | isClockIn.value = true; |
| | | } |
| | | |
| | | /** 下工 */ |
| | | async function handleClockOut() { |
| | | try { |
| | | await confirm('确认下工当前工作站?'); |
| | | } catch { |
| | | return; |
| | | } |
| | | await clockOutWorkRecord(); |
| | | message.success('下工成功'); |
| | | isClockIn.value = false; |
| | | } |
| | | |
| | | onMounted(loadClockStatus); |
| | | |
| | | // --- 任务列表 --- |
| | | const taskList = ref<PendingTask[]>([]); |
| | |
| | | |
| | | <nav class="workbench-nav"> |
| | | <button |
| | | v-for="tab in tabItems" |
| | | v-for="tab in availableTabs" |
| | | :key="tab.key" |
| | | type="button" |
| | | class="workbench-nav__item" |
| | |
| | | <!-- 工作站选择面板 --> |
| | | <WorkstationPanel |
| | | :workstation="workstation" |
| | | :is-clock-in="isClockIn" |
| | | @change="openWorkstationSelect" |
| | | @clock-in="handleClockIn" |
| | | @clock-out="handleClockOut" |
| | | /> |
| | | |
| | | <!-- 工单列表 --> |
| | |
| | | :list="taskList" |
| | | :selected-id="selectedTask?.id" |
| | | :loading="taskLoading" |
| | | :process-name="workstation?.processName" |
| | | @select="handleSelectTask" |
| | | @refresh="loadTaskList" |
| | | /> |
| | | </aside> |
| | | |
| | |
| | | <main class="workbench-main"> |
| | | <div class="workbench-main__head"> |
| | | <span class="workbench-main__tab-title">{{ activeTabLabel }}</span> |
| | | <span v-if="selectedTask?.workOrderCode" class="workbench-main__context"> |
| | | 工单 {{ selectedTask.workOrderCode }} |
| | | <span v-if="selectedTask?.code" class="workbench-main__context"> |
| | | 工单 {{ selectedTask.code }} |
| | | </span> |
| | | </div> |
| | | <div class="workbench-main__body"> |
| | |
| | | /> |
| | | <RecordParamTab |
| | | v-else-if="activeTab === 'record-param'" |
| | | :work-order-id="selectedTask?.workOrderId" |
| | | :task-id="selectedTask?.id" |
| | | :work-order-id="selectedTask?.id" |
| | | :workstation-id="workstationId" |
| | | /> |
| | | <ProductIssueTab |
| | | v-else-if="activeTab === 'product-issue'" |
| | | :task-id="selectedTask?.id" |
| | | :task-code="selectedTask?.code" |
| | | :task-name="selectedTask?.name" |
| | | :work-order-id="selectedTask?.workOrderId" |
| | | :workstation-id="workstationId" |
| | | :workstation-name="workstationName" |
| | | /> |
| | | <ReturnIssueTab |
| | | v-else-if="activeTab === 'return-issue'" |
| | | :task-id="selectedTask?.id" |
| | | :task-code="selectedTask?.code" |
| | | :task-name="selectedTask?.name" |
| | | :work-order-id="selectedTask?.workOrderId" |
| | | :workstation-id="workstationId" |
| | | :workstation-name="workstationName" |
| | | /> |