feat: 报工列表弹窗/对接上报接口/报工弹窗回显
| | |
| | | "@iconify-json/bx": "^1.1.7", |
| | | "@iconify-json/material-symbols-light": "^1.1.0", |
| | | "@iconify-json/mdi": "^1.1.55", |
| | | "@iconify-json/vaadin": "^1.1.7", |
| | | "@rushstack/eslint-patch": "^1.3.3", |
| | | "@tsconfig/node18": "^18.2.2", |
| | | "@types/node": "^18.18.6", |
| | |
| | | "version": "1.1.55", |
| | | "resolved": "https://registry.npmmirror.com/@iconify-json/mdi/-/mdi-1.1.55.tgz", |
| | | "integrity": "sha512-ycnFub+EQx+3D/aDCg6iC7sjexOUa5GzxUNIZFFl0Pq7aDxbmhIludoyYnguEO3REyWf9FcOOmvVcQkdtwKHTw==", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "@iconify/types": "*" |
| | | } |
| | | }, |
| | | "node_modules/@iconify-json/vaadin": { |
| | | "version": "1.1.7", |
| | | "resolved": "https://registry.npmmirror.com/@iconify-json/vaadin/-/vaadin-1.1.7.tgz", |
| | | "integrity": "sha512-gtczBFm5EBEkA3iXny4RR9MynZxIWaApnvwEbF/PV7JeSwDYRb8UJZmstNCSRkVXMFcnD+bdW9LJ0CPyjrq1Tw==", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "@iconify/types": "*" |
| | |
| | | "@iconify/types": "*" |
| | | } |
| | | }, |
| | | "@iconify-json/vaadin": { |
| | | "version": "1.1.7", |
| | | "resolved": "https://registry.npmmirror.com/@iconify-json/vaadin/-/vaadin-1.1.7.tgz", |
| | | "integrity": "sha512-gtczBFm5EBEkA3iXny4RR9MynZxIWaApnvwEbF/PV7JeSwDYRb8UJZmstNCSRkVXMFcnD+bdW9LJ0CPyjrq1Tw==", |
| | | "dev": true, |
| | | "requires": { |
| | | "@iconify/types": "*" |
| | | } |
| | | }, |
| | | "@iconify/types": { |
| | | "version": "2.0.0", |
| | | "resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", |
| | |
| | | "@iconify-json/bx": "^1.1.7", |
| | | "@iconify-json/material-symbols-light": "^1.1.0", |
| | | "@iconify-json/mdi": "^1.1.55", |
| | | "@iconify-json/vaadin": "^1.1.7", |
| | | "@rushstack/eslint-patch": "^1.3.3", |
| | | "@tsconfig/node18": "^18.2.2", |
| | | "@types/node": "^18.18.6", |
| | |
| | | import type { Devices } from './device' |
| | | import type { CraftModel } from './craftModel' |
| | | import type { Problem } from './problem' |
| | | import type { ReportingRecord } from './reporting' |
| | | |
| | | export interface BaseResponse<T = any> { |
| | | code: number |
| | |
| | | |
| | | export interface ProductProgressParams { |
| | | channel: number |
| | | procedureId?: number |
| | | } |
| | | |
| | | /** |
| | |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export interface ReportingRecordListParams { |
| | | page?: number |
| | | pageSize?: number |
| | | procedureId: number |
| | | } |
| | | |
| | | /** |
| | | * 获取报工记录列表 |
| | | */ |
| | | export function apiGetReportingRecordList(params: ReportingRecordListParams) { |
| | | return request<ListResponse<ReportingRecord[]>>({ |
| | | url: '/v1/reportWork/list', |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | | |
| | | export interface ReportWorkParams { |
| | | procedureId: number |
| | | reportAmount: number |
| | | workerID: string |
| | | } |
| | | |
| | | /** |
| | | * 报工 |
| | | */ |
| | | export function apiReportWork(params: ReportWorkParams) { |
| | | return request<BaseResponse>({ |
| | | url: '/v1/reportWork/report', |
| | | method: 'post', |
| | | data: params |
| | | }) |
| | | } |
New file |
| | |
| | | // 报工记录 |
| | | export interface ReportingRecord { |
| | | ID: number |
| | | CreatedAt: string |
| | | UpdatedAt: string |
| | | DeletedAt: string |
| | | proceduresId: number |
| | | workOrderId: string |
| | | deviceId: string |
| | | deviceName: string |
| | | procedureId: string |
| | | channel: number |
| | | startTime: number |
| | | endTime: number |
| | | reportAmount: number |
| | | finishAmount: number |
| | | workerID: string |
| | | workerName: string |
| | | } |
| | |
| | | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] |
| | | ElStep: typeof import('element-plus/es')['ElStep'] |
| | | ElSteps: typeof import('element-plus/es')['ElSteps'] |
| | | ElTable: typeof import('element-plus/es')['ElTable'] |
| | | ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] |
| | | ElTabPane: typeof import('element-plus/es')['ElTabPane'] |
| | | ElTabs: typeof import('element-plus/es')['ElTabs'] |
| | | ElText: typeof import('element-plus/es')['ElText'] |
| | |
| | | } = useRequest( |
| | | () => |
| | | getProductProgress({ |
| | | channel: taskStore.activeChannel ?? 0 |
| | | channel: taskStore.activeChannel ?? 0, |
| | | procedureId: taskStore.activeTask?.Procedure.ID ?? undefined |
| | | } as ProductProgressParams), |
| | | { |
| | | manual: true, |
| | |
| | | <el-text truncated class="device-name">{{ currentDeviceName }}</el-text> |
| | | </template> |
| | | </el-popover> |
| | | <el-icon size="32" color="#0db7f5" style="margin-left: 20px; cursor: pointer" @click="openDevicesModal"> |
| | | <el-icon size="32" color="#0db7f5" style="margin-left: 4px; cursor: pointer" @click="openDevicesModal"> |
| | | <IconSlider></IconSlider> |
| | | </el-icon> |
| | | </div> |
| | |
| | | </el-icon> |
| | | </div> |
| | | |
| | | <div class="reporting-record"> |
| | | <el-icon |
| | | size="26" |
| | | :color="taskStore.activeTask ? '#0db7f5' : '#c0c0c0'" |
| | | :style="{ 'margin-right': '10px', cursor: taskStore.activeTask ? 'pointer' : 'not-allowed' }" |
| | | @click="openReportingRecord" |
| | | > |
| | | <IconRecords></IconRecords> |
| | | </el-icon> |
| | | </div> |
| | | |
| | | <div class="params-config" @click="openConfigModal"> |
| | | <el-icon size="28"><Setting /></el-icon> |
| | | </div> |
| | |
| | | <DeviceCheckList v-model="showDevicesModal" @should-reload="emits('shouldReload')"></DeviceCheckList> |
| | | <TroubleTrackerModal v-model="showProblemsModal" :problems="problemList"></TroubleTrackerModal> |
| | | <DeliverParamsConfigModal v-model="showConfigModal"></DeliverParamsConfigModal> |
| | | <ReportingRecordModal v-model="showReportingRecordModal"></ReportingRecordModal> |
| | | </template> |
| | | <script setup lang="ts"> |
| | | import AlertLightIcon from '@/components/icons/AlertLightIcon.vue' |
| | | import IconRecords from '~icons/vaadin/records' |
| | | import { computed, onUnmounted, ref } from 'vue' |
| | | import { useDevicesStore } from '@/stores/devices' |
| | | import TroubleTrackerModal from '@/views/dashboard/components/TroubleTrackerModal.vue' |
| | |
| | | import { PROBLEMS_POLLING_DURATION } from '@/common/constants' |
| | | import { Setting } from '@element-plus/icons-vue' |
| | | import DeliverParamsConfigModal from '@/views/dashboard/components/DeliverParamsConfigModal.vue' |
| | | import ReportingRecordModal from '@/views/dashboard/components/ReportingRecordModal.vue' |
| | | import { useTasksStore } from '@/stores/tasks' |
| | | |
| | | const emits = defineEmits<{ |
| | | shouldReload: [] |
| | |
| | | onUnmounted(() => { |
| | | cancelProblemsPolling() |
| | | }) |
| | | |
| | | const taskStore = useTasksStore() |
| | | |
| | | // 是否显示报工记录 |
| | | const showReportingRecordModal = ref(false) |
| | | function openReportingRecord() { |
| | | if (!taskStore.activeTask) { |
| | | ElMessage.error('请先选择任务') |
| | | return |
| | | } |
| | | showReportingRecordModal.value = true |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | cursor: pointer; |
| | | } |
| | | .device-name { |
| | | max-width: 340px; |
| | | max-width: 220px; |
| | | font-size: 40px; |
| | | color: #fff; |
| | | } |
| | |
| | | import { useVModel } from '@vueuse/core' |
| | | import BigButton from './BigButton.vue' |
| | | import { ref, watch } from 'vue' |
| | | const props = withDefaults(defineProps<{ modelValue: boolean }>(), { |
| | | modelValue: false |
| | | const props = withDefaults(defineProps<{ modelValue: boolean; amount?: number }>(), { |
| | | modelValue: false, |
| | | amount: 0 |
| | | }) |
| | | const emit = defineEmits<{ |
| | | 'update:modelValue': [show: boolean] |
| | |
| | | } |
| | | watch(modelData, () => { |
| | | if (modelData.value) { |
| | | inputNumber.value = '' |
| | | inputNumber.value = (props.amount ?? '').toString() |
| | | } |
| | | }) |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div class="base-modal"> |
| | | <el-dialog v-model="modelData" :close-on-click-modal="false" :show-close="false" width="80%"> |
| | | <template #header> |
| | | <div class="modal-title"> |
| | | <div class="modal-title-text">报工记录</div> |
| | | <div class="modal-title-close" @click="closeModal"> |
| | | <el-icon :size="22" color="#fff"><CloseBold /></el-icon> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div class="table-content"> |
| | | <el-table class="table" :data="reportingRecordList" border style="width: 100%" :scrollbar-always-on="true"> |
| | | <el-table-column type="index" label="序号" width="56" align="center" :resizable="false"></el-table-column> |
| | | <el-table-column prop="deviceId" label="报工来源" align="center" :resizable="false"> |
| | | <template #default="scope"> |
| | | {{ scope?.row?.workerName ?? '' }}/{{ scope?.row?.deviceName ?? '' }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- TODO: 条码字段还没加 --> |
| | | <el-table-column prop="xxx" label="条码" align="center" :resizable="false">条码</el-table-column> |
| | | <el-table-column prop="reportAmount" label="报工数量" align="center" :resizable="false" /> |
| | | <el-table-column prop="finishAmount" label="完成数量" align="center" :resizable="false" /> |
| | | <el-table-column prop="startTime" label="开始时间" align="center" :resizable="false"> |
| | | <template #default="scope"> |
| | | {{ formatDate(scope.row.startTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="endTime" label="结束时间" align="center" :resizable="false"> |
| | | <template #default="scope"> |
| | | {{ formatDate(scope.row.endTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- TODO: 工时字段还没加--> |
| | | <el-table-column prop="xxx" label="工时" align="center" :resizable="false" /> |
| | | </el-table> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | <script setup lang="ts"> |
| | | import { useDateFormat, useVModel } from '@vueuse/core' |
| | | import { CloseBold } from '@element-plus/icons-vue' |
| | | import { ref, watch } from 'vue' |
| | | import { apiGetReportingRecordList } from '@/api' |
| | | import type { ReportingRecord } from '@/api/reporting' |
| | | import { useTasksStore } from '@/stores/tasks' |
| | | |
| | | export interface BaseModalProps { |
| | | /** 是否展示模态框 */ |
| | | modelValue: boolean |
| | | } |
| | | const props = withDefaults(defineProps<BaseModalProps>(), { |
| | | modelValue: false |
| | | }) |
| | | const emit = defineEmits<{ |
| | | 'update:modelValue': [show: boolean] |
| | | close: [] |
| | | }>() |
| | | const modelData = useVModel(props, 'modelValue', emit) |
| | | function closeModal() { |
| | | emit('update:modelValue', false) |
| | | emit('close') |
| | | } |
| | | |
| | | const taskStore = useTasksStore() |
| | | |
| | | // 报工记录列表 |
| | | const reportingRecordList = ref<ReportingRecord[]>([]) |
| | | function getReportingList() { |
| | | const procedureId = taskStore.activeTask?.Procedure.ID |
| | | if (!procedureId) { |
| | | return |
| | | } |
| | | apiGetReportingRecordList({ |
| | | procedureId: procedureId |
| | | }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | reportingRecordList.value = res?.data ?? [] |
| | | } else { |
| | | reportingRecordList.value = [] |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error(err) |
| | | reportingRecordList.value = [] |
| | | }) |
| | | } |
| | | // 打开弹窗时获取报工记录 |
| | | watch(modelData, (show) => { |
| | | if (show) { |
| | | getReportingList() |
| | | } |
| | | }) |
| | | |
| | | /** |
| | | * 格式化时间戳 |
| | | * @param timestamp 后端返的10位时间戳 |
| | | */ |
| | | function formatDate(timestamp?: number) { |
| | | if (!timestamp) { |
| | | return '--' |
| | | } |
| | | const time = useDateFormat(timestamp * 1000, 'YYYY-MM-DD HH:mm:ss', { locales: 'zh-cn' }) |
| | | return time.value |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | :deep(.el-dialog) { |
| | | background-color: #1d3081; |
| | | } |
| | | |
| | | .modal-title { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | &-text { |
| | | padding-left: 12px; |
| | | font-size: 26px; |
| | | font-weight: 600; |
| | | } |
| | | &-close { |
| | | cursor: pointer; |
| | | height: 36px; |
| | | width: 36px; |
| | | border-radius: 50%; |
| | | position: absolute; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | top: -16px; |
| | | right: -30px; |
| | | } |
| | | } |
| | | $bgc: #1d3081; |
| | | .table-content { |
| | | :deep(.el-table .el-table__cell) { |
| | | background-color: $bgc; |
| | | color: #dcdfec; |
| | | } |
| | | :deep(.el-table__body tr) { |
| | | background-color: $bgc; |
| | | color: #dcdfec; |
| | | } |
| | | :deep(.el-table__body tr:hover > td) { |
| | | background-color: $bgc; |
| | | } |
| | | :deep(.el-scrollbar__wrap) { |
| | | background-color: $bgc; |
| | | } |
| | | height: calc(70vh - 80px); |
| | | } |
| | | .table { |
| | | height: 100%; |
| | | } |
| | | </style> |
| | |
| | | <TaskControlModal v-model="showTaskControlModal" :task="task" @produce-start="onProduceStart"></TaskControlModal> |
| | | <ReportProductionModal |
| | | v-model="showReportModal" |
| | | :amount="plcInfo?.finishNumber ?? 0" |
| | | @close="showReportModal = false" |
| | | @submit="showReportModal = false" |
| | | @submit="onReportProduction" |
| | | ></ReportProductionModal> |
| | | </template> |
| | | <script setup lang="ts"> |
| | | import type { Task } from '@/api/task' |
| | | import type { Task, Worker } from '@/api/task' |
| | | import { ref, toRefs } from 'vue' |
| | | import BigButton from '@/views/dashboard/components/BigButton.vue' |
| | | import { useDateFormat } from '@vueuse/core' |
| | | import TaskControlModal from '@/views/dashboard/components/TaskControlModal.vue' |
| | | import { CircleCloseFilled } from '@element-plus/icons-vue' |
| | | import { finishTask } from '@/api' |
| | | import { apiReportWork, finishTask } from '@/api' |
| | | import { ElMessage } from 'element-plus' |
| | | import ReportProductionModal from '@/views/dashboard/components/ReportProductionModal.vue' |
| | | import { usePLCStore } from '@/stores/plc' |
| | | import { storeToRefs } from 'pinia' |
| | | |
| | | const props = defineProps<{ |
| | | task?: Task |
| | | workers: Worker[] |
| | | }>() |
| | | const { task } = toRefs(props) |
| | | const { task, workers } = toRefs(props) |
| | | |
| | | const emit = defineEmits<{ |
| | | shouldReload: [task: Task] |
| | |
| | | const time = useDateFormat(timestamp * 1000, 'YYYY-MM-DD HH:mm:ss', { locales: 'zh-cn' }) |
| | | return time.value |
| | | } |
| | | |
| | | const plcStore = usePLCStore() |
| | | const { plcInfo } = storeToRefs(plcStore) |
| | | // 报工 |
| | | const showReportModal = ref(false) |
| | | function openReportModal() { |
| | | showReportModal.value = true |
| | | } |
| | | |
| | | /** |
| | | * 上报加工数 |
| | | * @param amount 加工数 |
| | | */ |
| | | function onReportProduction(amount: number) { |
| | | if (!task?.value) { |
| | | return |
| | | } |
| | | apiReportWork({ |
| | | procedureId: task.value?.Procedure.ID, |
| | | reportAmount: amount, |
| | | workerID: workers.value[0].workerId |
| | | }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | ElMessage({ |
| | | message: '报工成功', |
| | | type: 'success', |
| | | duration: 2000 |
| | | }) |
| | | showReportModal.value = false |
| | | } else { |
| | | ElMessage({ |
| | | message: '报工失败', |
| | | type: 'error', |
| | | duration: 3000 |
| | | }) |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error(err) |
| | | }) |
| | | } |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | $title-text-color: #9599af; |
| | |
| | | <template #middleBlock3> |
| | | <SubTitle>任务详情</SubTitle> |
| | | <div class="task-detail"> |
| | | <TaskControl :task="activeTask" @should-reload="reloadChannel"></TaskControl> |
| | | <TaskControl :task="activeTask" :workers="currentWorkers" @should-reload="reloadChannel"></TaskControl> |
| | | </div> |
| | | <ColorInfo :type="1"></ColorInfo> |
| | | <ColorInfo :type="2"></ColorInfo> |
| | |
| | | import { computed, ref } from 'vue' |
| | | import ChannelCollapse from '@/views/dashboard/components/ChannelCollapse.vue' |
| | | |
| | | import type { Worker, Order, Task, Material } from '@/api/task' |
| | | import type { Task, Material } from '@/api/task' |
| | | import type { CraftModel } from '@/api/craftModel' |
| | | import PersonInfo from '@/views/dashboard/components/PersonInfo.vue' |
| | | import ProcessInfo from '@/views/dashboard/components/ProcessInfo.vue' |
| | |
| | | |
| | | import { updateCraftParams } from '@/api' |
| | | import { Loading } from '@element-plus/icons-vue' |
| | | import { isNumber } from 'lodash-es' |
| | | |
| | | defineOptions({ |
| | | name: 'DashboardView' |
| | |
| | | currentMaterialInfo.value = material |
| | | showMaterialDetail.value = true |
| | | } |
| | | |
| | | const currentWorkers = computed(() => { |
| | | const channel = activeTask.value?.Channel |
| | | if (isNumber(channel)) { |
| | | return channels.value[channel].workers ?? [] |
| | | } else { |
| | | return [] |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |