Files
erp-frontend/src/views/production/finished-product-shipment/FinishedProductShipmentView.vue

438 lines
16 KiB
Vue

<script lang="ts" setup>
import DefaultToolButton from "@/components/base/default-tool-button/DefaultToolButton.vue";
import DefaultOperateButtonColumn from "@/components/base/default-column/DefaultOperateButtonColumn.vue";
import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultStatusSwitchColumn.vue";
import { usePage } from "@/composables/use-page";
import BaseFormWithTable from "@/components/base/base-form-with-table/BaseFormWithTable.vue";
import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue";
import { $t } from "@/common/languages";
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
import { formatDate } from "@/common/utils/format-utils";
import BaseSelect from "@/components/base/base-select/BaseSelect.vue";
import { generateDucumentNo } from "@/common/utils/document-no-generator/document-no-generator";
import { get, post } from "@/common/http/request";
import { useStatus } from "@/common/languages/mapping/base-info-mapping";
import ExpandablePageableTable from "@/components/base/expandable-pageable-table/ExpandablePageableTable.vue";
import { type FieldMappingConfig } from "@/components/base/base-form-with-table/type";
import { watch } from "vue";
/**
* 必须要的变量
*/
const getPageUrl = "/production/finishedproductshipment/getFinishedProductShipmentPage";
const addUrl = "/production/finishedproductshipment/addFinishedProductShipment";
const editUrl = "/production/finishedproductshipment/updateFinishedProductShipment";
const removeUrl = "/production/finishedproductshipment/deleteFinishedProductShipment";
const approveUrl = "/production/finishedproductshipment/approveFinishedProductShipment";
const rejectUrl = "/production/finishedproductshipment/rejectFinishedProductShipment";
const getDetailUrl = "/production/finishedproductshipment/getFinishedProductShipmentDetail";
const getWarehouseSelectListUrl = "/warehouse/warehouse/getWarehouseSelectList";
const itemArrayName = "shipmentItems";
const searchers = [
{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.production.finishedproductshipment.formCode") },
{ name: "formName", type: "text" as const, placeholder: $t("_prop.production.finishedproductshipment.formName") },
];
const rules = reactive<FormRules>({
formCode: [
{ required: true, message: $t("_message.production.finishedproductshipment.input_formCode"), trigger: "blur" },
],
formName: [
{ required: true, message: $t("_message.production.finishedproductshipment.input_formName"), trigger: "blur" },
],
storeNo: [
{ required: true, message: $t("_message.production.finishedproductshipment.select_storeId"), trigger: "blur" },
],
outStockType: [
{ required: true, message: $t("_message.production.finishedproductshipment.select_outStockType"), trigger: "blur" },
],
shipmentItems: [
{
required: true,
validator: (rule, value, callback) => {
if (value === undefined || value.length === 0) {
callback($t("_message.production.finishedproductshipment.no_shipment_items"));
}
callback();
},
trigger: "change",
},
],
});
const outStockTypeOptions = [
{ label: $t("_enum.outStockType.material"), value: 1 },
{ label: $t("_enum.outStockType.finishedProduct"), value: 2 },
];
const mappingConfig: FieldMappingConfig = {
partNumber: {
sourceKey: "物料编号",
defaultValue: "",
},
productCount: {
sourceKey: "数量",
defaultValue: 0,
transform: (val: any) => {
const num = Number(val);
return isNaN(num) ? 0 : num;
},
},
productMark: {
sourceKey: "备注",
defaultValue: "",
},
};
/**
* 基本不变通用变量
*/
const tableRef = ref<InstanceType<typeof ExpandablePageableTable> | null>(null);
const { useAdd, useEdit, useRemove, useGeneralPageRef } = usePage(tableRef);
const { title, visible, formType, form, itemVisible, itemParentId } = useGeneralPageRef();
const warehouseSelectRef = ref<InstanceType<typeof BaseSelect>>();
const { getFormStatusLabel } = useStatus();
/**
* 根据 formType 获取出库类型标签
* formType = 4 (WAREHOUSE_ISSUE) -> 物料出库
* formType = 7 (FINISHED_PRODUCT_SHIPMENT) -> 成品出库
*/
const getOutStockTypeLabelByFormType = (formTypeValue: number) => {
if (formTypeValue === 4) {
return outStockTypeOptions.find(opt => opt.value === 1)?.label || "";
}
if (formTypeValue === 7) {
return outStockTypeOptions.find(opt => opt.value === 2)?.label || "";
}
return "";
};
/**
* 可以自定义的变量
*/
const add = () => {
form.value = {};
title.value = "_title.production.finishedproductshipment.add";
visible.value = true;
formType.value = false;
// 默认生成成品出库单号
form.value["formCode"] = generateDucumentNo("CPCK");
};
// 监听出库类型变化,动态生成单号
watch(
() => form.value?.outStockType,
newVal => {
if (newVal === 1) {
// 物料出库 - 使用 WarehouseIssue (4) 的单号前缀
form.value["formCode"] = generateDucumentNo("CK");
} else if (newVal === 2) {
// 成品出库 - 使用 FinishedProductShipment (7) 的单号前缀
form.value["formCode"] = generateDucumentNo("CPCK");
}
}
);
const edit = (row: any) => {
title.value = "_title.production.finishedproductshipment.edit";
form.value = { ...row };
visible.value = true;
formType.value = true;
};
const remove = (row: any) => {
useRemove(removeUrl, row.id, "_message.production.finishedproductshipment.delete_message");
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
const approve = (row: any) => {
ElMessageBox.confirm($t("_message.production.finishedproductshipment.approve_confirm"), $t("_level.warning"), {
confirmButtonText: $t("_button.confirm"),
cancelButtonText: $t("_button.cancel"),
type: "warning",
}).then(async () => {
try {
await post(approveUrl, { id: row.id });
ElMessage.success($t("_message.production.finishedproductshipment.approve_success"));
tableRef.value?.reload();
} catch (err: any) {
ElMessage.error($t("_message.production.finishedproductshipment.approve_fail") + err);
}
});
};
const reject = (row: any) => {
ElMessageBox.confirm($t("_message.production.finishedproductshipment.reject_confirm"), $t("_level.warning"), {
confirmButtonText: $t("_button.confirm"),
cancelButtonText: $t("_button.cancel"),
type: "warning",
}).then(async () => {
try {
await post(rejectUrl, { id: row.id });
ElMessage.success($t("_message.production.finishedproductshipment.reject_success"));
tableRef.value?.reload();
} catch (err: any) {
ElMessage.error($t("_message.production.finishedproductshipment.reject_fail") + err);
}
});
};
const showItem = (row: any) => {
itemParentId.value = row.id;
itemVisible.value = true;
};
const submit = (form: any, formRef: FormInstance | undefined) => {
setName();
if (formRef !== undefined) {
formRef.validate(valid => {
if (valid) {
if (formType.value) useEdit(editUrl, form, visible);
else useAdd(addUrl, form, visible);
}
});
}
};
const topButtonClick = (eventName: string) => {
switch (eventName) {
case "add":
add();
break;
}
};
const operateButtonClick = (eventName: string, row: any) => {
switch (eventName) {
case "edit":
edit(row);
break;
case "remove":
remove(row);
break;
case "approve":
approve(row);
break;
case "reject":
reject(row);
break;
case "showItem":
showItem(row);
break;
}
};
const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
// 已审核(formStatus=1)时不显示审核按钮,显示反审按钮
// 未审核(formStatus=0)时显示审核按钮,不显示反审按钮
if (row.formStatus === 0 && button.eventName === "reject") return false;
if (row.formStatus === 1 && button.eventName === "approve") return false;
// 已审核后不能编辑和删除
if (row.formStatus === 1 && (button.eventName === "edit" || button.eventName === "remove")) return false;
return true;
};
const setName = () => {
form.value["storeName"] = warehouseSelectRef.value?.getLabel();
};
const handleReset = () => {
form.value["formCode"] = generateDucumentNo("CPCK");
};
</script>
<template>
<ExpandablePageableTable
:url="getPageUrl"
:searchers="searchers"
ref="tableRef"
:item-url="getDetailUrl"
item-id-key="id"
item-id-name="id"
itemFieldName="shipmentItems"
>
<template #tool-button>
<DefaultToolButton @top-button-click="topButtonClick" />
</template>
<template #columns>
<el-table-column :label="$t('_prop.production.finishedproductshipment.formCode')" prop="formCode" />
<el-table-column :label="$t('_prop.production.finishedproductshipment.storeName')" prop="storeName" />
<el-table-column :label="$t('_prop.production.finishedproductshipment.formName')" prop="formName" />
<el-table-column :label="$t('_prop.production.finishedproductshipment.outStockType')" prop="formType">
<template #default="{ row }">
{{ getOutStockTypeLabelByFormType(row.formType) }}
</template>
</el-table-column>
<el-table-column :label="$t('_prop.production.finishedproductshipment.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">
<el-table :data="itemData" size="small" border stripe>
<el-table-column
:label="$t('_prop.production.finishedproductshipment.partNumber')"
prop="partNumber"
width="150"
/>
<el-table-column
:label="$t('_prop.production.finishedproductshipment.productSpecs')"
prop="productSpecs"
width="200"
/>
<el-table-column :label="$t('_prop.production.finishedproductshipment.productCount')" prop="productCount" />
<el-table-column :label="$t('_prop.production.finishedproductshipment.productMark')" prop="productMark" />
</el-table>
</template>
</ExpandablePageableTable>
<BaseFormWithTable
v-model:visible="visible"
@submit="submit"
v-model:form="form"
:title="$t(title)"
:rules="rules"
:base-title="$t('_title.production.finishedproductshipment.baseTitle')"
:table-title="$t('_title.production.finishedproductshipment.tableTitle')"
:item-array-name="itemArrayName"
upload-desc="物料信息"
:mapping-config="mappingConfig"
@reset="handleReset"
>
<template #form-items>
<el-form-item prop="id" v-if="false"><el-input v-model="form.id" /></el-form-item>
<el-form-item prop="storeName" v-if="false"><el-input v-model="form.storeName" /></el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="$t('_prop.production.finishedproductshipment.formCode')" prop="formCode">
<el-input
v-model="form.formCode"
:placeholder="$t('_message.production.finishedproductshipment.input_formCode')"
disabled
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('_prop.production.finishedproductshipment.formName')" prop="formName">
<el-input
v-model="form.formName"
:placeholder="$t('_message.production.finishedproductshipment.input_formName')"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="$t('_prop.production.finishedproductshipment.storeNo')" prop="storeNo">
<BaseSelect v-model="form.storeNo" :url="getWarehouseSelectListUrl" ref="warehouseSelectRef" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('_prop.production.finishedproductshipment.outStockType')" prop="outStockType">
<el-select
v-model="form.outStockType"
:placeholder="$t('_message.production.finishedproductshipment.select_outStockType')"
>
<el-option
v-for="item in outStockTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('_prop.production.finishedproductshipment.formMark')" prop="formMark">
<el-input
v-model="form.formMark"
:placeholder="$t('_message.production.finishedproductshipment.input_formMark')"
type="textarea"
autosize
/>
</el-form-item>
</template>
<template #form-table-columns>
<el-table-column :label="$t('_prop.production.finishedproductshipment.partNumber')" width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${itemArrayName}.${$index}.partNumber`">
<el-input
v-model="row.partNumber"
size="small"
:placeholder="$t('_message.production.finishedproductshipment.input_partNumber')"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="$t('_prop.production.finishedproductshipment.productSpecs')" width="200">
<template #default="{ row }">
<span>{{ row.productSpecs }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('_prop.production.finishedproductshipment.productCount')" width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${itemArrayName}.${$index}.productCount`">
<el-input-number v-model="row.productCount" size="small" :min="1" />
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="$t('_prop.production.finishedproductshipment.productMark')" width="200">
<template #default="{ row, $index }">
<el-form-item :prop="`${itemArrayName}.${$index}.productMark`">
<el-input
v-model="row.productMark"
size="small"
:placeholder="$t('_message.production.finishedproductshipment.input_productMark')"
/>
</el-form-item>
</template>
</el-table-column>
</template>
</BaseFormWithTable>
<BaseItemDialog
:title="$t('_title.production.finishedproductshipment.showItem')"
v-model:visible="itemVisible"
:url="getDetailUrl"
parent-param-name="id"
v-model:parent-param-value="itemParentId"
>
<template #columns>
<el-table-column
:label="$t('_prop.production.finishedproductshipment.partNumber')"
prop="partNumber"
width="150"
/>
<el-table-column
:label="$t('_prop.production.finishedproductshipment.productSpecs')"
prop="productSpecs"
width="200"
/>
<el-table-column :label="$t('_prop.production.finishedproductshipment.productCount')" prop="productCount" />
<el-table-column :label="$t('_prop.production.finishedproductshipment.productMark')" prop="productMark" />
</template>
</BaseItemDialog>
</template>