|
|
@@ -1,52 +1,52 @@
|
|
|
<template>
|
|
|
<AidopDemoShell :title="pageTitle" subtitle="S0 / Manufacturing / 工序流转卡">
|
|
|
<el-form :inline="true" :model="query" class="mb12" @submit.prevent>
|
|
|
- <el-form-item label="关键字">
|
|
|
- <el-input v-model="query.keyword" clearable placeholder="工单号 / 物料编码 / 物料描述" style="width: 220px" />
|
|
|
- </el-form-item>
|
|
|
<el-form-item label="工单号">
|
|
|
- <el-input v-model="query.workOrd" clearable placeholder="工单号模糊" style="width: 160px" />
|
|
|
+ <el-input v-model="query.workOrderNo" clearable placeholder="工单号模糊" style="width: 160px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="流转卡号">
|
|
|
+ <el-input v-model="query.flowCardNo" clearable placeholder="流转卡号模糊" style="width: 160px" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="物料编码">
|
|
|
<el-input v-model="query.itemNum" clearable placeholder="物料编码模糊" style="width: 160px" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="标签颜色">
|
|
|
- <el-select v-model="query.labelColor" clearable placeholder="全部" style="width: 120px">
|
|
|
- <el-option label="LZK" value="LZK" />
|
|
|
+ <el-form-item label="启用">
|
|
|
+ <el-select v-model="query.isActive" clearable placeholder="全部" style="width: 110px">
|
|
|
+ <el-option label="是" :value="true" />
|
|
|
+ <el-option label="否" :value="false" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="状态">
|
|
|
- <el-input v-model="query.status" clearable placeholder="状态码" style="width: 100px" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="创建时间">
|
|
|
- <el-date-picker
|
|
|
- v-model="query.createTimeRange"
|
|
|
- type="datetimerange"
|
|
|
- value-format="YYYY-MM-DDTHH:mm:ss"
|
|
|
- range-separator="-"
|
|
|
- start-placeholder="开始"
|
|
|
- end-placeholder="结束"
|
|
|
- style="width: 360px"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button type="primary" @click="loadList">查询</el-button>
|
|
|
<el-button @click="resetQuery">重置</el-button>
|
|
|
+ <el-button type="success" @click="openCreate">新增</el-button>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
|
|
|
<el-table :data="rows" v-loading="loading" border stripe style="width: 100%" max-height="calc(100vh - 260px)">
|
|
|
- <el-table-column prop="id" label="记录ID" width="100" show-overflow-tooltip />
|
|
|
- <el-table-column prop="workOrd" label="工单号" width="140" show-overflow-tooltip />
|
|
|
- <el-table-column prop="op" label="工序" width="80" align="right" />
|
|
|
- <el-table-column prop="itemNum" label="物料编码" width="140" show-overflow-tooltip />
|
|
|
- <el-table-column prop="descr" label="物料描述" min-width="180" show-overflow-tooltip />
|
|
|
- <el-table-column prop="labelFormat" label="标签格式" width="110" show-overflow-tooltip />
|
|
|
- <el-table-column prop="labelColor" label="标签颜色" width="90" />
|
|
|
- <el-table-column label="状态" width="110" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">{{ row.statusDesc || row.status || '' }}</template>
|
|
|
+ <el-table-column prop="workOrderNo" label="工单" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="workOrderBatchNo" label="工单批号" width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="flowCardNo" label="流转卡号" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="nature" label="性质" width="100" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="itemNum" label="物料编码" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="itemName" label="物料名称" min-width="150" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="drawingNo" label="图号" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="bomVersion" label="BOM版本" width="100" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="routingCode" label="工艺路线" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column label="启用" width="72" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="row.isActive ? 'success' : 'info'" size="small">{{ row.isActive ? '是' : '否' }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200" fixed="right" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button link type="primary" @click="openEdit(row)">编辑</el-button>
|
|
|
+ <el-button link :type="row.isActive ? 'warning' : 'success'" @click="toggleActive(row)">
|
|
|
+ {{ row.isActive ? '禁用' : '启用' }}
|
|
|
+ </el-button>
|
|
|
+ <el-button link type="danger" @click="onDelete(row)">删除</el-button>
|
|
|
+ </template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="createTime" label="创建时间" width="170" />
|
|
|
</el-table>
|
|
|
|
|
|
<div class="pager">
|
|
|
@@ -60,50 +60,187 @@
|
|
|
@size-change="loadList"
|
|
|
/>
|
|
|
</div>
|
|
|
+
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="720px" destroy-on-close @closed="resetForm">
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12"><el-form-item label="工单" prop="workOrderNo"><el-input v-model="form.workOrderNo" placeholder="工单号" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="工单批号"><el-input v-model="form.workOrderBatchNo" placeholder="工单批号" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="流转卡号" prop="flowCardNo"><el-input v-model="form.flowCardNo" placeholder="同工厂内唯一" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="性质"><el-input v-model="form.nature" placeholder="性质" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="物料编码" prop="itemNum">
|
|
|
+ <el-select
|
|
|
+ v-model="form.itemNum"
|
|
|
+ filterable
|
|
|
+ remote
|
|
|
+ reserve-keyword
|
|
|
+ :remote-method="remoteSearchMaterials"
|
|
|
+ :loading="materialLoading"
|
|
|
+ placeholder="输入物料编码/名称搜索"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="onMaterialChange"
|
|
|
+ >
|
|
|
+ <el-option v-for="m in materialOptions" :key="m.itemNum" :label="`${m.itemNum} ${m.descr ?? ''}`" :value="m.itemNum" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="物料名称"><el-input v-model="form.itemName" placeholder="选物料自动带出,可改" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="图号"><el-input v-model="form.drawingNo" placeholder="选物料自动带出,可改" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="BOM版本"><el-input v-model="form.bomVersion" placeholder="选物料自动带出,可改" /></el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="工艺路线">
|
|
|
+ <el-select
|
|
|
+ v-if="!routingIsManual"
|
|
|
+ v-model="form.routingCode"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="按物料关联路线选择"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option v-for="r in routingOptions" :key="r.routeCode" :label="r.routeName ? `${r.routeCode} ${r.routeName}` : r.routeCode" :value="r.routeCode" />
|
|
|
+ </el-select>
|
|
|
+ <el-input v-else v-model="form.routingCode" placeholder="该物料暂无关联路线,可手工填写" />
|
|
|
+ </el-form-item></el-col>
|
|
|
+ <el-col :span="12"><el-form-item label="启用"><el-switch v-model="form.isActive" /></el-form-item></el-col>
|
|
|
+ <el-col :span="24"><el-form-item label="备注"><el-input v-model="form.remarks" type="textarea" :rows="2" /></el-form-item></el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</AidopDemoShell>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts" name="aidopS0MfgProcessFlowCard">
|
|
|
import { computed, onMounted, reactive, ref } from 'vue';
|
|
|
import { useRoute } from 'vue-router';
|
|
|
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
|
|
|
import AidopDemoShell from '../../components/AidopDemoShell.vue';
|
|
|
import {
|
|
|
+ loadOrgList,
|
|
|
+ searchMaterialsForMfg,
|
|
|
s0MfgProcessFlowCardsApi,
|
|
|
- type S0MfgFlowCardPrintRecordRow,
|
|
|
+ type OrgOption,
|
|
|
+ type S0ProcessFlowCardRow,
|
|
|
+ type S0ProcessFlowCardUpsert,
|
|
|
+ type S0RoutingOption,
|
|
|
} from '../api/s0ManufacturingApi';
|
|
|
+import {
|
|
|
+ applyDefaultOrgScopeForCreate,
|
|
|
+ normalizeOrgScopeForSubmit,
|
|
|
+ preserveOrgScopeForEdit,
|
|
|
+} from '../composables/useS0MfgOrgScope';
|
|
|
|
|
|
const route = useRoute();
|
|
|
const pageTitle = computed(() => (route.meta?.title as string) || '工序流转卡');
|
|
|
|
|
|
+interface MaterialOption {
|
|
|
+ itemNum: string;
|
|
|
+ descr?: string | null;
|
|
|
+ drawing?: string | null;
|
|
|
+ bomVersion?: string | null;
|
|
|
+}
|
|
|
+
|
|
|
const query = reactive({
|
|
|
- keyword: '',
|
|
|
- workOrd: '',
|
|
|
+ workOrderNo: '',
|
|
|
+ flowCardNo: '',
|
|
|
itemNum: '',
|
|
|
- labelColor: '',
|
|
|
- status: '',
|
|
|
- createTimeRange: [] as string[],
|
|
|
+ isActive: undefined as boolean | undefined,
|
|
|
page: 1,
|
|
|
pageSize: 20,
|
|
|
});
|
|
|
-
|
|
|
const loading = ref(false);
|
|
|
-const rows = ref<S0MfgFlowCardPrintRecordRow[]>([]);
|
|
|
+const rows = ref<S0ProcessFlowCardRow[]>([]);
|
|
|
const total = ref(0);
|
|
|
|
|
|
+const dialogVisible = ref(false);
|
|
|
+const dialogTitle = ref('新增');
|
|
|
+const editingId = ref<number | null>(null);
|
|
|
+const saving = ref(false);
|
|
|
+const formRef = ref<FormInstance>();
|
|
|
+
|
|
|
+const companyOptions = ref<OrgOption[]>([]);
|
|
|
+const factoryOptions = ref<OrgOption[]>([]);
|
|
|
+const materialOptions = ref<MaterialOption[]>([]);
|
|
|
+const materialLoading = ref(false);
|
|
|
+const routingOptions = ref<S0RoutingOption[]>([]);
|
|
|
+const routingIsManual = computed(() => routingOptions.value.length === 0);
|
|
|
+
|
|
|
+const form = reactive<S0ProcessFlowCardUpsert>({
|
|
|
+ companyRefId: undefined,
|
|
|
+ factoryRefId: undefined,
|
|
|
+ domainCode: '',
|
|
|
+ workOrderNo: '',
|
|
|
+ workOrderBatchNo: '',
|
|
|
+ flowCardNo: '',
|
|
|
+ nature: '',
|
|
|
+ itemNum: '',
|
|
|
+ itemName: '',
|
|
|
+ drawingNo: '',
|
|
|
+ bomVersion: '',
|
|
|
+ routingCode: '',
|
|
|
+ remarks: '',
|
|
|
+ isActive: true,
|
|
|
+ createUser: '',
|
|
|
+ updateUser: '',
|
|
|
+});
|
|
|
+
|
|
|
+const rules: FormRules = {
|
|
|
+ workOrderNo: [{ required: true, message: '请填写工单', trigger: 'blur' }],
|
|
|
+ flowCardNo: [{ required: true, message: '请填写流转卡号', trigger: 'blur' }],
|
|
|
+ itemNum: [{ required: true, message: '请选择物料编码', trigger: 'change' }],
|
|
|
+};
|
|
|
+
|
|
|
+async function remoteSearchMaterials(keyword: string) {
|
|
|
+ if (!keyword) return;
|
|
|
+ materialLoading.value = true;
|
|
|
+ try {
|
|
|
+ const data = await searchMaterialsForMfg({ keyword, page: 1, pageSize: 50 });
|
|
|
+ materialOptions.value = (data.list as any[]).map((m) => ({
|
|
|
+ itemNum: m.itemNum,
|
|
|
+ descr: m.descr,
|
|
|
+ drawing: m.drawing,
|
|
|
+ bomVersion: m.bomVersion,
|
|
|
+ }));
|
|
|
+ } catch {
|
|
|
+ materialOptions.value = [];
|
|
|
+ } finally {
|
|
|
+ materialLoading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 用户重新选择物料时才覆盖 名称/图号/BOM版本,并重置工艺路线(旧路线属于旧物料)。
|
|
|
+function onMaterialChange(itemNum: string) {
|
|
|
+ const m = materialOptions.value.find((x) => x.itemNum === itemNum);
|
|
|
+ if (m) {
|
|
|
+ form.itemName = m.descr ?? '';
|
|
|
+ form.drawingNo = m.drawing ?? '';
|
|
|
+ form.bomVersion = m.bomVersion ?? '';
|
|
|
+ }
|
|
|
+ void loadRoutingOptions(itemNum, true);
|
|
|
+}
|
|
|
+
|
|
|
+async function loadRoutingOptions(itemNum: string | undefined, resetSelected: boolean) {
|
|
|
+ if (resetSelected) form.routingCode = '';
|
|
|
+ routingOptions.value = [];
|
|
|
+ if (!itemNum) return;
|
|
|
+ try {
|
|
|
+ routingOptions.value = await s0MfgProcessFlowCardsApi.routingOptions(itemNum);
|
|
|
+ } catch {
|
|
|
+ routingOptions.value = [];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
async function loadList() {
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
- const [from, to] = query.createTimeRange || [];
|
|
|
const data = await s0MfgProcessFlowCardsApi.list({
|
|
|
page: query.page,
|
|
|
pageSize: query.pageSize,
|
|
|
- keyword: query.keyword || undefined,
|
|
|
- workOrd: query.workOrd || undefined,
|
|
|
+ workOrderNo: query.workOrderNo || undefined,
|
|
|
+ flowCardNo: query.flowCardNo || undefined,
|
|
|
itemNum: query.itemNum || undefined,
|
|
|
- labelColor: query.labelColor || undefined,
|
|
|
- status: query.status || undefined,
|
|
|
- createTimeFrom: from || undefined,
|
|
|
- createTimeTo: to || undefined,
|
|
|
+ isActive: query.isActive,
|
|
|
});
|
|
|
rows.value = data.list;
|
|
|
total.value = data.total;
|
|
|
@@ -116,18 +253,109 @@ async function loadList() {
|
|
|
}
|
|
|
|
|
|
function resetQuery() {
|
|
|
- query.keyword = '';
|
|
|
- query.workOrd = '';
|
|
|
- query.itemNum = '';
|
|
|
- query.labelColor = '';
|
|
|
- query.status = '';
|
|
|
- query.createTimeRange = [];
|
|
|
- query.page = 1;
|
|
|
+ Object.assign(query, { workOrderNo: '', flowCardNo: '', itemNum: '', isActive: undefined, page: 1 });
|
|
|
void loadList();
|
|
|
}
|
|
|
|
|
|
-onMounted(() => {
|
|
|
- void loadList();
|
|
|
+function resetForm() {
|
|
|
+ editingId.value = null;
|
|
|
+ Object.assign(form, {
|
|
|
+ companyRefId: undefined, factoryRefId: undefined, domainCode: '',
|
|
|
+ workOrderNo: '', workOrderBatchNo: '', flowCardNo: '', nature: '',
|
|
|
+ itemNum: '', itemName: '', drawingNo: '', bomVersion: '', routingCode: '',
|
|
|
+ remarks: '', isActive: true, createUser: '', updateUser: '',
|
|
|
+ });
|
|
|
+ materialOptions.value = [];
|
|
|
+ routingOptions.value = [];
|
|
|
+ formRef.value?.clearValidate();
|
|
|
+}
|
|
|
+
|
|
|
+function openCreate() {
|
|
|
+ resetForm();
|
|
|
+ // 新增:后端会 stamp 默认组织,前端同步补默认便于校验/透传。
|
|
|
+ applyDefaultOrgScopeForCreate(form, companyOptions.value, factoryOptions.value);
|
|
|
+ dialogTitle.value = '新增工序流转卡';
|
|
|
+ dialogVisible.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+function openEdit(row: S0ProcessFlowCardRow) {
|
|
|
+ resetForm();
|
|
|
+ editingId.value = row.id;
|
|
|
+ dialogTitle.value = `编辑:${row.flowCardNo}`;
|
|
|
+ Object.assign(form, {
|
|
|
+ companyRefId: row.companyRefId, factoryRefId: row.factoryRefId, domainCode: row.domainCode ?? '',
|
|
|
+ workOrderNo: row.workOrderNo, workOrderBatchNo: row.workOrderBatchNo ?? '', flowCardNo: row.flowCardNo,
|
|
|
+ nature: row.nature ?? '', itemNum: row.itemNum, itemName: row.itemName ?? '', drawingNo: row.drawingNo ?? '',
|
|
|
+ bomVersion: row.bomVersion ?? '', routingCode: row.routingCode ?? '', remarks: row.remarks ?? '',
|
|
|
+ isActive: row.isActive, createUser: row.createUser ?? '', updateUser: row.updateUser ?? '',
|
|
|
+ });
|
|
|
+ // 编辑:保留接口返回的原归属(legacy 不转雪花)。
|
|
|
+ preserveOrgScopeForEdit(form, row);
|
|
|
+ // 预置物料下拉当前值以便显示;加载工艺路线选项但不重置已保存路线(不覆盖历史值)。
|
|
|
+ if (row.itemNum) {
|
|
|
+ materialOptions.value = [{ itemNum: row.itemNum, descr: row.itemName, drawing: row.drawingNo, bomVersion: row.bomVersion }];
|
|
|
+ void loadRoutingOptions(row.itemNum, false);
|
|
|
+ }
|
|
|
+ dialogVisible.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+async function submitForm() {
|
|
|
+ try {
|
|
|
+ await formRef.value?.validate();
|
|
|
+ } catch {
|
|
|
+ // 校验未通过:表单已就地提示,不继续提交。
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ normalizeOrgScopeForSubmit(form, editingId.value ? 'edit' : 'create', companyOptions.value, factoryOptions.value);
|
|
|
+ saving.value = true;
|
|
|
+ try {
|
|
|
+ const payload: S0ProcessFlowCardUpsert = { ...form };
|
|
|
+ if (editingId.value) {
|
|
|
+ await s0MfgProcessFlowCardsApi.update(editingId.value, payload);
|
|
|
+ ElMessage.success('已保存');
|
|
|
+ } else {
|
|
|
+ await s0MfgProcessFlowCardsApi.create(payload);
|
|
|
+ ElMessage.success('已创建');
|
|
|
+ }
|
|
|
+ dialogVisible.value = false;
|
|
|
+ await loadList();
|
|
|
+ } catch {
|
|
|
+ // 保存失败(如流转卡号重复 409):错误消息由全局 axios 拦截器提示,弹窗保持打开供修改。
|
|
|
+ } finally {
|
|
|
+ saving.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onDelete(row: S0ProcessFlowCardRow) {
|
|
|
+ ElMessageBox.confirm(`确定删除流转卡「${row.flowCardNo}」?`, '确认', { type: 'warning' })
|
|
|
+ .then(async () => {
|
|
|
+ await s0MfgProcessFlowCardsApi.delete(row.id);
|
|
|
+ ElMessage.success('已删除');
|
|
|
+ await loadList();
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+}
|
|
|
+
|
|
|
+function toggleActive(row: S0ProcessFlowCardRow) {
|
|
|
+ const next = !row.isActive;
|
|
|
+ ElMessageBox.confirm(`确定${next ? '启用' : '禁用'}流转卡「${row.flowCardNo}」?`, '确认', { type: 'warning' })
|
|
|
+ .then(async () => {
|
|
|
+ await s0MfgProcessFlowCardsApi.toggleEnabled(row.id, { isEnabled: next });
|
|
|
+ ElMessage.success('操作成功');
|
|
|
+ await loadList();
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+}
|
|
|
+
|
|
|
+async function loadOptions() {
|
|
|
+ const [companies, factories] = await Promise.all([loadOrgList('201'), loadOrgList('501')]);
|
|
|
+ companyOptions.value = companies;
|
|
|
+ factoryOptions.value = factories;
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await loadOptions();
|
|
|
+ await loadList();
|
|
|
});
|
|
|
</script>
|
|
|
|