新增工单管理界面
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
||||
import { listStation } from '@/api/station';
|
||||
import { usePwoStore } from '@/store';
|
||||
|
||||
interface TableItem {
|
||||
key: string;
|
||||
index: number;
|
||||
operationTitle: string;
|
||||
planQty: number;
|
||||
okQty: number;
|
||||
ngQty: number;
|
||||
status: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const pwoStore = usePwoStore();
|
||||
const tableData = ref<TableItem[]>([]);
|
||||
|
||||
const columns = [
|
||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
||||
{ title: '制程', dataIndex: 'operationTitle', key: 'operationTitle', align: 'center' },
|
||||
{ title: '总数量', dataIndex: 'planQty', key: 'planQty', align: 'center' },
|
||||
{ title: '合格数量', dataIndex: 'okQty', key: 'okQty', align: 'center' },
|
||||
{ title: '报废数量', dataIndex: 'ngQty', key: 'ngQty', align: 'center' },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', align: 'center' },
|
||||
{ title: '操作', key: 'action', align: 'center', width: 120 },
|
||||
];
|
||||
|
||||
const loadingStations = ref(false);
|
||||
const fetchStations = async (traceOrderCode: string) => {
|
||||
try {
|
||||
loadingStations.value = true;
|
||||
const res = await listStation({ traceOrderCode });
|
||||
tableData.value = (res.rows || []).map((item: any, idx: number) => {
|
||||
return {
|
||||
key: String(item.id ?? idx),
|
||||
index: item.seqNo ?? idx + 1,
|
||||
...item,
|
||||
} as TableItem;
|
||||
});
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || error?.message || '查询站点失败');
|
||||
} finally {
|
||||
loadingStations.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (record: TableItem) => {
|
||||
message.success(`提交了序号: ${record.index}`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const traceOrderCode = route.query.traceOrderCode as string;
|
||||
if (traceOrderCode) {
|
||||
fetchStations(traceOrderCode);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.query.t,
|
||||
(val) => {
|
||||
if (val) {
|
||||
fetchStations(route.query.traceOrderCode as string);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function rowClick(record: TableItem) {
|
||||
return {
|
||||
onClick: () => {
|
||||
pwoStore.setCurrentJob(record);
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="table-wrapper">
|
||||
<a-table
|
||||
:dataSource="tableData"
|
||||
:columns="columns as ColumnsType<TableItem>"
|
||||
:pagination="false"
|
||||
:loading="loadingStations"
|
||||
bordered
|
||||
sticky
|
||||
size="middle"
|
||||
rowKey="key"
|
||||
class="custom-table"
|
||||
:customRow="rowClick"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-button type="primary" @click="handleSubmit(record as TableItem)">进入</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.table-wrapper {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.custom-table {
|
||||
:deep(.ant-table-thead > tr > th) {
|
||||
background-color: #e6f7ff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:deep(.ant-table-tbody > tr > td) {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
376
src/views/pwoManage/layout.vue
Normal file
376
src/views/pwoManage/layout.vue
Normal file
@@ -0,0 +1,376 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import Header from '@/components/Header/index.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useAuthStore, usePwoStore } from '@/store';
|
||||
import type { WorkOrderInfo } from './model';
|
||||
import { listLotTraceOrder } from '@/api/pwoManage';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const pwoStore = usePwoStore();
|
||||
|
||||
const workOrderInfo = reactive<WorkOrderInfo>({
|
||||
code: '',
|
||||
batchNo: '',
|
||||
status: '',
|
||||
tarMaterialName: '',
|
||||
tarMaterialCode: '',
|
||||
planQty: undefined,
|
||||
});
|
||||
|
||||
const processInfo: any = storeToRefs(pwoStore).currentJob;
|
||||
|
||||
const loadingPwoInfo = ref(false);
|
||||
const fetchLotTraceOrder = async () => {
|
||||
if (!workOrderInfo.code) return;
|
||||
try {
|
||||
loadingPwoInfo.value = true;
|
||||
const res = await listLotTraceOrder({
|
||||
code: workOrderInfo.code,
|
||||
});
|
||||
if (res.total as number <= 0) {
|
||||
throw new Error('未查询到工单信息,请检查工单编码')
|
||||
};
|
||||
workOrderInfo.status = res.rows[0].status;
|
||||
workOrderInfo.batchNo = res.rows[0].batchNo;
|
||||
workOrderInfo.tarMaterialName = res.rows[0].tarMaterialName;
|
||||
workOrderInfo.tarMaterialCode = res.rows[0].tarMaterialCode;
|
||||
workOrderInfo.planQty = res.rows[0].planQty;
|
||||
router.replace({ query: { ...route.query, traceOrderCode: workOrderInfo.code, t: Date.now() } });
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '获取工单信息失败');
|
||||
} finally {
|
||||
loadingPwoInfo.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 孙组件
|
||||
const infeedRef = ref<any>(null);
|
||||
const collapsed = ref(false);
|
||||
const toggleCollapse = async () => {
|
||||
collapsed.value = !collapsed.value;
|
||||
await nextTick();
|
||||
infeedRef.value?.renderTableHeight();
|
||||
}
|
||||
const handlePwoManage = () => {
|
||||
router.push({ name: 'PwoManage' })
|
||||
}
|
||||
|
||||
const handleWaferAppearance = () => {
|
||||
message.info('点击了 Wafer');
|
||||
};
|
||||
|
||||
const handleDieAppearance = () => {
|
||||
message.info('点击了 Die');
|
||||
};
|
||||
|
||||
const openHoldModal = ref(false);
|
||||
const holdForm = reactive({
|
||||
planCompleteDate: '',
|
||||
});
|
||||
const handleHold = () => {
|
||||
openHoldModal.value = true;
|
||||
};
|
||||
const handleCloseHold = () => {
|
||||
holdForm.planCompleteDate = '';
|
||||
openHoldModal.value = false;
|
||||
};
|
||||
const handleSubmitHold = () => {
|
||||
message.success('Hold 住!')
|
||||
holdForm.planCompleteDate = '';
|
||||
openHoldModal.value = false;
|
||||
};
|
||||
|
||||
const handleInfeed = () => {
|
||||
router.push({ name: 'Infeed' });
|
||||
};
|
||||
|
||||
const handleOutfeed = () => {
|
||||
router.push({ name: 'Outfeed' });
|
||||
};
|
||||
|
||||
const handleCopy = async (text: string | number | undefined) => {
|
||||
if (!text) return;
|
||||
await navigator.clipboard.writeText(String(text));
|
||||
message.success(`已复制: ${ text }`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<Header title="过站工控机" showHome showBack showLogout>
|
||||
<template #right-opts>
|
||||
<a-button @click="toggleCollapse">{{ collapsed ? '展开' : '折叠' }}</a-button>
|
||||
</template>
|
||||
</Header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- Top Section -->
|
||||
<div class="top-section">
|
||||
<a-row :gutter="16" class="full-height-row">
|
||||
<!-- Work Order Info -->
|
||||
<a-col :span="13">
|
||||
<a-spin :spinning="loadingPwoInfo">
|
||||
<a-card title="工单信息" class="info-card" :bordered="false">
|
||||
<a-form :model="workOrderInfo" :colon="false" v-show="!collapsed">
|
||||
<a-row :gutter="36">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="工单批次">
|
||||
<a-input v-model:value="workOrderInfo.batchNo" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(workOrderInfo.batchNo)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="工单状态">
|
||||
<a-input v-model:value="workOrderInfo.status" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(workOrderInfo.status)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="总计数量">
|
||||
<a-input v-model:value="workOrderInfo.planQty" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(workOrderInfo.planQty)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="产品编码">
|
||||
<a-input v-model:value="workOrderInfo.tarMaterialCode" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(workOrderInfo.tarMaterialCode)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="产品名称">
|
||||
<a-input v-model:value="workOrderInfo.tarMaterialName" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(workOrderInfo.tarMaterialName)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="产品规格">
|
||||
<a-input v-model:value="workOrderInfo.planQty" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(workOrderInfo.planQty)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<template #extra>
|
||||
<a-input-search v-model:value="workOrderInfo.code" @search="fetchLotTraceOrder" placeholder="输入工单编码" enter-button />
|
||||
</template>
|
||||
</a-card>
|
||||
</a-spin>
|
||||
</a-col>
|
||||
|
||||
<!-- Process Info -->
|
||||
<a-col :span="8">
|
||||
<a-card title="工序信息" class="info-card" :bordered="false">
|
||||
<a-form :model="processInfo" :colon="false" :label-col="{ span: 4 }" :wrapper-col="{ span: 20, offset: 2 }"
|
||||
v-show="!collapsed">
|
||||
<a-form-item label="工序名称">
|
||||
<a-input v-model:value="processInfo.operationTitle" readonly>
|
||||
<template #suffix>
|
||||
<a-tag color="#f50" v-if="processInfo.status">{{ processInfo.status }}</a-tag>
|
||||
<a-button @click="handleCopy(processInfo.operationTitle)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="作业编码">
|
||||
<a-input v-model:value="processInfo.code" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(processInfo.code)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="设备名称">
|
||||
<a-input v-model:value="processInfo.tarMaterialName" readonly>
|
||||
<template #suffix>
|
||||
<a-button @click="handleCopy(processInfo.tarMaterialName)">
|
||||
<template #icon><i-lucide-copy /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<a-col :span="3" class="action-buttons-col">
|
||||
<div class="action-buttons" v-show="!collapsed">
|
||||
<a-button class="action-btn green-btn" @click="handlePwoManage">工单管理</a-button>
|
||||
<a-button class="action-btn orange-btn" @click="handleWaferAppearance">Wafer 清单</a-button>
|
||||
<a-button class="action-btn blue-btn" @click="handleDieAppearance">Die 清单</a-button>
|
||||
<a-space>
|
||||
<a-button class="action-btn red-btn" @click="handleHold">Hold</a-button>
|
||||
<a-button class="action-btn orange-btn" @click="handleInfeed">进站</a-button>
|
||||
<a-button class="action-btn blue-btn" @click="handleOutfeed">出站</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-card title="操作" class="info-card" :bordered="false" v-show="collapsed">
|
||||
<template #extra>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="handlePwoManage">工单管理</a-menu-item>
|
||||
<a-menu-item key="2" @click="handleWaferAppearance">Wafer 清单</a-menu-item>
|
||||
<a-menu-item key="3" @click="handleDieAppearance">Die 清单</a-menu-item>
|
||||
<a-menu-item key="4" @click="handleHold">Hold</a-menu-item>
|
||||
<a-menu-item key="5" @click="handleInfeed">进站</a-menu-item>
|
||||
<a-menu-item key="6" @click="handleOutfeed">出站</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
更多
|
||||
<i-lucide-chevron-down />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Section -->
|
||||
<main class="bottom-section">
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" ref="infeedRef" />
|
||||
</router-view>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Hold Modal -->
|
||||
<a-modal
|
||||
v-model:open="openHoldModal"
|
||||
title="Hold 操作"
|
||||
@cancel="handleCloseHold"
|
||||
@ok="handleSubmitHold"
|
||||
>
|
||||
<a-form :model="holdForm" :colon="false" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="工单编码">
|
||||
<a-input v-model:value="workOrderInfo.code" readonly />
|
||||
</a-form-item>
|
||||
<a-form-item label="目标产品编码">
|
||||
<a-input v-model:value="workOrderInfo.tarMaterialCode" readonly />
|
||||
</a-form-item>
|
||||
<a-form-item label="目标产品名称">
|
||||
<a-input v-model:value="workOrderInfo.tarMaterialName" readonly />
|
||||
</a-form-item>
|
||||
<a-form-item label="工序名称">
|
||||
<a-input v-model:value="processInfo.operationTitle" readonly />
|
||||
</a-form-item>
|
||||
<a-form-item label="产品规格">
|
||||
<a-input v-model:value="processInfo.code" readonly />
|
||||
</a-form-item>
|
||||
<a-form-item label="计划完成日期">
|
||||
<a-input v-model:value="holdForm.planCompleteDate" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-container {
|
||||
height: 100vh;
|
||||
background-color: #f0f2f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding: 16px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading),
|
||||
:deep(.ant-spin-container) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
|
||||
:deep(.ant-card-head) {
|
||||
min-height: 48px;
|
||||
padding: 0 16px;
|
||||
border-bottom: none;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
padding: 1px 16px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
height: 100%;
|
||||
|
||||
.action-btn {
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
16
src/views/pwoManage/model.d.ts
vendored
Normal file
16
src/views/pwoManage/model.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export interface WorkOrderInfo {
|
||||
code?: string;
|
||||
batchNo?: string;
|
||||
status?: string;
|
||||
tarMaterialName?: string;
|
||||
tarMaterialCode?: string;
|
||||
planQty?: number;
|
||||
}
|
||||
|
||||
export interface ProcessInfo {
|
||||
process?: string;
|
||||
cut?: string;
|
||||
jobCode?: string;
|
||||
status?: string;
|
||||
equipment?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user