工序查看汇报和订单查看汇报

This commit is contained in:
shaoleiliu-netizen123 2026-07-28 17:36:07 +08:00
parent cb0b868ac3
commit 23edd93c97
5 changed files with 378 additions and 258 deletions

View File

@ -97,6 +97,7 @@ export function exportNccode(params:any) {
return request({
url: `biz/ncCode/export`,
method: 'get',
params
params,
responseType: 'blob'
})
}

View File

@ -90,8 +90,12 @@ export const submitLogApi = {
},
//根据工序Id查询汇报
getProcessReportDetail(processId:number){
return request({url:`/mes/report/submitLogToProcess/${processId}`,method:"get"})
getProcessReportDetail(params?:{page?:number;pageSize?:number;submitStatus?:number;processId?:number;}){
return request({url:`/mes/report/submitLogToProcess`,method:"get",params})
},
//根据订单Id查询汇报
getOrderItemReportDetail(params?:{page?:number;pageSize?:number;submitStatus?:number;orderItemId?:number;}){
return request({url:`/mes/report/allSubmitLogByorderItemId`,method:"get",params})
},
//加载汇报列表

View File

@ -544,9 +544,9 @@
</n-modal>
<n-modal v-model:show="submitModalVisible" preset="card" title="工单汇报详情" style="width: 500px">
<n-modal v-model:show="submitModalVisible" preset="card" title="工单汇报详情" style="width: 2000px">
<OrderReportDetailPage
:orderId="orderId"
:orderItemId="orderItemId"
/>
<template #footer>
<n-space justify="end">
@ -1151,11 +1151,12 @@ async function handleImportUpload({ file }: UploadCustomRequestOptions) {
}
const orderId = ref<number>(null)
const orderItemId = ref<number>(null)
function openReportDetail(order) {
submitModalVisible.value = true
orderId.value = order.id
orderItemId.value = order.orderItemId
}
onMounted(loadData)

View File

@ -1,9 +1,209 @@
<script setup lang="ts">
import {SubmitLog, submitLogApi} from '@/api/submitLog'
import {h, onMounted, reactive, ref} from "vue";
import {NButton, NDataTable, NIcon, NSpace, NTag} from "naive-ui";
import { dictDataApi } from "@/api/org";
import {ArrowDown, ArrowUp, RefreshOutline, SearchOutline} from "@vicons/ionicons5";
const props = defineProps<{
orderItemId: number
}>()
const searchForm = ref({
submitStatus: undefined
})
const submitList = ref<SubmitLog[]>([])
//Id
async function loadReportDetail() {
try {
console.log(props.orderItemId)
const res = await submitLogApi.getOrderItemReportDetail({
page: pagination.page,
pageSize: pagination.pageSize,
submitStatus: searchForm.value.submitStatus,
orderItemId:props.orderItemId
})
submitList.value = res.list
pagination.itemCount = res.total
} catch (error) {
console.error('加载汇报详情失败', error)
}
}
const columns = [
{
title: '提交数量',
key: 'quantity',
width: 120
},
{
title: '提交人',
key: 'userName',
width: 120
},
{
title: '提交时间',
key: 'createTime',
width: 120
},
{
title: '汇报状态',
key: 'submitStatus',
width: 120,
render(row) {
const option = submitStatusOptions.value.find(o => o.value === row.submitStatus)
return h(NTag, { type: option.class, size: 'small' }, { default: () => option.label })
}
}
]
const submitStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
//
async function loadDictOptions() {
try {
const data = await dictDataApi.listByType('submit_status')
submitStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class:d.listClass }))
}catch {}
}
const doShowInner = () => {
if (showSearchInput.value) {
showSearchInput.value = false
}else {
showSearchInput.value = true
}
}
const showSearchInput = ref<Boolean>(false)
const pagination = reactive({
page: 1,
pageSize: 10,
itemCount: 0,
showSizePicker: true,
pageSizes: [10, 20, 50]
})
const handleSearch = () => {
loadReportDetail()
}
const handleReset = () => {
searchForm.value.submitStatus = null
pagination.page = 1
pagination.pageSize = 10
loadReportDetail()
}
//
function handlePageChange(page: number) {
pagination.page = page
loadReportDetail()
}
function handlePageSizeChange(pageSize: number) {
pagination.pageSize = pageSize
pagination.page = 1
loadReportDetail()
}
onMounted(async () => {
loadReportDetail()
loadDictOptions()
})
</script>
<template>
<div>
<n-button @click="doShowInner" v-if="showSearchInput">
<n-icon size="20" :depth="3">
<ArrowDown/>
</n-icon>
展开
</n-button>
<n-card v-if="!showSearchInput">
<n-form inline :model="searchForm" label-placement="left">
<n-grid :x-gap="8" :cols="4">
<n-gi>
<n-form-item label="汇报状态">
<n-select
v-model:value="searchForm.submitStatus"
:options="submitStatusOptions"
placeholder="请选择异常类型"
clearable
style="width: 200px"
/>
</n-form-item>
</n-gi>
<n-gi>
<n-form-item>
<n-space>
<n-button type="primary" @click="handleSearch">
<template #icon>
<n-icon>
<SearchOutline/>
</n-icon>
</template>
搜索
</n-button>
<n-button @click="handleReset">
<template #icon>
<n-icon>
<RefreshOutline/>
</n-icon>
</template>
重置
</n-button>
</n-space>
</n-form-item>
</n-gi>
</n-grid>
</n-form>
<n-button @click="doShowInner" v-if="!showSearchInput" style="float: right;margin: 10px">
<n-icon size="20" :depth="3">
<ArrowUp/>
</n-icon>
闭合
</n-button>
</n-card>
<n-card style="margin: 10px 0">
<n-scrollbar x-scrollable>
<n-data-table
:columns="columns"
:data="submitList"
:bordered="false"
/>
<div class="pagination-container" style="display: flex; justify-content: flex-end; margin-top: 12px">
<n-pagination
v-model:page="pagination.page"
v-model:page-size="pagination.pageSize"
:item-count="pagination.itemCount"
:page-sizes="[10, 20, 50, 100]"
show-size-picker
show-quick-jumper
@update:page="handlePageChange"
@update:page-size="handlePageSizeChange"
>
<template #prefix>
{{ pagination.itemCount }}
</template>
</n-pagination>
</div>
</n-scrollbar>
</n-card>
</div>
</template>
<style scoped>

View File

@ -1,268 +1,117 @@
<script setup lang="ts">
import { submitLogApi } from '@/api/submitLog'
import {h, onMounted, ref} from "vue";
import {DataTableColumns, NButton, NDataTable, NSpace, NTag} from "naive-ui";
import type {AssingWork} from "@/api/production.ts";
import {SubmitLog, submitLogApi} from '@/api/submitLog'
import {h, onMounted, reactive, ref} from "vue";
import {NButton, NDataTable, NIcon, NSpace, NTag} from "naive-ui";
import { dictDataApi } from "@/api/org";
import {AssingWorkDetail} from "@/api/AssingWorkDetail.ts";
import {ArrowDown, ArrowUp, RefreshOutline, SearchOutline} from "@vicons/ionicons5";
const props = defineProps<{
planId: number
}>()
const tableLoading = ref<Boolean>(false)
const AssingWorkTableData = ref<AssingWork[]>([])
const searchForm = ref({
submitStatus: undefined
})
const submitList = ref<SubmitLog[]>([])
//Id
async function loadReportDetail() {
try {
const res = await submitLogApi.getProcessReportDetail(props.planId)
AssingWorkTableData.value = res
const res = await submitLogApi.getProcessReportDetail({
page: pagination.page,
pageSize: pagination.pageSize,
submitStatus: searchForm.value.submitStatus,
processId:props.planId
})
submitList.value = res.list
pagination.itemCount = res.total
} catch (error) {
console.error('加载汇报详情失败', error)
}
}
const qcAssingStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
const columns = [
{
title: '提交数量',
key: 'quantity',
width: 120
},
{
title: '提交人',
key: 'userName',
width: 120
},
{
title: '提交时间',
key: 'createTime',
width: 120
},
{
title: '汇报状态',
key: 'submitStatus',
width: 120,
render(row) {
const option = submitStatusOptions.value.find(o => o.value === row.submitStatus)
return h(NTag, { type: option.class, size: 'small' }, { default: () => option.label })
}
}
]
const submitStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
const AssingWorkExpandedKeys = ref<Array<string |number>>([])
const AssingWorkColumns :DataTableColumns<AssingWork> = [
{
type:"expand",
expandable:()=>true,
renderExpand:(row) => renderAssingWorkExpand(row)
},
{
align:"center",
title:"物料名称",
key:"materialName",
minWidth:150,
render(row:any) {
var opt = row.materialName
if(opt) return opt
return '-'
}
},
{
align:"center",
title:"物料编码",
key:"materialCode",
minWidth:150,
render(row:any) {
var opt = row.materialCode
if(opt) return opt
return '-'
}
},
{
align:"center",
title:"派工编号",
key:"assingCode",
minWidth:150
},
{
align:'center',
title: '工序名称',
key: 'processName',
minWidth: 200
},
{
align:'center',
title: '工序编号',
key: 'processCode',
minWidth: 200
},
{
align:'center',
title: '派工状态',
key: 'assingStatus',
minWidth: 150,
render(row:any) {
const val = row.assingStatus
const opt = qcAssingStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
if (!opt) return val ?? '-'
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
}
//0 1 2+34
},
{
align:'center',
title: '工段名称',
key: 'sectionName',
minWidth: 200
},
{
align:'center',
title: '工位名称',
key: 'deviceName',
minWidth: 200
},
{
align:'center',
title: '数量',
minWidth: 100,
key: 'quantity'
},
{
align:'center',
title: '是否返工',
key: 'rework',
minWidth: 100,
render(row:any) {
const quit = row.rework === 1
return h(NTag, {type: quit ? 'error' : 'success', size: 'small'}, {default: () => (quit ? '是' : '否')})
}
},
{
align:'center',
title: '返工次数',
key: 'reworkNum',
minWidth: 150
},
{
align:'center',
title: '完成数量',
key: 'completedQuantity',
minWidth: 150
},
{
align:'center',
title: '完成时间',
key: 'completedTime',
minWidth: 180
},
{
align:'center',
title: '工序状态',
key: 'status',
minWidth: 100,
render(row:any) {
if(row.status == 0){
return h(NTag, {
type:'success',
size: 'small'
},
{
default: () => '启用'
}
)
}
if(row.status == 1){
return h(NTag, {
type:'warning',
size: 'small'
},
{
default: () => '已暂停'
}
)
}
if(row.status == 2){
return h(NTag, {
type:'warning',
size: 'small'
},
{
default: () => '已改派'
}
)
}
return '-'
}
}
]
function RowKey(row:AssingWork) {
return row.id
}
const expandDataMap = new Map<number, any[]>()
const loadingRowIds = ref<Set<number>>(new Set())
function renderAssingWorkExpand(row: AssingWork) {
// 1.
if (!expandDataMap.has(row.id)) {
expandDataMap.set(row.id, [])
loadingRowIds.value.add(row.id)
//
expandDataMap.set(row.id, row.submitLogList)
loadingRowIds.value.delete(row.id)
}
// 2.
if (loadingRowIds.value.has(row.id)) {
return h('div', { style: 'padding:12px' }, '加载中...')
}
const tableData = expandDataMap.get(row.id)!
return h('div', { style: 'padding: 0 16px 12px' }, [
h(NDataTable, {
columns: AssingWorkDetailColums,
data: tableData,
rowKey: DetailRowKey,
size: 'small',
bordered: true,
striped: true,
scrollX: 600
})
])
}
function DetailRowKey(row:any) {
return row.id
}
const AssingWorkDetailColums:DataTableColumns<AssingWorkDetail> = [
{
align:"center",
title:"数量",
key:"quantity",
width:150
},
{
align:"center",
title:"汇报状态",
key:"submitStatus",
render: (row) => {
const val = row.submitStatus
const opt = submitStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
if (!opt) return val ?? '-'
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
}
},
{ title: '撤回原因', key: 'remark',align: 'center' },
{
align:"center",
title:"操作人",
key:"userName",
width:150
},
{
align:"center",
title:"创建时间",
key:"createTime",
width:150
},
]
//
async function loadDictOptions() {
try {
const data = await dictDataApi.listByType('assing_status')
qcAssingStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class:d.listClass }))
}catch {}
try {
const data = await dictDataApi.listByType('submit_status')
submitStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class:d.listClass }))
}catch {}
}
const doShowInner = () => {
if (showSearchInput.value) {
showSearchInput.value = false
}else {
showSearchInput.value = true
}
}
const showSearchInput = ref<Boolean>(false)
const pagination = reactive({
page: 1,
pageSize: 10,
itemCount: 0,
showSizePicker: true,
pageSizes: [10, 20, 50]
})
const handleSearch = () => {
loadReportDetail()
}
const handleReset = () => {
searchForm.value.submitStatus = null
pagination.page = 1
pagination.pageSize = 10
loadReportDetail()
}
//
function handlePageChange(page: number) {
pagination.page = page
loadReportDetail()
}
function handlePageSizeChange(pageSize: number) {
pagination.pageSize = pageSize
pagination.page = 1
loadReportDetail()
}
onMounted(async () => {
loadReportDetail()
loadDictOptions()
@ -271,19 +120,84 @@ onMounted(async () => {
<template>
<div>
<n-card>
<n-button @click="doShowInner" v-if="showSearchInput">
<n-icon size="20" :depth="3">
<ArrowDown/>
</n-icon>
展开
</n-button>
<n-card v-if="!showSearchInput">
<n-form inline :model="searchForm" label-placement="left">
<n-grid :x-gap="8" :cols="4">
<n-gi>
<n-form-item label="汇报状态">
<n-select
v-model:value="searchForm.submitStatus"
:options="submitStatusOptions"
placeholder="请选择异常类型"
clearable
style="width: 200px"
/>
</n-form-item>
</n-gi>
<n-gi>
<n-form-item>
<n-space>
<n-button type="primary" @click="handleSearch">
<template #icon>
<n-icon>
<SearchOutline/>
</n-icon>
</template>
搜索
</n-button>
<n-button @click="handleReset">
<template #icon>
<n-icon>
<RefreshOutline/>
</n-icon>
</template>
重置
</n-button>
</n-space>
</n-form-item>
</n-gi>
</n-grid>
</n-form>
<n-button @click="doShowInner" v-if="!showSearchInput" style="float: right;margin: 10px">
<n-icon size="20" :depth="3">
<ArrowUp/>
</n-icon>
闭合
</n-button>
</n-card>
<n-card style="margin: 10px 0">
<n-scrollbar x-scrollable>
<n-data-table
v-model:expanded-row-keys="AssingWorkExpandedKeys"
:columns="AssingWorkColumns"
:data="AssingWorkTableData"
:loading="tableLoading"
:scroll-x="600"
:max-height="1000"
size="small"
style="margin-bottom:15px"
:row-key="RowKey"
:columns="columns"
:data="submitList"
:bordered="false"
/>
<div class="pagination-container" style="display: flex; justify-content: flex-end; margin-top: 12px">
<n-pagination
v-model:page="pagination.page"
v-model:page-size="pagination.pageSize"
:item-count="pagination.itemCount"
:page-sizes="[10, 20, 50, 100]"
show-size-picker
show-quick-jumper
@update:page="handlePageChange"
@update:page-size="handlePageSizeChange"
>
<template #prefix>
{{ pagination.itemCount }}
</template>
</n-pagination>
</div>
</n-scrollbar>
</n-card>