668 lines
17 KiB
Vue
668 lines
17 KiB
Vue
<template>
|
||
<div>
|
||
<div class="toolbar">
|
||
<n-space>
|
||
<n-button
|
||
size="small"
|
||
:disabled="tableLoading || !AssingWorkTableData.length"
|
||
@click="AssingWorkToggleExpandAll"
|
||
>
|
||
{{ AssingWorkAllExpanded ? '全部收起' : '全部展开' }}
|
||
</n-button>
|
||
</n-space>
|
||
</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="1800"
|
||
:max-height="1000"
|
||
size="small"
|
||
style="margin-bottom:15px"
|
||
@update-expanded-row-keys="handleExpandChange"
|
||
/>
|
||
</n-scrollbar>
|
||
|
||
<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="[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 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",
|
||
minWidth:150,
|
||
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: 'processName',
|
||
minWidth: 200
|
||
},
|
||
{
|
||
align:'center',
|
||
title: '工序编号',
|
||
key: 'processCode',
|
||
minWidth: 200
|
||
},
|
||
{
|
||
align:'center',
|
||
title: '是否质检',
|
||
key: 'qualityInspection',
|
||
minWidth: 200,
|
||
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',
|
||
minWidth: 200,
|
||
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',
|
||
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 })
|
||
|
||
}
|
||
},
|
||
// {
|
||
// align:'center',
|
||
// title: '车间名称',
|
||
// key: 'workshopName',
|
||
// minWidth: 200
|
||
// },
|
||
// {
|
||
// 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: '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 '-'
|
||
}
|
||
},
|
||
{
|
||
align:'center',
|
||
title: '操作',
|
||
key: 'action',
|
||
width: 250,
|
||
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()
|
||
}
|
||
|
||
// //获取列表数据
|
||
function getlist() {
|
||
try{
|
||
mytasks({
|
||
page:pagination.page,
|
||
pageSize:pagination.pageSize,
|
||
processName:processName.value
|
||
}).then((rps:any) => {
|
||
AssingWorkTableData.value = rps.records
|
||
pagination.itemCount = rps.total
|
||
})
|
||
}catch(error) {
|
||
console.log(error);
|
||
|
||
}
|
||
}
|
||
|
||
const loadTableData = async(params:Record<string,any>) => {
|
||
processName.value = params.processName
|
||
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=派工id,value=明细数组
|
||
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
|
||
}),
|
||
// ✅ 复刻你原来的分页容器 + n‑pagination + 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>
|
||
.toolbar {
|
||
margin-bottom: 12px;
|
||
}
|
||
.pgClass {
|
||
font-weight: bold;
|
||
}
|
||
</style> |