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