From c06965849f7f85fac75746845004647b629c47fb Mon Sep 17 00:00:00 2001 From: songshankun <songshankun@foxmail.com> Date: 星期一, 27 十一月 2023 14:42:55 +0800 Subject: [PATCH] feat: 报工列表弹窗/对接上报接口/报工弹窗回显 --- src/stores/plc.ts | 3 src/views/dashboard/components/ReportProductionModal.vue | 7 src/views/dashboard/components/TaskControl.vue | 49 +++++++ src/api/reporting.ts | 19 +++ package-lock.json | 19 +++ src/views/dashboard/index.vue | 14 ++ package.json | 1 src/views/dashboard/components/ReportingRecordModal.vue | 158 ++++++++++++++++++++++++++ src/components.d.ts | 2 src/api/index.ts | 36 ++++++ src/views/dashboard/components/DashboardTitle.vue | 31 ++++ 11 files changed, 327 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d767bd..68479d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@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", @@ -835,6 +836,15 @@ "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": "*" @@ -5500,6 +5510,15 @@ "@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", diff --git a/package.json b/package.json index 9360d7f..cdd567a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@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", diff --git a/src/api/index.ts b/src/api/index.ts index 818c651..b45f15f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -4,6 +4,7 @@ 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 @@ -43,6 +44,7 @@ export interface ProductProgressParams { channel: number + procedureId?: number } /** @@ -187,3 +189,37 @@ 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 + }) +} diff --git a/src/api/reporting.ts b/src/api/reporting.ts new file mode 100644 index 0000000..a13dc3e --- /dev/null +++ b/src/api/reporting.ts @@ -0,0 +1,19 @@ +// 鎶ュ伐璁板綍 +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 +} diff --git a/src/components.d.ts b/src/components.d.ts index ce217b0..be5c27d 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -24,6 +24,8 @@ 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'] diff --git a/src/stores/plc.ts b/src/stores/plc.ts index 88d0122..83ff4cd 100644 --- a/src/stores/plc.ts +++ b/src/stores/plc.ts @@ -27,7 +27,8 @@ } = useRequest( () => getProductProgress({ - channel: taskStore.activeChannel ?? 0 + channel: taskStore.activeChannel ?? 0, + procedureId: taskStore.activeTask?.Procedure.ID ?? undefined } as ProductProgressParams), { manual: true, diff --git a/src/views/dashboard/components/DashboardTitle.vue b/src/views/dashboard/components/DashboardTitle.vue index 446f322..cde7f28 100644 --- a/src/views/dashboard/components/DashboardTitle.vue +++ b/src/views/dashboard/components/DashboardTitle.vue @@ -7,7 +7,7 @@ <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> @@ -27,6 +27,17 @@ </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> @@ -35,9 +46,11 @@ <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' @@ -50,6 +63,8 @@ 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: [] @@ -129,6 +144,18 @@ 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"> @@ -154,7 +181,7 @@ cursor: pointer; } .device-name { - max-width: 340px; + max-width: 220px; font-size: 40px; color: #fff; } diff --git a/src/views/dashboard/components/ReportProductionModal.vue b/src/views/dashboard/components/ReportProductionModal.vue index f48da66..ec61c1b 100644 --- a/src/views/dashboard/components/ReportProductionModal.vue +++ b/src/views/dashboard/components/ReportProductionModal.vue @@ -24,8 +24,9 @@ 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] @@ -72,7 +73,7 @@ } watch(modelData, () => { if (modelData.value) { - inputNumber.value = '' + inputNumber.value = (props.amount ?? '').toString() } }) </script> diff --git a/src/views/dashboard/components/ReportingRecordModal.vue b/src/views/dashboard/components/ReportingRecordModal.vue new file mode 100644 index 0000000..04e06b7 --- /dev/null +++ b/src/views/dashboard/components/ReportingRecordModal.vue @@ -0,0 +1,158 @@ +<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> diff --git a/src/views/dashboard/components/TaskControl.vue b/src/views/dashboard/components/TaskControl.vue index 03c17b4..755a2d9 100644 --- a/src/views/dashboard/components/TaskControl.vue +++ b/src/views/dashboard/components/TaskControl.vue @@ -47,25 +47,29 @@ <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] @@ -124,11 +128,48 @@ 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; diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue index f4cff31..2ffecc5 100644 --- a/src/views/dashboard/index.vue +++ b/src/views/dashboard/index.vue @@ -58,7 +58,7 @@ <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> @@ -96,7 +96,7 @@ 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' @@ -124,6 +124,7 @@ import { updateCraftParams } from '@/api' import { Loading } from '@element-plus/icons-vue' +import { isNumber } from 'lodash-es' defineOptions({ name: 'DashboardView' @@ -235,6 +236,15 @@ 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"> -- Gitblit v1.8.0