mes-vue/src/views/production/componets/AssingWorkTreeTable.vue
shaoleiliu-netizen123 877c907166 提交修改代码
2026-08-17 11:14:42 +08:00

703 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<n-scrollbar x-scrollable>
<n-data-table
v-model:expanded-row-keys="AssingWorkExpandedKeys"
:columns="AssingWorkColumns"
:data="AssingWorkTableData"
:loading="tableLoading"
:row-key="RowKey"
:scroll-x="1700"
:max-height="1000"
size="small"
style="margin-bottom:15px"
@update-expanded-row-keys="handleExpandChange"
/>
</n-scrollbar>
<div class="pagination-container">
<n-button
size="small"
quaternary
:disabled="tableLoading || !AssingWorkTableData.length"
@click="AssingWorkToggleExpandAll"
>
{{ AssingWorkAllExpanded ? '全部收起' : '全部展开' }}
</n-button>
<n-pagination
v-model:page="pagination.page"
v-model:page-size="pagination.pageSize"
:item-count="pagination.itemCount"
:page-sizes="[2,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-modal v-model:show="showQcReport" :title="reportTitle" style="width: 1000px" >
<div>
<SubmitLogInfoPage
v-if="baseInfo.logOrQuality === 'log'"
:assingId="assingId"
:baseInfo="baseInfo"
/>
<QualityInfoPage
v-if="baseInfo.logOrQuality === 'Quality'"
:assingId="assingId"
:baseInfo="baseInfo"
/>
</div>
<template #footer>
<n-space justify="end">
<n-button @click="showQcReport = false">取消</n-button>
</n-space>
</template>
</n-modal>
<!--派工详情弹窗-->
<n-modal v-model:show="showAssingWorkDetail" title="派工详情" style="width: 1000px" >
<n-card>
<n-descriptions :column="2" bordered size="small" label-placement="left" style="margin-bottom: 16px">
<n-descriptions-item label="物料编码">{{ assingWorkDetail.materialCode || '-' }}</n-descriptions-item>
<n-descriptions-item label="物料名称">{{ assingWorkDetail.materialName || '-' }}</n-descriptions-item>
<n-descriptions-item label="工序名称">{{ assingWorkDetail.processName || '-' }}</n-descriptions-item>
<n-descriptions-item label="车间名称">{{ assingWorkDetail.workshopName || '-' }}</n-descriptions-item>
<n-descriptions-item label="生产工段">{{ assingWorkDetail.sectionName || '-' }}</n-descriptions-item>
<n-descriptions-item label="生产设备">{{ assingWorkDetail.deviceName || '-' }}</n-descriptions-item>
<n-descriptions-item label="派工编号">{{ assingWorkDetail.assingCode || '-' }}</n-descriptions-item>
<n-descriptions-item label="派工数量">{{ assingWorkDetail.quantity ?? '-' }}</n-descriptions-item>
<n-descriptions-item label="是否返工">{{ handlerReWork(assingWorkDetail.rework) ?? '-' }}</n-descriptions-item>
<n-descriptions-item label="返工数量">{{ assingWorkDetail.reworkNum ?? '-' }}</n-descriptions-item>
<n-descriptions-item label="派工状态">{{ handlerPatchStatus(assingWorkDetail.assingStatus) ?? '-' }}</n-descriptions-item>
</n-descriptions>
<template #footer>
<n-space justify="end">
<n-button @click="showAssingWorkDetail = false">取消</n-button>
</n-space>
</template>
</n-card>
</n-modal>
</div>
</template>
<script setup lang="ts">
import {
ref,
computed,
h,
reactive,
onMounted,
} from 'vue'
import { mytasks,type AssingWork } from '@/api/production';
import {
NButton, NSpace, NTag, NDataTable, DataTableColumns, NDescriptions, NDescriptionsItem, NPagination
} from 'naive-ui'
import { dictDataApi } from '@/api/org'
import { assingWorkDetailApi,AssingWorkDetail } from '@/api/AssingWorkDetail'
import SubmitLogInfoPage from "@/views/production/componets/SubmitLogInfoPage.vue";
import QualityInfoPage from "@/views/production/componets/QualityInfoPage.vue";
import Button from "naive-ui/es/button/src/Button";
const defaultBaseInfo = ref({
materialName:undefined,
materialCode:undefined,
processName:undefined,
processCode:undefined,
assingCode:undefined,
logOrQuality:undefined
})
const baseInfo = ref({...defaultBaseInfo})
const qcAssingStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
const qcAssingWorkDetailStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
//汇报信息
const tableLoading = ref<Boolean>(false)
const AssingWorkTableData = ref<AssingWork[]>([])
const AssingWorkExpandedKeys = ref<Array<string |number>>([])
const processName = ref<string>()
const assingCode = ref<string>()
const materialCode = ref<string>()
const materialName = ref<string>()
//派工详情弹窗
const showAssingWorkDetail = ref<boolean>(false)
const assingWorkDetail = ref<AssingWork>({})
const AssingWorkColumns :DataTableColumns<AssingWork> = [
{
type:"expand",
expandable:()=>true,
renderExpand:(row) => renderAssingWorkExpand(row)
},
{
align:"center",
title:"派工编号",
key:"assingCode",
width: 170,
render(row:any) {
return h(
'a',
{
class: 'route-code-link',
href: 'javascript:;',
style: {
color: '#2080f0',
textDecoration: 'underline',
cursor: 'pointer',
},
onClick: (e: MouseEvent) => {
viewAssingWorkDetail(row)
},
},
row.assingCode
)
}
},
{
align:'center',
title: '物料编码',
key: 'materialCode',
width: 120,
ellipsis: { tooltip: true },
render(row:any) {
return row.materialCode || '-'
}
},
{
align:'center',
title: '物料名称',
key: 'materialName',
width: 140,
ellipsis: { tooltip: true },
render(row:any) {
return row.materialName || '-'
}
},
{
align:'center',
title: '工序名称',
key: 'processName',
width: 120,
ellipsis: { tooltip: true }
},
/*{
align:'center',
title: '工序编号',
key: 'processCode',
width: 100
}, */
{
align:'center',
title: '是否质检',
key: 'qualityInspection',
width: 90,
render(row) {
var opt = row.qualityInspection
if (opt == 0) {
return h(NTag, { type: 'error', size: 'small' }, { default: () => "否" })
}
if (opt == 1) {
return h(NTag, { type: 'success', size: 'small' }, { default: () => "是" })
}
}
},
{
align:'center',
title: '是否入库',
key: 'storageEntry',
width: 90,
render(row) {
var opt = row.storageEntry
if (opt == 0) {
return h(NTag, { type: 'error', size: 'small' }, { default: () => "否" })
}
if (opt == 1) {
return h(NTag, { type: 'success', size: 'small' }, { default: () => "是" })
}
}
},
{
align:'center',
title: '派工状态',
key: 'assingStatus',
width: 90,
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 })
}
},
// {
// align:'center',
// title: '车间名称',
// key: 'workshopName',
// minWidth: 200
// },
// {
// align:'center',
// title: '工段名称',
// key: 'sectionName',
// minWidth: 200
// },
{
align:'center',
title: '工位/设备',
key: 'deviceName',
width: 110,
ellipsis: { tooltip: true }
},
{
align:'center',
title: '数量',
width: 70,
key: 'quantity'
},
{
align:'center',
title: '完成数量',
key: 'completedQuantity',
width: 80
},
{
align:'center',
title: '完成时间',
key: 'completedTime',
width: 160
},
{
align:'center',
title: '状态',
key: 'status',
width: 80,
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 '-'
}
},
{
align:'center',
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
render(row:any) {
const buttons:any = []
buttons.push(
h(
NButton,
{
size: 'small',
type: 'primary',
ghost:true,
onClick: () => {
handleReportRecords(row)
}
},
{ default: () => '汇报信息' }
)
)
buttons.push(
h(
NButton,
{
size: 'small',
type: 'primary',
ghost:true,
onClick: () => {
handleQualityRecords(row)
}
},
{ default: () => '质检信息' }
)
)
return buttons.length > 0 ? h(NSpace, {justify:'center'}, { default: () => buttons }) : '-'
}
}
]
const showQcReport = ref<boolean>(false)
const reportTitle = ref<string>()
const AssingWorkDetailColums:DataTableColumns<AssingWorkDetail> = [
{
align:"center",
title:"工位/设备",
key:"deviceName",
width:100,
render(row:any) {
var opt = row.deviceName
if(opt) return opt
return '-'
}
},
{
align:"center",
title:"数量",
key:"num",
width:80
},
{
align:"center",
title:"来源",
key:"source",
width:100,
render(row:any) {
var opt = row.source
if(opt == 1) return '设备'
if(opt == 2) return '终端'
if(opt == 3) return '后台'
return '-'
}
},
{
align:'center',
title: '状态',
key: 'status',
width: 100,
render(row:any) {
const val = row.status
const opt = qcAssingWorkDetailStatusOptions.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质检中+3已汇报一4已关闭
},
{
align:"center",
title:"操作人",
key:"userName",
width:100
},
{
align:"center",
title:"创建时间",
key:"createTime",
width:150
},
]
//分页
const pagination = reactive({
page: 1,
pageSize: 10,
itemCount: 0
})
function handlePageChange(page: number) {
pagination.page = page
getlist()
}
function handlePageSizeChange(pageSize: number) {
pagination.pageSize = pageSize
pagination.page = 1
getlist()
}
// //获取列表数据
async function getlist() {
tableLoading.value = true
try {
const rps: any = await mytasks({
page: pagination.page,
pageSize: pagination.pageSize,
processName: processName.value || undefined,
assingCode: assingCode.value || undefined,
materialCode: materialCode.value || undefined,
materialName: materialName.value || undefined,
})
AssingWorkTableData.value = rps?.records ?? []
pagination.itemCount = rps?.total ?? 0
} catch (error) {
console.error(error)
} finally {
tableLoading.value = false
}
}
const loadTableData = async (params: Record<string, any> = {}) => {
processName.value = params.processName || undefined
assingCode.value = params.assingCode || undefined
materialCode.value = params.materialCode || undefined
materialName.value = params.materialName || undefined
pagination.page = 1
AssingWorkExpandedKeys.value = []
await getlist()
}
function RowKey(row:AssingWork) {
return row.id
}
function DetailRowKey(row:any) {
return row.id
}
const AssingWorkAllExpanded = computed(()=>{
const total = AssingWorkTableData.value.length
if(!total) return false
return AssingWorkExpandedKeys.value.length >= total
})
function AssingWorkToggleExpandAll() {
console.log(AssingWorkTableData.value)
if(AssingWorkAllExpanded.value) {
console.log(AssingWorkTableData.value)
AssingWorkExpandedKeys.value = []
}else{
AssingWorkExpandedKeys.value = AssingWorkTableData.value.map(RowKey)
console.log(AssingWorkTableData.value)
}
}
// 缓存key=派工idvalue=明细数组
interface DetailPageState {
list: AssingWorkDetail[]
page: number
pageSize: number
total: number
loading: boolean
}
// key父行id
const expandDetailMap = new Map<number, DetailPageState>()
function renderAssingWorkExpand(row: AssingWork) {
const rowId = row.id
if (!expandDetailMap.has(rowId)) {
expandDetailMap.set(rowId, reactive({
list: [],
page: 1,
pageSize: 10,
total: 0,
loading: true
}))
loadDetail(rowId)
}
const state = expandDetailMap.get(rowId)!
console.log(state)
console.log(state.list)
// 加载中提示
// if (state.loading) {
// return h('div', {style: 'padding:12px'}, '加载中...')
// }
return h('div', { style: 'padding: 0 16px 12px' }, [
h(NDataTable, {
columns: AssingWorkDetailColums,
data: state.list,
rowKey: DetailRowKey,
size: 'small',
bordered: true,
striped: true,
scrollX: 1360
}),
// ✅ 复刻你原来的分页容器 + npagination + prefix插槽
h('div', {
class: 'pagination-container',
style: 'display: flex; justify-content: flex-end; margin-top: 12px'
}, [
h(NPagination, {
page: state.page,
pageSize: state.pageSize,
itemCount: state.total,
onUpdatePage: (p: number) => handleDetailPageChange(rowId, p),
onUpdatePageSize: (sz: number) => handleDetailPageSizeChange(rowId, sz)
}, {
// ✅ #prefix 插槽
prefix: () => h('span', `${state.total}`)
})
])
])
}
// 分页切换事件,点击页码/每页大小
async function handleDetailPageChange(rowId: number,page:number) {
const state = expandDetailMap.get(rowId)
if (!state) return
state.page = page
loadDetail(rowId)
}
async function handleDetailPageSizeChange(rowId: number,pageSize: number) {
const state = expandDetailMap.get(rowId)
if (!state) return
state.pageSize = pageSize
state.page = 1
loadDetail(rowId)
}
function handleExpandChange(openKeys: number[]){
for (const [rowId] of expandDetailMap) {
if (!openKeys.includes(rowId)) {
expandDetailMap.delete(rowId)
}
}
}
const loadDetail = async (rowId: number)=>{
const state = expandDetailMap.get(rowId)
if (!state) return
state.loading = true
try {
const res = await assingWorkDetailApi.selectAssingWorkDetailListByAssingWorkId({
assingWorkId: rowId,
page: state.page,
pageSize: state.pageSize
})
state.list = res.list
state.total = res.total
}finally {
state.loading = false
}
}
// 加载字典选项
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('assing_work_detail_status')
qcAssingWorkDetailStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class:d.listClass }))
}catch {}
}
//汇报信息弹窗
const assingId = ref<number>(null)
function handleReportRecords(row:any) {
showQcReport.value = true
reportTitle.value = "汇报信息"
assingId.value = row.id
baseInfo.value = {...row}
baseInfo.value.logOrQuality = "log"
}
//质检信息弹窗
function handleQualityRecords(row:any) {
showQcReport.value = true
reportTitle.value = "质检信息"
assingId.value = row.id
baseInfo.value = {...row}
baseInfo.value.logOrQuality = "Quality"
}
const viewAssingWorkDetail= ((row:any)=>{
showAssingWorkDetail.value = true
assingWorkDetail.value = row
console.log(assingWorkDetail.value)
})
const handlerReWork = ((row:any)=>{
if (row == 0) return '否'
if (row == 1) return '是'
})
const handlerPatchStatus = ((row:any)=>{
const val = row
console.log(qcAssingStatusOptions.value)
const opt = qcAssingStatusOptions.value.find(
o => o.value === val || String(o.value) === String(val)
)
if (!opt) return val ?? '-'
console.log( opt)
return opt.label
})
onMounted(()=>{
getlist()
loadDictOptions()
})
defineExpose({
loadTableData
})
</script>
<style scoped>
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 12px;
}
.pgClass {
font-weight: bold;
}
</style>