feat(mes):优化 review - 工序定义(pro_process)、工艺路线(pro_route)
parent
4949ed1c07
commit
adb57ed364
|
|
@ -40,7 +40,6 @@ export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||||
{
|
{
|
||||||
type: 'default',
|
type: 'default',
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
// TODO @AI:需要 try catch 么?感觉直接去掉也可以?
|
|
||||||
try {
|
try {
|
||||||
const code = await generateAutoCode(
|
const code = await generateAutoCode(
|
||||||
MesAutoCodeRuleCode.PRO_PROCESS_CODE,
|
MesAutoCodeRuleCode.PRO_PROCESS_CODE,
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,11 @@ import { useContentGridColumns } from '../data';
|
||||||
import ContentForm from './content-form.vue';
|
import ContentForm from './content-form.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
formMode: 'create' | 'detail' | 'update';
|
||||||
processId: number;
|
processId: number;
|
||||||
readonly?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const isEditable = ref(props.formMode !== 'detail'); // 是否可编辑
|
||||||
const list = ref<MesProProcessContentApi.ProcessContent[]>([]);
|
const list = ref<MesProProcessContentApi.ProcessContent[]>([]);
|
||||||
|
|
||||||
const [ContentFormModal, contentFormModalApi] = useVbenModal({
|
const [ContentFormModal, contentFormModalApi] = useVbenModal({
|
||||||
|
|
@ -60,11 +61,7 @@ async function getList() {
|
||||||
|
|
||||||
/** 新增工序步骤 */
|
/** 新增工序步骤 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
// TODO @AI:是不是可以直接 max 然后 || 0;
|
const maxSort = Math.max(0, ...list.value.map((item) => item.sort || 0));
|
||||||
const maxSort =
|
|
||||||
list.value.length > 0
|
|
||||||
? Math.max(...list.value.map((item) => item.sort || 0))
|
|
||||||
: 0;
|
|
||||||
contentFormModalApi
|
contentFormModalApi
|
||||||
.setData({ maxSort, processId: props.processId })
|
.setData({ maxSort, processId: props.processId })
|
||||||
.open();
|
.open();
|
||||||
|
|
@ -95,7 +92,7 @@ watch(
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ContentFormModal @success="getList" />
|
<ContentFormModal @success="getList" />
|
||||||
<div v-if="!readonly" class="mb-3 flex items-center justify-start">
|
<div v-if="isEditable" class="mb-3 flex items-center justify-start">
|
||||||
<TableAction
|
<TableAction
|
||||||
:actions="[
|
:actions="[
|
||||||
{
|
{
|
||||||
|
|
@ -113,14 +110,14 @@ watch(
|
||||||
{
|
{
|
||||||
label: $t('common.edit'),
|
label: $t('common.edit'),
|
||||||
type: 'link',
|
type: 'link',
|
||||||
ifShow: () => !readonly,
|
ifShow: () => isEditable,
|
||||||
onClick: handleEdit.bind(null, row),
|
onClick: handleEdit.bind(null, row),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $t('common.delete'),
|
label: $t('common.delete'),
|
||||||
type: 'link',
|
type: 'link',
|
||||||
danger: true,
|
danger: true,
|
||||||
ifShow: () => !readonly,
|
ifShow: () => isEditable,
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: $t('ui.actionMessage.deleteConfirm', ['工序步骤']),
|
title: $t('ui.actionMessage.deleteConfirm', ['工序步骤']),
|
||||||
confirm: handleDelete.bind(null, row),
|
confirm: handleDelete.bind(null, row),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type FormMode = 'create' | 'detail' | 'update';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formMode = ref<FormMode>('create'); // 表单模式
|
const formMode = ref<FormMode>('create'); // 表单模式
|
||||||
const formData = ref<MesProProcessApi.Process>(); // 当前编辑/查看的工序数据
|
const formData = ref<MesProProcessApi.Process>();
|
||||||
|
|
||||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
|
|
@ -110,7 +110,7 @@ const processId = computed(() => formData.value?.id);
|
||||||
<template v-if="processId">
|
<template v-if="processId">
|
||||||
<Divider class="!my-3" orientation="left">操作步骤</Divider>
|
<Divider class="!my-3" orientation="left">操作步骤</Divider>
|
||||||
<div class="mx-4">
|
<div class="mx-4">
|
||||||
<ContentList :process-id="processId" :readonly="isDetail" />
|
<ContentList :form-mode="formMode" :process-id="processId" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import { $t } from '#/locales';
|
||||||
import { useRouteProductBomFormSchema } from '../data';
|
import { useRouteProductBomFormSchema } from '../data';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formData = ref<MesProRouteProductBomApi.RouteProductBom>(); // 当前编辑的 BOM 数据
|
const formData = ref<MesProRouteProductBomApi.RouteProductBom>();
|
||||||
const productId = ref<number>(0); // 当前产品物料编号,供 BOM 选择器筛选可用 BOM
|
const productId = ref<number>(0); // 当前产品物料编号,供 BOM 选择器筛选可用 BOM
|
||||||
const getTitle = computed(() =>
|
const getTitle = computed(() =>
|
||||||
formData.value?.id
|
formData.value?.id
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,7 @@ import { useVbenModal } from '@vben/common-ui';
|
||||||
import { message, TabPane, Tabs } from 'ant-design-vue';
|
import { message, TabPane, Tabs } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import {
|
import { createRoute, getRoute, updateRoute } from '#/api/mes/pro/route';
|
||||||
createRoute,
|
|
||||||
getRoute,
|
|
||||||
updateRoute,
|
|
||||||
} from '#/api/mes/pro/route';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useFormSchema } from '../data';
|
import { useFormSchema } from '../data';
|
||||||
|
|
@ -22,11 +18,11 @@ import ProductList from './product-list.vue';
|
||||||
type FormMode = 'create' | 'detail' | 'update';
|
type FormMode = 'create' | 'detail' | 'update';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formMode = ref<FormMode>('create');
|
const formMode = ref<FormMode>('create'); // 表单模式
|
||||||
const subTab = ref('process');
|
const subTab = ref('process'); // 当前激活的子表 Tab
|
||||||
const formData = ref<MesProRouteApi.Route>();
|
const formData = ref<MesProRouteApi.Route>();
|
||||||
|
|
||||||
const isDetail = computed(() => formMode.value === 'detail');
|
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
if (formMode.value === 'detail') {
|
if (formMode.value === 'detail') {
|
||||||
return $t('ui.actionTitle.detail', ['工艺路线']);
|
return $t('ui.actionTitle.detail', ['工艺路线']);
|
||||||
|
|
@ -48,10 +44,9 @@ const [Form, formApi] = useVbenForm({
|
||||||
wrapperClass: 'grid-cols-2',
|
wrapperClass: 'grid-cols-2',
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 表单 schema 需要 formApi 引用(生成编码按钮),所以通过 setState 设置 schema */
|
||||||
formApi.setState({ schema: useFormSchema(formApi) });
|
formApi.setState({ schema: useFormSchema(formApi) });
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
if (isDetail.value) {
|
if (isDetail.value) {
|
||||||
|
|
@ -63,9 +58,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as MesProRouteApi.Route;
|
const data = (await formApi.getValues()) as MesProRouteApi.Route;
|
||||||
try {
|
try {
|
||||||
if (formMode.value === 'create') {
|
if (formMode.value === 'create') {
|
||||||
|
// 新增成功后切到编辑模式,方便继续维护组成工序、关联产品
|
||||||
const id = await createRoute(data);
|
const id = await createRoute(data);
|
||||||
formData.value = { ...data, id };
|
formData.value = { ...data, id };
|
||||||
await formApi.setFieldValue('id', id);
|
await formApi.setFieldValue('id', id);
|
||||||
|
|
@ -87,6 +84,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
subTab.value = 'process';
|
subTab.value = 'process';
|
||||||
|
// 加载数据
|
||||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||||
formMode.value = data?.type ?? 'create';
|
formMode.value = data?.type ?? 'create';
|
||||||
formApi.setDisabled(formMode.value === 'detail');
|
formApi.setDisabled(formMode.value === 'detail');
|
||||||
|
|
@ -97,6 +95,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
formData.value = await getRoute(data.id);
|
formData.value = await getRoute(data.id);
|
||||||
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
|
|
@ -108,6 +107,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
<template>
|
<template>
|
||||||
<Modal :title="getTitle" class="w-4/5">
|
<Modal :title="getTitle" class="w-4/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
|
<!-- 编辑/详情模式下展示子表 Tab,新增模式下隐藏 -->
|
||||||
<Tabs
|
<Tabs
|
||||||
v-if="formMode !== 'create' && formData?.id"
|
v-if="formMode !== 'create' && formData?.id"
|
||||||
v-model:active-key="subTab"
|
v-model:active-key="subTab"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { useVbenForm } from '#/adapter/form';
|
||||||
import { getProcessSimpleList } from '#/api/mes/pro/process';
|
import { getProcessSimpleList } from '#/api/mes/pro/process';
|
||||||
import {
|
import {
|
||||||
createRouteProcess,
|
createRouteProcess,
|
||||||
|
getRouteProcess,
|
||||||
updateRouteProcess,
|
updateRouteProcess,
|
||||||
} from '#/api/mes/pro/route/process';
|
} from '#/api/mes/pro/route/process';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
@ -48,7 +49,6 @@ async function loadSchema(): Promise<VbenFormSchema[]> {
|
||||||
return useRouteProcessFormSchema(options);
|
return useRouteProcessFormSchema(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
|
|
@ -56,12 +56,14 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
const data =
|
const data =
|
||||||
(await formApi.getValues()) as MesProRouteProcessApi.RouteProcess;
|
(await formApi.getValues()) as MesProRouteProcessApi.RouteProcess;
|
||||||
try {
|
try {
|
||||||
await (formData.value?.id
|
await (formData.value?.id
|
||||||
? updateRouteProcess(data)
|
? updateRouteProcess(data)
|
||||||
: createRouteProcess(data));
|
: createRouteProcess(data));
|
||||||
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
|
@ -76,22 +78,24 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
|
// 工序下拉依赖远程数据,schema 在弹窗打开时再生成
|
||||||
const schema = await loadSchema();
|
const schema = await loadSchema();
|
||||||
formApi.setState({ schema });
|
formApi.setState({ schema });
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
|
// 加载数据
|
||||||
const data = modalApi.getData<{
|
const data = modalApi.getData<{
|
||||||
id?: number;
|
id?: number;
|
||||||
maxSort?: number;
|
maxSort?: number;
|
||||||
routeId: number;
|
routeId: number;
|
||||||
row?: MesProRouteProcessApi.RouteProcess;
|
|
||||||
}>();
|
}>();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!data.id) {
|
if (!data.id) {
|
||||||
|
// 新增时,默认序号 = maxSort + 1,并给一个默认甘特图颜色
|
||||||
await formApi.setValues({
|
await formApi.setValues({
|
||||||
colorCode: '#00AEF3',
|
colorCode: '#00AEF3',
|
||||||
routeId: data.routeId,
|
routeId: data.routeId,
|
||||||
|
|
@ -99,9 +103,13 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.row) {
|
modalApi.lock();
|
||||||
formData.value = data.row;
|
try {
|
||||||
await formApi.setValues(data.row);
|
formData.value = await getRouteProcess(data.id);
|
||||||
|
// 设置到 values
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ const props = defineProps<{
|
||||||
routeId: number;
|
routeId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isEditable = ref(props.formMode !== 'detail');
|
const isEditable = ref(props.formMode !== 'detail'); // 是否可编辑
|
||||||
const list = ref<MesProRouteProcessApi.RouteProcess[]>([]);
|
const list = ref<MesProRouteProcessApi.RouteProcess[]>([]); // 工艺路线工序列表
|
||||||
|
|
||||||
const [ProcessFormModal, processFormModalApi] = useVbenModal({
|
const [ProcessFormModal, processFormModalApi] = useVbenModal({
|
||||||
connectedComponent: ProcessForm,
|
connectedComponent: ProcessForm,
|
||||||
|
|
@ -45,7 +45,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
} as VxeTableGridOptions<MesProRouteProcessApi.RouteProcess>,
|
} as VxeTableGridOptions<MesProRouteProcessApi.RouteProcess>,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 加载工艺路线工序列表 */
|
||||||
async function getList() {
|
async function getList() {
|
||||||
gridApi.setLoading(true);
|
gridApi.setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -56,21 +56,18 @@ async function getList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 新增工艺路线工序 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
const maxSort =
|
const maxSort = Math.max(0, ...list.value.map((item) => item.sort || 0));
|
||||||
list.value.length > 0
|
|
||||||
? Math.max(...list.value.map((item) => item.sort || 0))
|
|
||||||
: 0;
|
|
||||||
processFormModalApi.setData({ maxSort, routeId: props.routeId }).open();
|
processFormModalApi.setData({ maxSort, routeId: props.routeId }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 编辑工艺路线工序 */
|
||||||
function handleEdit(row: MesProRouteProcessApi.RouteProcess) {
|
function handleEdit(row: MesProRouteProcessApi.RouteProcess) {
|
||||||
processFormModalApi.setData({ id: row.id, routeId: props.routeId, row }).open();
|
processFormModalApi.setData({ id: row.id, routeId: props.routeId }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 删除工艺路线工序 */
|
||||||
async function handleDelete(row: MesProRouteProcessApi.RouteProcess) {
|
async function handleDelete(row: MesProRouteProcessApi.RouteProcess) {
|
||||||
await deleteRouteProcess(row.id!);
|
await deleteRouteProcess(row.id!);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', ['工艺路线工序']));
|
message.success($t('ui.actionMessage.deleteSuccess', ['工艺路线工序']));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesProRouteProcessApi } from '#/api/mes/pro/route/process';
|
||||||
import type { MesProRouteProductBomApi } from '#/api/mes/pro/route/productbom';
|
import type { MesProRouteProductBomApi } from '#/api/mes/pro/route/productbom';
|
||||||
|
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
@ -25,12 +26,9 @@ const props = defineProps<{
|
||||||
routeId: number;
|
routeId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// TODO @AI:const 的尾注释?别的 vue 文件也看看,遵守 agents md 里说的。
|
const processList = ref<MesProRouteProcessApi.RouteProcess[]>([]); // 工序列表(用于 Tab)
|
||||||
const processOptions = ref<
|
const activeProcessId = ref<number>(); // 当前选中的工序 Tab
|
||||||
Array<{ processId: number; processName?: string }>
|
const list = ref<MesProRouteProductBomApi.RouteProductBom[]>([]); // 当前工序下的 BOM 列表
|
||||||
>([]); // TODO @AI:是不是不用转换,vue 那直接处理就好了;
|
|
||||||
const activeProcessId = ref<string>(''); // TODO @AI:这个好像是 number?看看 vue3 + ep 里的代码。
|
|
||||||
const list = ref<MesProRouteProductBomApi.RouteProductBom[]>([]);
|
|
||||||
|
|
||||||
const [BomFormModal, bomFormModalApi] = useVbenModal({
|
const [BomFormModal, bomFormModalApi] = useVbenModal({
|
||||||
connectedComponent: BomForm,
|
connectedComponent: BomForm,
|
||||||
|
|
@ -53,16 +51,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
|
||||||
/** 加载路线下的工序列表,用于工序 Tab */
|
/** 加载路线下的工序列表,用于工序 Tab */
|
||||||
async function loadProcessList() {
|
async function loadProcessList() {
|
||||||
const data = await getRouteProcessListByRoute(props.routeId);
|
processList.value = (await getRouteProcessListByRoute(props.routeId)) || [];
|
||||||
processOptions.value = (data || []).map((item) => ({
|
if (processList.value.length > 0) {
|
||||||
processId: item.processId!,
|
activeProcessId.value = processList.value[0]!.processId;
|
||||||
processName: item.processName,
|
|
||||||
}));
|
|
||||||
if (processOptions.value.length > 0) {
|
|
||||||
activeProcessId.value = String(processOptions.value[0]!.processId);
|
|
||||||
await getList();
|
await getList();
|
||||||
} else {
|
} else {
|
||||||
activeProcessId.value = '';
|
activeProcessId.value = undefined;
|
||||||
list.value = [];
|
list.value = [];
|
||||||
gridApi.setGridOptions({ data: list.value });
|
gridApi.setGridOptions({ data: list.value });
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +70,7 @@ async function getList() {
|
||||||
gridApi.setLoading(true);
|
gridApi.setLoading(true);
|
||||||
try {
|
try {
|
||||||
list.value = await getRouteProductBomList({
|
list.value = await getRouteProductBomList({
|
||||||
processId: Number(activeProcessId.value),
|
processId: activeProcessId.value,
|
||||||
productId: props.productId,
|
productId: props.productId,
|
||||||
routeId: props.routeId,
|
routeId: props.routeId,
|
||||||
});
|
});
|
||||||
|
|
@ -94,7 +88,7 @@ function handleCreate() {
|
||||||
}
|
}
|
||||||
bomFormModalApi
|
bomFormModalApi
|
||||||
.setData({
|
.setData({
|
||||||
processId: Number(activeProcessId.value),
|
processId: activeProcessId.value,
|
||||||
productId: props.productId,
|
productId: props.productId,
|
||||||
routeId: props.routeId,
|
routeId: props.routeId,
|
||||||
})
|
})
|
||||||
|
|
@ -105,10 +99,10 @@ function handleCreate() {
|
||||||
function handleEdit(row: MesProRouteProductBomApi.RouteProductBom) {
|
function handleEdit(row: MesProRouteProductBomApi.RouteProductBom) {
|
||||||
bomFormModalApi
|
bomFormModalApi
|
||||||
.setData({
|
.setData({
|
||||||
processId: Number(activeProcessId.value),
|
id: row.id,
|
||||||
|
processId: activeProcessId.value!,
|
||||||
productId: props.productId,
|
productId: props.productId,
|
||||||
routeId: props.routeId,
|
routeId: props.routeId,
|
||||||
row,
|
|
||||||
})
|
})
|
||||||
.open();
|
.open();
|
||||||
}
|
}
|
||||||
|
|
@ -135,8 +129,8 @@ watch(
|
||||||
<BomFormModal @success="getList" />
|
<BomFormModal @success="getList" />
|
||||||
<Tabs v-model:active-key="activeProcessId" @change="getList">
|
<Tabs v-model:active-key="activeProcessId" @change="getList">
|
||||||
<TabPane
|
<TabPane
|
||||||
v-for="item in processOptions"
|
v-for="item in processList"
|
||||||
:key="String(item.processId)"
|
:key="item.processId"
|
||||||
:tab="item.processName"
|
:tab="item.processName"
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { Divider, message } from 'ant-design-vue';
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import {
|
import {
|
||||||
createRouteProduct,
|
createRouteProduct,
|
||||||
|
getRouteProduct,
|
||||||
updateRouteProduct,
|
updateRouteProduct,
|
||||||
} from '#/api/mes/pro/route/product';
|
} from '#/api/mes/pro/route/product';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
@ -18,7 +19,7 @@ import { useRouteProductFormSchema } from '../data';
|
||||||
import ProductBomList from './product-bom-list.vue';
|
import ProductBomList from './product-bom-list.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formData = ref<MesProRouteProductApi.RouteProduct>(); // 当前编辑/查看的产品数据
|
const formData = ref<MesProRouteProductApi.RouteProduct>();
|
||||||
const getTitle = computed(() =>
|
const getTitle = computed(() =>
|
||||||
formData.value?.id
|
formData.value?.id
|
||||||
? $t('ui.actionTitle.edit', ['工艺路线产品'])
|
? $t('ui.actionTitle.edit', ['工艺路线产品'])
|
||||||
|
|
@ -51,7 +52,6 @@ const [Form, formApi] = useVbenForm({
|
||||||
});
|
});
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
|
|
@ -64,7 +64,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
if (formData.value?.id) {
|
if (formData.value?.id) {
|
||||||
await updateRouteProduct(data);
|
await updateRouteProduct(data);
|
||||||
|
// 用最新表单值同步 formData,确保产品 BOM 子表绑定的 itemId 与表单一致
|
||||||
|
formData.value = { ...formData.value, ...data };
|
||||||
} else {
|
} else {
|
||||||
|
// 新增成功后切换到编辑模式,方便继续维护产品 BOM
|
||||||
const id = await createRouteProduct(data);
|
const id = await createRouteProduct(data);
|
||||||
formData.value = { ...data, id };
|
formData.value = { ...data, id };
|
||||||
await formApi.setFieldValue('id', id);
|
await formApi.setFieldValue('id', id);
|
||||||
|
|
@ -85,18 +88,22 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
const data = modalApi.getData<{
|
const data = modalApi.getData<{
|
||||||
id?: number;
|
id?: number;
|
||||||
routeId: number;
|
routeId: number;
|
||||||
row?: MesProRouteProductApi.RouteProduct;
|
|
||||||
}>();
|
}>();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.row) {
|
if (!data.id) {
|
||||||
formData.value = data.row;
|
await formApi.setValues({ routeId: data.routeId });
|
||||||
// 设置到 values
|
|
||||||
await formApi.setValues(data.row);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await formApi.setValues({ routeId: data.routeId });
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
formData.value = await getRouteProduct(data.id);
|
||||||
|
// 设置到 values
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -104,7 +111,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
<template>
|
<template>
|
||||||
<Modal :title="getTitle" class="w-3/5">
|
<Modal :title="getTitle" class="w-3/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
<!-- 编辑/详情模式下展示产品 BOM 子表,新增模式下隐藏 -->
|
<!-- 编辑模式下展示产品 BOM 子表,新增模式下隐藏 -->
|
||||||
<template v-if="formData?.id && formData?.itemId">
|
<template v-if="formData?.id && formData?.itemId">
|
||||||
<Divider class="!my-3" orientation="left">产品 BOM 配置</Divider>
|
<Divider class="!my-3" orientation="left">产品 BOM 配置</Divider>
|
||||||
<div class="mx-4">
|
<div class="mx-4">
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,14 @@ const props = defineProps<{
|
||||||
routeId: number;
|
routeId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isEditable = ref(props.formMode !== 'detail');
|
const isEditable = ref(props.formMode !== 'detail'); // 是否可编辑
|
||||||
const list = ref<MesProRouteProductApi.RouteProduct[]>([]);
|
const list = ref<MesProRouteProductApi.RouteProduct[]>([]); // 工艺路线产品列表
|
||||||
|
|
||||||
const [ProductFormModal, productFormModalApi] = useVbenModal({
|
const [ProductFormModal, productFormModalApi] = useVbenModal({
|
||||||
connectedComponent: ProductForm,
|
connectedComponent: ProductForm,
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO @AI:代码的排版风格?ps:和别的模块,看看是不是一致;
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
autoResize: true,
|
autoResize: true,
|
||||||
|
|
@ -48,7 +47,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
} as VxeTableGridOptions<MesProRouteProductApi.RouteProduct>,
|
} as VxeTableGridOptions<MesProRouteProductApi.RouteProduct>,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 加载工艺路线产品列表 */
|
||||||
async function getList() {
|
async function getList() {
|
||||||
gridApi.setLoading(true);
|
gridApi.setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -59,17 +58,17 @@ async function getList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 新增工艺路线产品 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
productFormModalApi.setData({ routeId: props.routeId }).open();
|
productFormModalApi.setData({ routeId: props.routeId }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 编辑工艺路线产品 */
|
||||||
function handleEdit(row: MesProRouteProductApi.RouteProduct) {
|
function handleEdit(row: MesProRouteProductApi.RouteProduct) {
|
||||||
productFormModalApi.setData({ id: row.id, routeId: props.routeId, row }).open();
|
productFormModalApi.setData({ id: row.id, routeId: props.routeId }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @AI:注释风格,是不是和别的没对齐
|
/** 删除工艺路线产品 */
|
||||||
async function handleDelete(row: MesProRouteProductApi.RouteProduct) {
|
async function handleDelete(row: MesProRouteProductApi.RouteProduct) {
|
||||||
await deleteRouteProduct(row.id!);
|
await deleteRouteProduct(row.id!);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', ['工艺路线产品']));
|
message.success($t('ui.actionMessage.deleteSuccess', ['工艺路线产品']));
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,11 @@ import { useContentGridColumns } from '../data';
|
||||||
import ContentForm from './content-form.vue';
|
import ContentForm from './content-form.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
formMode: 'create' | 'detail' | 'update';
|
||||||
processId: number;
|
processId: number;
|
||||||
readonly?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const isEditable = ref(props.formMode !== 'detail'); // 是否可编辑
|
||||||
const list = ref<MesProProcessContentApi.ProcessContent[]>([]);
|
const list = ref<MesProProcessContentApi.ProcessContent[]>([]);
|
||||||
|
|
||||||
const [ContentFormModal, contentFormModalApi] = useVbenModal({
|
const [ContentFormModal, contentFormModalApi] = useVbenModal({
|
||||||
|
|
@ -60,10 +61,7 @@ async function getList() {
|
||||||
|
|
||||||
/** 新增工序步骤 */
|
/** 新增工序步骤 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
const maxSort =
|
const maxSort = Math.max(0, ...list.value.map((item) => item.sort || 0));
|
||||||
list.value.length > 0
|
|
||||||
? Math.max(...list.value.map((item) => item.sort || 0))
|
|
||||||
: 0;
|
|
||||||
contentFormModalApi
|
contentFormModalApi
|
||||||
.setData({ maxSort, processId: props.processId })
|
.setData({ maxSort, processId: props.processId })
|
||||||
.open();
|
.open();
|
||||||
|
|
@ -94,7 +92,7 @@ watch(
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ContentFormModal @success="getList" />
|
<ContentFormModal @success="getList" />
|
||||||
<div v-if="!readonly" class="mb-3 flex items-center justify-start">
|
<div v-if="isEditable" class="mb-3 flex items-center justify-start">
|
||||||
<TableAction
|
<TableAction
|
||||||
:actions="[
|
:actions="[
|
||||||
{
|
{
|
||||||
|
|
@ -113,14 +111,14 @@ watch(
|
||||||
label: $t('common.edit'),
|
label: $t('common.edit'),
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
link: true,
|
link: true,
|
||||||
ifShow: () => !readonly,
|
ifShow: () => isEditable,
|
||||||
onClick: handleEdit.bind(null, row),
|
onClick: handleEdit.bind(null, row),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $t('common.delete'),
|
label: $t('common.delete'),
|
||||||
type: 'danger',
|
type: 'danger',
|
||||||
link: true,
|
link: true,
|
||||||
ifShow: () => !readonly,
|
ifShow: () => isEditable,
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: $t('ui.actionMessage.deleteConfirm', ['工序步骤']),
|
title: $t('ui.actionMessage.deleteConfirm', ['工序步骤']),
|
||||||
confirm: handleDelete.bind(null, row),
|
confirm: handleDelete.bind(null, row),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type FormMode = 'create' | 'detail' | 'update';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formMode = ref<FormMode>('create'); // 表单模式
|
const formMode = ref<FormMode>('create'); // 表单模式
|
||||||
const formData = ref<MesProProcessApi.Process>(); // 当前编辑/查看的工序数据
|
const formData = ref<MesProProcessApi.Process>();
|
||||||
|
|
||||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
|
|
@ -110,7 +110,7 @@ const processId = computed(() => formData.value?.id);
|
||||||
<template v-if="processId">
|
<template v-if="processId">
|
||||||
<ElDivider class="!my-3" content-position="left">操作步骤</ElDivider>
|
<ElDivider class="!my-3" content-position="left">操作步骤</ElDivider>
|
||||||
<div class="mx-4">
|
<div class="mx-4">
|
||||||
<ContentList :process-id="processId" :readonly="isDetail" />
|
<ContentList :form-mode="formMode" :process-id="processId" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@ import { ElButton } from 'element-plus';
|
||||||
|
|
||||||
import { z } from '#/adapter/form';
|
import { z } from '#/adapter/form';
|
||||||
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||||
|
import {
|
||||||
|
MdItemSelect,
|
||||||
|
MdProductBomSelect,
|
||||||
|
} from '#/views/mes/md/item/components';
|
||||||
import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants';
|
import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants';
|
||||||
|
|
||||||
/** 工艺路线表单 */
|
/** 工艺路线表单 */
|
||||||
|
|
@ -348,3 +352,134 @@ export function useRouteProductBomGridColumns(): VxeTableGridOptions<MesProRoute
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 工艺路线产品表单 */
|
||||||
|
export function useRouteProductFormSchema(
|
||||||
|
onItemChange?: (item: any) => void,
|
||||||
|
): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: { triggerFields: [''], show: () => false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'routeId',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: { triggerFields: [''], show: () => false },
|
||||||
|
},
|
||||||
|
// 产品物料:使用业务自定义选择器,change 回填编码/名称/规格/单位
|
||||||
|
{
|
||||||
|
fieldName: 'itemId',
|
||||||
|
label: '产品',
|
||||||
|
component: MdItemSelect as any,
|
||||||
|
componentProps: {
|
||||||
|
onChange: onItemChange,
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
rules: 'selectRequired',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'quantity',
|
||||||
|
label: '生产数量',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: {
|
||||||
|
class: '!w-full',
|
||||||
|
controlsPosition: 'right',
|
||||||
|
min: 1,
|
||||||
|
precision: 0,
|
||||||
|
},
|
||||||
|
rules: z.number().default(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'productionTime',
|
||||||
|
label: '生产用时',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: {
|
||||||
|
class: '!w-full',
|
||||||
|
controlsPosition: 'right',
|
||||||
|
min: 0,
|
||||||
|
precision: 2,
|
||||||
|
},
|
||||||
|
rules: z.number().default(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'timeUnitType',
|
||||||
|
label: '时间单位',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_TIME_UNIT_TYPE),
|
||||||
|
placeholder: '请选择',
|
||||||
|
},
|
||||||
|
rules: z.string().default('MINUTE'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
component: 'Textarea',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
componentProps: { maxLength: 250, placeholder: '请输入备注', rows: 2 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 工艺路线产品 BOM 表单 */
|
||||||
|
export function useRouteProductBomFormSchema(
|
||||||
|
itemId: () => number,
|
||||||
|
onBomChange?: (bom: any) => void,
|
||||||
|
): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: { triggerFields: [''], show: () => false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'routeId',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: { triggerFields: [''], show: () => false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'processId',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: { triggerFields: [''], show: () => false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'productId',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: { triggerFields: [''], show: () => false },
|
||||||
|
},
|
||||||
|
// BOM 物料:依赖产品物料,使用业务自定义选择器
|
||||||
|
{
|
||||||
|
fieldName: 'itemId',
|
||||||
|
label: 'BOM 物料',
|
||||||
|
component: MdProductBomSelect as any,
|
||||||
|
componentProps: () => ({
|
||||||
|
itemId: itemId(),
|
||||||
|
onChange: onBomChange,
|
||||||
|
placeholder: '请选择 BOM 物料',
|
||||||
|
}),
|
||||||
|
rules: 'selectRequired',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'quantity',
|
||||||
|
label: '用料比例',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: {
|
||||||
|
class: '!w-full',
|
||||||
|
controlsPosition: 'right',
|
||||||
|
min: 0,
|
||||||
|
precision: 2,
|
||||||
|
},
|
||||||
|
rules: z.number().default(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
component: 'Textarea',
|
||||||
|
componentProps: { maxLength: 250, placeholder: '请输入备注', rows: 2 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@ import ProductList from './product-list.vue';
|
||||||
type FormMode = 'create' | 'detail' | 'update';
|
type FormMode = 'create' | 'detail' | 'update';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formMode = ref<FormMode>('create');
|
const formMode = ref<FormMode>('create'); // 表单模式
|
||||||
const subTab = ref('process');
|
const subTab = ref('process'); // 当前激活的子表 Tab
|
||||||
const formData = ref<MesProRouteApi.Route>();
|
const formData = ref<MesProRouteApi.Route>();
|
||||||
|
|
||||||
const isDetail = computed(() => formMode.value === 'detail');
|
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
if (formMode.value === 'detail') {
|
if (formMode.value === 'detail') {
|
||||||
return $t('ui.actionTitle.detail', ['工艺路线']);
|
return $t('ui.actionTitle.detail', ['工艺路线']);
|
||||||
|
|
@ -44,6 +44,7 @@ const [Form, formApi] = useVbenForm({
|
||||||
wrapperClass: 'grid-cols-2',
|
wrapperClass: 'grid-cols-2',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 表单 schema 需要 formApi 引用(生成编码按钮),所以通过 setState 设置 schema */
|
||||||
formApi.setState({ schema: useFormSchema(formApi) });
|
formApi.setState({ schema: useFormSchema(formApi) });
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
|
@ -57,9 +58,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as MesProRouteApi.Route;
|
const data = (await formApi.getValues()) as MesProRouteApi.Route;
|
||||||
try {
|
try {
|
||||||
if (formMode.value === 'create') {
|
if (formMode.value === 'create') {
|
||||||
|
// 新增成功后切到编辑模式,方便继续维护组成工序、关联产品
|
||||||
const id = await createRoute(data);
|
const id = await createRoute(data);
|
||||||
formData.value = { ...data, id };
|
formData.value = { ...data, id };
|
||||||
await formApi.setFieldValue('id', id);
|
await formApi.setFieldValue('id', id);
|
||||||
|
|
@ -81,6 +84,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
subTab.value = 'process';
|
subTab.value = 'process';
|
||||||
|
// 加载数据
|
||||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||||
formMode.value = data?.type ?? 'create';
|
formMode.value = data?.type ?? 'create';
|
||||||
formApi.setDisabled(formMode.value === 'detail');
|
formApi.setDisabled(formMode.value === 'detail');
|
||||||
|
|
@ -91,6 +95,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
formData.value = await getRoute(data.id);
|
formData.value = await getRoute(data.id);
|
||||||
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
|
|
@ -102,6 +107,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
<template>
|
<template>
|
||||||
<Modal :title="getTitle" class="w-4/5">
|
<Modal :title="getTitle" class="w-4/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
|
<!-- 编辑/详情模式下展示子表 Tab,新增模式下隐藏 -->
|
||||||
<ElTabs
|
<ElTabs
|
||||||
v-if="formMode !== 'create' && formData?.id"
|
v-if="formMode !== 'create' && formData?.id"
|
||||||
v-model="subTab"
|
v-model="subTab"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { useVbenForm } from '#/adapter/form';
|
||||||
import { getProcessSimpleList } from '#/api/mes/pro/process';
|
import { getProcessSimpleList } from '#/api/mes/pro/process';
|
||||||
import {
|
import {
|
||||||
createRouteProcess,
|
createRouteProcess,
|
||||||
|
getRouteProcess,
|
||||||
updateRouteProcess,
|
updateRouteProcess,
|
||||||
} from '#/api/mes/pro/route/process';
|
} from '#/api/mes/pro/route/process';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
@ -38,6 +39,7 @@ const [Form, formApi] = useVbenForm({
|
||||||
wrapperClass: 'grid-cols-2',
|
wrapperClass: 'grid-cols-2',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 加载工序选项后再生成 schema(避免下拉空选项) */
|
||||||
async function loadSchema(): Promise<VbenFormSchema[]> {
|
async function loadSchema(): Promise<VbenFormSchema[]> {
|
||||||
const list = await getProcessSimpleList();
|
const list = await getProcessSimpleList();
|
||||||
const options = (list || []).map((item) => ({
|
const options = (list || []).map((item) => ({
|
||||||
|
|
@ -54,12 +56,14 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
const data =
|
const data =
|
||||||
(await formApi.getValues()) as MesProRouteProcessApi.RouteProcess;
|
(await formApi.getValues()) as MesProRouteProcessApi.RouteProcess;
|
||||||
try {
|
try {
|
||||||
await (formData.value?.id
|
await (formData.value?.id
|
||||||
? updateRouteProcess(data)
|
? updateRouteProcess(data)
|
||||||
: createRouteProcess(data));
|
: createRouteProcess(data));
|
||||||
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
|
@ -74,22 +78,24 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
|
// 工序下拉依赖远程数据,schema 在弹窗打开时再生成
|
||||||
const schema = await loadSchema();
|
const schema = await loadSchema();
|
||||||
formApi.setState({ schema });
|
formApi.setState({ schema });
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
|
// 加载数据
|
||||||
const data = modalApi.getData<{
|
const data = modalApi.getData<{
|
||||||
id?: number;
|
id?: number;
|
||||||
maxSort?: number;
|
maxSort?: number;
|
||||||
routeId: number;
|
routeId: number;
|
||||||
row?: MesProRouteProcessApi.RouteProcess;
|
|
||||||
}>();
|
}>();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!data.id) {
|
if (!data.id) {
|
||||||
|
// 新增时,默认序号 = maxSort + 1,并给一个默认甘特图颜色
|
||||||
await formApi.setValues({
|
await formApi.setValues({
|
||||||
colorCode: '#00AEF3',
|
colorCode: '#00AEF3',
|
||||||
routeId: data.routeId,
|
routeId: data.routeId,
|
||||||
|
|
@ -97,9 +103,13 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.row) {
|
modalApi.lock();
|
||||||
formData.value = data.row;
|
try {
|
||||||
await formApi.setValues(data.row);
|
formData.value = await getRouteProcess(data.id);
|
||||||
|
// 设置到 values
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ const props = defineProps<{
|
||||||
routeId: number;
|
routeId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isEditable = ref(props.formMode !== 'detail');
|
const isEditable = ref(props.formMode !== 'detail'); // 是否可编辑
|
||||||
const list = ref<MesProRouteProcessApi.RouteProcess[]>([]);
|
const list = ref<MesProRouteProcessApi.RouteProcess[]>([]); // 工艺路线工序列表
|
||||||
|
|
||||||
const [ProcessFormModal, processFormModalApi] = useVbenModal({
|
const [ProcessFormModal, processFormModalApi] = useVbenModal({
|
||||||
connectedComponent: ProcessForm,
|
connectedComponent: ProcessForm,
|
||||||
|
|
@ -45,6 +45,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
} as VxeTableGridOptions<MesProRouteProcessApi.RouteProcess>,
|
} as VxeTableGridOptions<MesProRouteProcessApi.RouteProcess>,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 加载工艺路线工序列表 */
|
||||||
async function getList() {
|
async function getList() {
|
||||||
gridApi.setLoading(true);
|
gridApi.setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -55,20 +56,18 @@ async function getList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 新增工艺路线工序 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
const maxSort =
|
const maxSort = Math.max(0, ...list.value.map((item) => item.sort || 0));
|
||||||
list.value.length > 0
|
|
||||||
? Math.max(...list.value.map((item) => item.sort || 0))
|
|
||||||
: 0;
|
|
||||||
processFormModalApi.setData({ maxSort, routeId: props.routeId }).open();
|
processFormModalApi.setData({ maxSort, routeId: props.routeId }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 编辑工艺路线工序 */
|
||||||
function handleEdit(row: MesProRouteProcessApi.RouteProcess) {
|
function handleEdit(row: MesProRouteProcessApi.RouteProcess) {
|
||||||
processFormModalApi
|
processFormModalApi.setData({ id: row.id, routeId: props.routeId }).open();
|
||||||
.setData({ id: row.id, routeId: props.routeId, row })
|
|
||||||
.open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除工艺路线工序 */
|
||||||
async function handleDelete(row: MesProRouteProcessApi.RouteProcess) {
|
async function handleDelete(row: MesProRouteProcessApi.RouteProcess) {
|
||||||
await deleteRouteProcess(row.id!);
|
await deleteRouteProcess(row.id!);
|
||||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['工艺路线工序']));
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['工艺路线工序']));
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,24 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
|
||||||
|
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesProRouteProcessApi } from '#/api/mes/pro/route/process';
|
||||||
import type { MesProRouteProductBomApi } from '#/api/mes/pro/route/productbom';
|
import type { MesProRouteProductBomApi } from '#/api/mes/pro/route/productbom';
|
||||||
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
import {
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
ElDialog,
|
|
||||||
ElForm,
|
import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
|
||||||
ElFormItem,
|
|
||||||
ElInput,
|
|
||||||
ElInputNumber,
|
|
||||||
ElMessage,
|
|
||||||
ElTabPane,
|
|
||||||
ElTabs,
|
|
||||||
} from 'element-plus';
|
|
||||||
|
|
||||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getRouteProcessListByRoute } from '#/api/mes/pro/route/process';
|
import { getRouteProcessListByRoute } from '#/api/mes/pro/route/process';
|
||||||
import {
|
import {
|
||||||
createRouteProductBom,
|
|
||||||
deleteRouteProductBom,
|
deleteRouteProductBom,
|
||||||
getRouteProductBomList,
|
getRouteProductBomList,
|
||||||
updateRouteProductBom,
|
|
||||||
} from '#/api/mes/pro/route/productbom';
|
} from '#/api/mes/pro/route/productbom';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { MdProductBomSelect } from '#/views/mes/md/item/components';
|
|
||||||
|
|
||||||
import { useRouteProductBomGridColumns } from '../data';
|
import { useRouteProductBomGridColumns } from '../data';
|
||||||
|
import BomForm from './bom-form.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
productId: number;
|
productId: number;
|
||||||
|
|
@ -36,27 +26,14 @@ const props = defineProps<{
|
||||||
routeId: number;
|
routeId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const processOptions = ref<
|
const processList = ref<MesProRouteProcessApi.RouteProcess[]>([]); // 工序列表(用于 Tab)
|
||||||
Array<{ processId: number; processName?: string }>
|
const activeProcessId = ref<number>(); // 当前选中的工序 Tab
|
||||||
>([]);
|
const list = ref<MesProRouteProductBomApi.RouteProductBom[]>([]); // 当前工序下的 BOM 列表
|
||||||
const activeProcessId = ref<string>('');
|
|
||||||
const list = ref<MesProRouteProductBomApi.RouteProductBom[]>([]);
|
|
||||||
|
|
||||||
const formVisible = ref(false);
|
const [BomFormModal, bomFormModalApi] = useVbenModal({
|
||||||
const formRef = ref<FormInstance>();
|
connectedComponent: BomForm,
|
||||||
const isUpdate = ref(false);
|
destroyOnClose: true,
|
||||||
const formData = reactive<MesProRouteProductBomApi.RouteProductBom>({
|
|
||||||
quantity: 1,
|
|
||||||
});
|
});
|
||||||
const formRules: FormRules = {
|
|
||||||
itemId: [{ message: 'BOM 物料不能为空', required: true }],
|
|
||||||
quantity: [{ message: '用料比例不能为空', required: true }],
|
|
||||||
};
|
|
||||||
const formTitle = computed(() =>
|
|
||||||
isUpdate.value
|
|
||||||
? $t('ui.actionTitle.edit', ['BOM 物料'])
|
|
||||||
: $t('ui.actionTitle.create', ['BOM 物料']),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
|
|
@ -72,22 +49,20 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
} as VxeTableGridOptions<MesProRouteProductBomApi.RouteProductBom>,
|
} as VxeTableGridOptions<MesProRouteProductBomApi.RouteProductBom>,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 加载路线下的工序列表,用于工序 Tab */
|
||||||
async function loadProcessList() {
|
async function loadProcessList() {
|
||||||
const data = await getRouteProcessListByRoute(props.routeId);
|
processList.value = (await getRouteProcessListByRoute(props.routeId)) || [];
|
||||||
processOptions.value = (data || []).map((item) => ({
|
if (processList.value.length > 0) {
|
||||||
processId: item.processId!,
|
activeProcessId.value = processList.value[0]!.processId;
|
||||||
processName: item.processName,
|
|
||||||
}));
|
|
||||||
if (processOptions.value.length > 0) {
|
|
||||||
activeProcessId.value = String(processOptions.value[0]!.processId);
|
|
||||||
await getList();
|
await getList();
|
||||||
} else {
|
} else {
|
||||||
activeProcessId.value = '';
|
activeProcessId.value = undefined;
|
||||||
list.value = [];
|
list.value = [];
|
||||||
gridApi.setGridOptions({ data: list.value });
|
gridApi.setGridOptions({ data: list.value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 加载当前工序下的 BOM 列表 */
|
||||||
async function getList() {
|
async function getList() {
|
||||||
if (!activeProcessId.value) {
|
if (!activeProcessId.value) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -95,7 +70,7 @@ async function getList() {
|
||||||
gridApi.setLoading(true);
|
gridApi.setLoading(true);
|
||||||
try {
|
try {
|
||||||
list.value = await getRouteProductBomList({
|
list.value = await getRouteProductBomList({
|
||||||
processId: Number(activeProcessId.value),
|
processId: activeProcessId.value,
|
||||||
productId: props.productId,
|
productId: props.productId,
|
||||||
routeId: props.routeId,
|
routeId: props.routeId,
|
||||||
});
|
});
|
||||||
|
|
@ -105,69 +80,40 @@ async function getList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetForm() {
|
/** 新增 BOM */
|
||||||
Object.assign(formData, {
|
|
||||||
id: undefined,
|
|
||||||
itemCode: undefined,
|
|
||||||
itemId: undefined,
|
|
||||||
itemName: undefined,
|
|
||||||
processId: Number(activeProcessId.value),
|
|
||||||
productId: props.productId,
|
|
||||||
quantity: 1,
|
|
||||||
remark: undefined,
|
|
||||||
routeId: props.routeId,
|
|
||||||
specification: undefined,
|
|
||||||
unitName: undefined,
|
|
||||||
});
|
|
||||||
formRef.value?.clearValidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
if (!activeProcessId.value) {
|
if (!activeProcessId.value) {
|
||||||
ElMessage.warning('请先选择工序');
|
ElMessage.warning('请先选择工序');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resetForm();
|
bomFormModalApi
|
||||||
isUpdate.value = false;
|
.setData({
|
||||||
formVisible.value = true;
|
processId: activeProcessId.value,
|
||||||
|
productId: props.productId,
|
||||||
|
routeId: props.routeId,
|
||||||
|
})
|
||||||
|
.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 编辑 BOM */
|
||||||
function handleEdit(row: MesProRouteProductBomApi.RouteProductBom) {
|
function handleEdit(row: MesProRouteProductBomApi.RouteProductBom) {
|
||||||
Object.assign(formData, row);
|
bomFormModalApi
|
||||||
isUpdate.value = true;
|
.setData({
|
||||||
formVisible.value = true;
|
id: row.id,
|
||||||
|
processId: activeProcessId.value!,
|
||||||
|
productId: props.productId,
|
||||||
|
routeId: props.routeId,
|
||||||
|
})
|
||||||
|
.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除 BOM */
|
||||||
async function handleDelete(row: MesProRouteProductBomApi.RouteProductBom) {
|
async function handleDelete(row: MesProRouteProductBomApi.RouteProductBom) {
|
||||||
await deleteRouteProductBom(row.id!);
|
await deleteRouteProductBom(row.id!);
|
||||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['BOM 物料']));
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['BOM 物料']));
|
||||||
await getList();
|
await getList();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitForm() {
|
|
||||||
try {
|
|
||||||
await formRef.value?.validate();
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await (isUpdate.value
|
|
||||||
? updateRouteProductBom(formData)
|
|
||||||
: createRouteProductBom(formData));
|
|
||||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
|
||||||
formVisible.value = false;
|
|
||||||
await getList();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBomChange(bom?: any) {
|
|
||||||
if (bom) {
|
|
||||||
formData.quantity = bom.quantity ?? 1;
|
|
||||||
formData.itemCode = bom.bomItemCode;
|
|
||||||
formData.itemName = bom.bomItemName;
|
|
||||||
formData.specification = bom.specification;
|
|
||||||
formData.unitName = bom.unitName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [props.routeId, props.productId],
|
() => [props.routeId, props.productId],
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -180,12 +126,13 @@ watch(
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<BomFormModal @success="getList" />
|
||||||
<ElTabs v-model="activeProcessId" @tab-change="getList">
|
<ElTabs v-model="activeProcessId" @tab-change="getList">
|
||||||
<ElTabPane
|
<ElTabPane
|
||||||
v-for="item in processOptions"
|
v-for="item in processList"
|
||||||
:key="String(item.processId)"
|
:key="item.processId"
|
||||||
:label="item.processName"
|
:label="item.processName"
|
||||||
:name="String(item.processId)"
|
:name="item.processId"
|
||||||
/>
|
/>
|
||||||
</ElTabs>
|
</ElTabs>
|
||||||
<div class="mb-3 flex items-center justify-start">
|
<div class="mb-3 flex items-center justify-start">
|
||||||
|
|
@ -223,56 +170,4 @@ watch(
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ElDialog v-model="formVisible" :title="formTitle" width="500px">
|
|
||||||
<ElForm
|
|
||||||
ref="formRef"
|
|
||||||
label-width="100px"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
>
|
|
||||||
<ElFormItem label="BOM 物料" prop="itemId">
|
|
||||||
<MdProductBomSelect
|
|
||||||
v-model="formData.itemId"
|
|
||||||
:item-id="productId"
|
|
||||||
placeholder="请选择 BOM 物料"
|
|
||||||
@change="handleBomChange"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="用料比例" prop="quantity">
|
|
||||||
<ElInputNumber
|
|
||||||
v-model="formData.quantity"
|
|
||||||
class="!w-full"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="备注" prop="remark">
|
|
||||||
<ElInput
|
|
||||||
v-model="formData.remark"
|
|
||||||
:maxlength="250"
|
|
||||||
placeholder="请输入备注"
|
|
||||||
:rows="2"
|
|
||||||
type="textarea"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
</ElForm>
|
|
||||||
<template #footer>
|
|
||||||
<TableAction
|
|
||||||
:actions="[
|
|
||||||
{
|
|
||||||
label: $t('common.cancel'),
|
|
||||||
type: 'default',
|
|
||||||
onClick: () => (formVisible = false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: $t('common.confirm'),
|
|
||||||
type: 'primary',
|
|
||||||
onClick: submitForm,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</ElDialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,76 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
|
||||||
|
|
||||||
import type { MesProRouteProductApi } from '#/api/mes/pro/route/product';
|
import type { MesProRouteProductApi } from '#/api/mes/pro/route/product';
|
||||||
|
|
||||||
import { computed, reactive, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
|
||||||
import { getDictOptions } from '@vben/hooks';
|
|
||||||
|
|
||||||
import {
|
import { ElDivider, ElMessage } from 'element-plus';
|
||||||
ElDivider,
|
|
||||||
ElForm,
|
|
||||||
ElFormItem,
|
|
||||||
ElInput,
|
|
||||||
ElInputNumber,
|
|
||||||
ElMessage,
|
|
||||||
ElOption,
|
|
||||||
ElSelect,
|
|
||||||
} from 'element-plus';
|
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import {
|
import {
|
||||||
createRouteProduct,
|
createRouteProduct,
|
||||||
|
getRouteProduct,
|
||||||
updateRouteProduct,
|
updateRouteProduct,
|
||||||
} from '#/api/mes/pro/route/product';
|
} from '#/api/mes/pro/route/product';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { MdItemSelect } from '#/views/mes/md/item/components';
|
|
||||||
|
|
||||||
|
import { useRouteProductFormSchema } from '../data';
|
||||||
import ProductBomList from './product-bom-list.vue';
|
import ProductBomList from './product-bom-list.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const isUpdate = ref(false);
|
const formData = ref<MesProRouteProductApi.RouteProduct>();
|
||||||
const formRef = ref<FormInstance>();
|
|
||||||
const formData = reactive<MesProRouteProductApi.RouteProduct>({
|
|
||||||
productionTime: 1,
|
|
||||||
quantity: 1,
|
|
||||||
timeUnitType: 'MINUTE',
|
|
||||||
});
|
|
||||||
const formRules: FormRules = {
|
|
||||||
itemId: [{ message: '产品不能为空', required: true }],
|
|
||||||
quantity: [{ message: '生产数量不能为空', required: true }],
|
|
||||||
};
|
|
||||||
const timeUnitOptions = computed(() =>
|
|
||||||
getDictOptions(DICT_TYPE.MES_TIME_UNIT_TYPE),
|
|
||||||
);
|
|
||||||
const getTitle = computed(() =>
|
const getTitle = computed(() =>
|
||||||
isUpdate.value
|
formData.value?.id
|
||||||
? $t('ui.actionTitle.edit', ['工艺路线产品'])
|
? $t('ui.actionTitle.edit', ['工艺路线产品'])
|
||||||
: $t('ui.actionTitle.create', ['工艺路线产品']),
|
: $t('ui.actionTitle.create', ['工艺路线产品']),
|
||||||
);
|
);
|
||||||
|
|
||||||
function resetForm(routeId?: number) {
|
/** MdItemSelect change 回调:把物料编码/名称/规格/单位回填到 form */
|
||||||
Object.assign(formData, {
|
async function handleItemChange(item?: any) {
|
||||||
id: undefined,
|
if (!item) {
|
||||||
itemCode: undefined,
|
return;
|
||||||
itemId: undefined,
|
}
|
||||||
itemName: undefined,
|
await formApi.setValues({
|
||||||
productionTime: 1,
|
itemCode: item.code,
|
||||||
quantity: 1,
|
itemName: item.name,
|
||||||
remark: undefined,
|
specification: item.specification,
|
||||||
routeId: routeId ?? formData.routeId,
|
unitName: item.unitMeasureName,
|
||||||
specification: undefined,
|
|
||||||
timeUnitType: 'MINUTE',
|
|
||||||
unitName: undefined,
|
|
||||||
});
|
});
|
||||||
formRef.value?.clearValidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: { class: 'w-full' },
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
labelWidth: 100,
|
||||||
|
},
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: useRouteProductFormSchema(handleItemChange),
|
||||||
|
showDefaultActions: false,
|
||||||
|
wrapperClass: 'grid-cols-2',
|
||||||
|
});
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
try {
|
const { valid } = await formApi.validate();
|
||||||
await formRef.value?.validate();
|
if (!valid) {
|
||||||
} catch {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
|
const data =
|
||||||
|
(await formApi.getValues()) as MesProRouteProductApi.RouteProduct;
|
||||||
try {
|
try {
|
||||||
if (isUpdate.value) {
|
if (formData.value?.id) {
|
||||||
await updateRouteProduct(formData);
|
await updateRouteProduct(data);
|
||||||
|
// 用最新表单值同步 formData,确保产品 BOM 子表绑定的 itemId 与表单一致
|
||||||
|
formData.value = { ...formData.value, ...data };
|
||||||
} else {
|
} else {
|
||||||
const id = await createRouteProduct(formData);
|
// 新增成功后切换到编辑模式,方便继续维护产品 BOM
|
||||||
formData.id = id;
|
const id = await createRouteProduct(data);
|
||||||
isUpdate.value = true;
|
formData.value = { ...data, id };
|
||||||
|
await formApi.setFieldValue('id', id);
|
||||||
}
|
}
|
||||||
emit('success');
|
emit('success');
|
||||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
|
@ -89,102 +78,41 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
resetForm();
|
formData.value = undefined;
|
||||||
isUpdate.value = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await formApi.resetForm();
|
||||||
|
// 加载数据
|
||||||
const data = modalApi.getData<{
|
const data = modalApi.getData<{
|
||||||
id?: number;
|
id?: number;
|
||||||
routeId: number;
|
routeId: number;
|
||||||
row?: MesProRouteProductApi.RouteProduct;
|
|
||||||
}>();
|
}>();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.row) {
|
if (!data.id) {
|
||||||
Object.assign(formData, data.row);
|
await formApi.setValues({ routeId: data.routeId });
|
||||||
isUpdate.value = true;
|
return;
|
||||||
} else {
|
}
|
||||||
resetForm(data.routeId);
|
modalApi.lock();
|
||||||
isUpdate.value = false;
|
try {
|
||||||
|
formData.value = await getRouteProduct(data.id);
|
||||||
|
// 设置到 values
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleItemChange(item?: any) {
|
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
formData.itemCode = item.code;
|
|
||||||
formData.itemName = item.name;
|
|
||||||
formData.specification = item.specification;
|
|
||||||
formData.unitName = item.unitName;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal :title="getTitle" class="w-3/5">
|
<Modal :title="getTitle" class="w-3/5">
|
||||||
<ElForm
|
<Form class="mx-4" />
|
||||||
ref="formRef"
|
<!-- 编辑模式下展示产品 BOM 子表,新增模式下隐藏 -->
|
||||||
class="mx-4"
|
<template v-if="formData?.id && formData?.itemId">
|
||||||
label-width="120px"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
>
|
|
||||||
<div class="grid grid-cols-2 gap-x-4">
|
|
||||||
<ElFormItem class="col-span-2" label="产品" prop="itemId">
|
|
||||||
<MdItemSelect
|
|
||||||
v-model="formData.itemId"
|
|
||||||
@change="handleItemChange"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="生产数量" prop="quantity">
|
|
||||||
<ElInputNumber
|
|
||||||
v-model="formData.quantity"
|
|
||||||
class="!w-full"
|
|
||||||
controls-position="right"
|
|
||||||
:min="1"
|
|
||||||
:precision="0"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="生产用时" prop="productionTime">
|
|
||||||
<ElInputNumber
|
|
||||||
v-model="formData.productionTime"
|
|
||||||
class="!w-full"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="时间单位" prop="timeUnitType">
|
|
||||||
<ElSelect
|
|
||||||
v-model="formData.timeUnitType"
|
|
||||||
class="!w-full"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择"
|
|
||||||
>
|
|
||||||
<ElOption
|
|
||||||
v-for="dict in timeUnitOptions"
|
|
||||||
:key="dict.value as string"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value as string"
|
|
||||||
/>
|
|
||||||
</ElSelect>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem class="col-span-2" label="备注" prop="remark">
|
|
||||||
<ElInput
|
|
||||||
v-model="formData.remark"
|
|
||||||
:maxlength="250"
|
|
||||||
placeholder="请输入备注"
|
|
||||||
:rows="2"
|
|
||||||
type="textarea"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
</div>
|
|
||||||
</ElForm>
|
|
||||||
<template v-if="isUpdate && formData.id && formData.itemId">
|
|
||||||
<ElDivider class="!my-3" content-position="left">产品 BOM 配置</ElDivider>
|
<ElDivider class="!my-3" content-position="left">产品 BOM 配置</ElDivider>
|
||||||
<div class="mx-4">
|
<div class="mx-4">
|
||||||
<ProductBomList
|
<ProductBomList
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ const props = defineProps<{
|
||||||
routeId: number;
|
routeId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isEditable = ref(props.formMode !== 'detail');
|
const isEditable = ref(props.formMode !== 'detail'); // 是否可编辑
|
||||||
const list = ref<MesProRouteProductApi.RouteProduct[]>([]);
|
const list = ref<MesProRouteProductApi.RouteProduct[]>([]); // 工艺路线产品列表
|
||||||
|
|
||||||
const [ProductFormModal, productFormModalApi] = useVbenModal({
|
const [ProductFormModal, productFormModalApi] = useVbenModal({
|
||||||
connectedComponent: ProductForm,
|
connectedComponent: ProductForm,
|
||||||
|
|
@ -47,6 +47,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
} as VxeTableGridOptions<MesProRouteProductApi.RouteProduct>,
|
} as VxeTableGridOptions<MesProRouteProductApi.RouteProduct>,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 加载工艺路线产品列表 */
|
||||||
async function getList() {
|
async function getList() {
|
||||||
gridApi.setLoading(true);
|
gridApi.setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -57,16 +58,17 @@ async function getList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 新增工艺路线产品 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
productFormModalApi.setData({ routeId: props.routeId }).open();
|
productFormModalApi.setData({ routeId: props.routeId }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 编辑工艺路线产品 */
|
||||||
function handleEdit(row: MesProRouteProductApi.RouteProduct) {
|
function handleEdit(row: MesProRouteProductApi.RouteProduct) {
|
||||||
productFormModalApi
|
productFormModalApi.setData({ id: row.id, routeId: props.routeId }).open();
|
||||||
.setData({ id: row.id, routeId: props.routeId, row })
|
|
||||||
.open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除工艺路线产品 */
|
||||||
async function handleDelete(row: MesProRouteProductApi.RouteProduct) {
|
async function handleDelete(row: MesProRouteProductApi.RouteProduct) {
|
||||||
await deleteRouteProduct(row.id!);
|
await deleteRouteProduct(row.id!);
|
||||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['工艺路线产品']));
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['工艺路线产品']));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue