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