fix: 第一版错误修改后的存档。

This commit is contained in:
c
2026-03-17 15:23:31 +08:00
parent 0c4e4679b3
commit 88aef44583
17 changed files with 283 additions and 67 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Teek Design Vue3</title> <title>牛安管理系统</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@@ -90,7 +90,7 @@ export default {
remark: "Remark", remark: "Remark",
startdate: "Start Date", startdate: "Start Date",
enddate: "End Date", enddate: "End Date",
searchCode: "Doc No/Name/Remark", searchCode: "Doc No / Remark",
}, },
warehouse: { warehouse: {
warehouse: { warehouse: {
@@ -233,6 +233,7 @@ export default {
productCount: "Count", productCount: "Count",
productMark: "Remark", productMark: "Remark",
createDate: "Create Date", createDate: "Create Date",
searchCode: "Form Code/Name/Remark",
}, },
finishedproductreceipt: { finishedproductreceipt: {
totalValue: "Receipt Count", totalValue: "Receipt Count",
@@ -255,6 +256,7 @@ export default {
remark: "Remark", remark: "Remark",
createDate: "Create Date", createDate: "Create Date",
keyAccountId: "Customer", keyAccountId: "Customer",
searchCode: "Form Code/Name/Remark",
}, },
}, },
purchase: { purchase: {
@@ -268,6 +270,7 @@ export default {
baseTitle: "Purchase Plan Basic Info", baseTitle: "Purchase Plan Basic Info",
tableTitle: "Purchase Details", tableTitle: "Purchase Details",
generateOrder: "Generate Purchase Order", generateOrder: "Generate Purchase Order",
showItem: "Purchase Details",
model: "Model", model: "Model",
defaultVendor: "Default Vendor", defaultVendor: "Default Vendor",
demandQuantity: "Demand Qty", demandQuantity: "Demand Qty",
@@ -276,6 +279,7 @@ export default {
unitPrice: "Unit Price", unitPrice: "Unit Price",
totalPrice: "Total Price", totalPrice: "Total Price",
purchaseStatus: "Purchase Status", purchaseStatus: "Purchase Status",
searchCode: "Plan No/Name/Remark",
}, },
purchase_plan_item: { purchase_plan_item: {
partNumber: "Part Number", partNumber: "Part Number",
@@ -343,6 +347,7 @@ export default {
sendCount: "Out Count", sendCount: "Out Count",
surplusCount: "Remaining", surplusCount: "Remaining",
saleMark: "Remark", saleMark: "Remark",
searchCode: "Form Code/Name/Remark",
}, },
devicesn: { devicesn: {
softVersion: "Software Version", softVersion: "Software Version",
@@ -691,6 +696,10 @@ export default {
outstock_success: "Outstock success", outstock_success: "Outstock success",
outstock_fail: "Outstock failed", outstock_fail: "Outstock failed",
input_outstock_mark: "Please enter outstock remark", input_outstock_mark: "Please enter outstock remark",
duplicate_productSn: "Duplicate SN: {value}",
duplicate_mac: "Duplicate MAC Address: {value}",
duplicate_serialNum: "Duplicate Serial Number: {value}",
validate_failed: "Device validation failed",
}, },
}, },
purchase: { purchase: {
@@ -715,6 +724,7 @@ export default {
}, },
purchaseorder: { purchaseorder: {
delete_message: "Delete Order", delete_message: "Delete Order",
part_number_not_exists: "Part number does not exist",
}, },
finishedproductshipment: { finishedproductshipment: {
select_storeId: "Please select store", select_storeId: "Please select store",
@@ -819,6 +829,7 @@ export default {
stocktransferorder: { stocktransferorder: {
add: "Add Transfer", add: "Add Transfer",
edit: "Edit Transfer", edit: "Edit Transfer",
stock_check_failed: "Stock check failed",
}, },
warehousereceipt: { warehousereceipt: {
add: "Add Receipt", add: "Add Receipt",
@@ -845,6 +856,10 @@ export default {
}, },
production_issue: { production_issue: {
returnDialog: "Return Edit", returnDialog: "Return Edit",
showItem: "Issue Details",
},
production_return: {
showItem: "Return Details",
}, },
finishedproductreceipt: { finishedproductreceipt: {
add: "Add Receipt", add: "Add Receipt",

View File

@@ -90,7 +90,7 @@ export default {
remark: "备注", remark: "备注",
startdate: "开始时间", startdate: "开始时间",
enddate: "结束时间", enddate: "结束时间",
searchCode: "单据编号/名称/备注", searchCode: "单据编号 / 备注",
}, },
warehouse: { warehouse: {
warehouse: { warehouse: {
@@ -233,6 +233,7 @@ export default {
productCount: "数量", productCount: "数量",
productMark: "备注", productMark: "备注",
createDate: "创建时间", createDate: "创建时间",
searchCode: "单据编号/名称/备注",
}, },
finishedproductreceipt: { finishedproductreceipt: {
totalValue: "入库数量", totalValue: "入库数量",
@@ -255,6 +256,7 @@ export default {
remark: "备注", remark: "备注",
createDate: "创建时间", createDate: "创建时间",
keyAccountId: "客户", keyAccountId: "客户",
searchCode: "单据编号/名称/备注",
}, },
}, },
purchase: { purchase: {
@@ -268,6 +270,7 @@ export default {
baseTitle: "采购计划基本信息", baseTitle: "采购计划基本信息",
tableTitle: "采购明细", tableTitle: "采购明细",
generateOrder: "生成采购订单", generateOrder: "生成采购订单",
showItem: "采购明细",
model: "型号", model: "型号",
defaultVendor: "默认供应商", defaultVendor: "默认供应商",
demandQuantity: "需求量", demandQuantity: "需求量",
@@ -276,14 +279,15 @@ export default {
unitPrice: "单价", unitPrice: "单价",
totalPrice: "总价", totalPrice: "总价",
purchaseStatus: "采购状态", purchaseStatus: "采购状态",
searchCode: "计划号/名称/备注",
}, },
purchase_plan_item: { purchase_plan_item: {
partNumber: "商品编号", partNumber: "物料编号",
purchaseCount: "计划数量", purchaseCount: "计划数量",
completeCount: "已完成数量", completeCount: "已完成数量",
price: "单价", price: "单价",
currentCount: "本次采购数量", currentCount: "本次采购数量",
input_partNumber: "请输入商品编号", input_partNumber: "请输入物料编号",
input_purchaseCount: "请输入计划数量", input_purchaseCount: "请输入计划数量",
input_price: "请输入单价", input_price: "请输入单价",
input_currentCount: "请输入本次采购数量", input_currentCount: "请输入本次采购数量",
@@ -343,6 +347,7 @@ export default {
sendCount: "出库数量", sendCount: "出库数量",
surplusCount: "剩余量", surplusCount: "剩余量",
saleMark: "备注", saleMark: "备注",
searchCode: "单据编号/名称/备注",
}, },
devicesn: { devicesn: {
softVersion: "软件版本", softVersion: "软件版本",
@@ -699,6 +704,10 @@ export default {
outstock_success: "出货成功", outstock_success: "出货成功",
outstock_fail: "出货失败", outstock_fail: "出货失败",
input_outstock_mark: "请输入出货备注", input_outstock_mark: "请输入出货备注",
duplicate_productSn: "SN码重复{value}",
duplicate_mac: "MAC地址重复{value}",
duplicate_serialNum: "序列号重复:{value}",
validate_failed: "验证设备失败",
}, },
finishedproductshipment: { finishedproductshipment: {
select_storeId: "请选择仓库", select_storeId: "请选择仓库",
@@ -763,6 +772,7 @@ export default {
input_partNumber: "请输入物料编号", input_partNumber: "请输入物料编号",
input_purchaseCount: "请输入采购数量", input_purchaseCount: "请输入采购数量",
input_price: "请输入单价", input_price: "请输入单价",
part_number_not_exists: "物料编号不存在",
}, },
finishedproductshipment: { finishedproductshipment: {
select_storeId: "请选择仓库", select_storeId: "请选择仓库",
@@ -917,6 +927,7 @@ export default {
showItem: "调拨明细", showItem: "调拨明细",
baseTitle: "调拨单基本信息", baseTitle: "调拨单基本信息",
tableTitle: "调拨明细", tableTitle: "调拨明细",
stock_check_failed: "库存检测失败",
}, },
warehousereceipt: { warehousereceipt: {
add: "添加入库单", add: "添加入库单",
@@ -951,6 +962,9 @@ export default {
returnDialog: "退料编辑", returnDialog: "退料编辑",
showItem: "发料明细", showItem: "发料明细",
}, },
production_return: {
showItem: "退料明细",
},
finishedproductreceipt: { finishedproductreceipt: {
add: "添加成品入库单", add: "添加成品入库单",
edit: "编辑成品入库单", edit: "编辑成品入库单",

View File

@@ -82,7 +82,7 @@ export const useStatus = () => {
}; };
const getPurchasePlanItemStatusLabel = (code: number | null): string => { const getPurchasePlanItemStatusLabel = (code: number | null): string => {
if (code === null || code === undefined) return ""; if (code === null || code === undefined) return t("_base_info.purchase.purchase_plan_item.no_start");
const key = purchasePlanItemStatusKeyMap[code]; const key = purchasePlanItemStatusKeyMap[code];
return key ? t(key) : t(unknown); return key ? t(key) : t(unknown);
}; };

View File

@@ -78,7 +78,7 @@ watch(() => parentParamValue.value, loadData);
} }
.item-dialog { .item-dialog {
--el-dialog-width: fit-content !important; --el-dialog-width: 1400px !important;
--el-dialog-padding-primary: 20px; --el-dialog-padding-primary: 20px;
} }

View File

@@ -11,6 +11,7 @@ const props = defineProps({
label: String, label: String,
url: String, url: String,
data: Array<Record<string, string | number>>, data: Array<Record<string, string | number>>,
parse: Function,
}); });
const optionData = ref<OptionData[]>([]); const optionData = ref<OptionData[]>([]);
@@ -37,10 +38,13 @@ defineExpose({
const option = optionData.value.find(item => item.value === model.value); const option = optionData.value.find(item => item.value === model.value);
return option?.label; return option?.label;
}, },
getOptions: () => {
return optionData.value;
},
}); });
</script> </script>
<template> <template>
<el-select :name="name || ''" v-model="model"> <el-select :name="name || ''" v-model="model">
<el-option v-for="o in optionData" :key="o.value" :value="o.value" :label="o.label" /> <el-option v-for="o in parse ? parse(optionData) : optionData" :key="o.value" :value="o.value" :label="o.label" />
</el-select> </el-select>
</template> </template>

View File

@@ -59,11 +59,13 @@ function reset() {
display: flex; display: flex;
padding-right: 20px; padding-right: 20px;
} }
.searcher-box { .searcher-box {
padding-left: 5px;
padding-right: 5px; padding-right: 5px;
padding-left: 5px;
} }
.searcher { .searcher {
width: 285px; width: 400px;
} }
</style> </style>

View File

@@ -32,8 +32,7 @@ const saveOutstockUrl = "/production/finishedproductreceipt/saveOutstock";
const getKeyAccountSelectListUrl = "/sys/keyaccount/getKeyAccountSelectList"; const getKeyAccountSelectListUrl = "/sys/keyaccount/getKeyAccountSelectList";
const itemArrayName = "deviceItems"; const itemArrayName = "deviceItems";
const searchers = [ const searchers = [
{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.production.finishedproductreceipt.formCode") }, { name: "searchCode", type: "text" as const, placeholder: $t("_prop.production.finishedproductreceipt.searchCode") },
{ name: "formName", type: "text" as const, placeholder: $t("_prop.production.finishedproductreceipt.formName") },
]; ];
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
formCode: [ formCode: [
@@ -58,6 +57,56 @@ const rules = reactive<FormRules>({
}, },
], ],
}); });
const validateDeviceDuplicates = (deviceItems: any[]): string | null => {
const productSns = new Set<string>();
const macs = new Set<string>();
const serialNums = new Set<string>();
for (const item of deviceItems) {
if (item.productSn) {
if (productSns.has(item.productSn)) {
return $t("_message.production.finishedproductreceipt.duplicate_productSn", { value: item.productSn });
}
productSns.add(item.productSn);
}
if (item.mac) {
if (macs.has(item.mac)) {
return $t("_message.production.finishedproductreceipt.duplicate_mac", { value: item.mac });
}
macs.add(item.mac);
}
if (item.serialNum) {
if (serialNums.has(item.serialNum)) {
return $t("_message.production.finishedproductreceipt.duplicate_serialNum", { value: item.serialNum });
}
serialNums.add(item.serialNum);
}
}
return null;
};
const validateDevicesWithBackend = async (deviceItems: any[]): Promise<string | null> => {
const validateUrl = "/sale/device/validateDevices";
const devices = deviceItems.map((item: any) => ({
productSn: item.productSn || "",
mac: item.mac || "",
serialNum: item.serialNum || "",
}));
try {
const res = await post(validateUrl, { devices });
if (res.code === 0 && res.data) {
if (!res.data.valid) {
return res.data.message;
}
}
return null;
} catch (error) {
console.error("验证设备失败:", error);
return $t("_message.production.finishedproductreceipt.validate_failed");
}
};
/** /**
* 基本不变通用变量 * 基本不变通用变量
*/ */
@@ -160,7 +209,7 @@ const mappingConfig: FieldMappingConfig = {
const add = () => { const add = () => {
form.value = {}; form.value = {};
title.value = "_title.production.finishedproductreceipt.add"; title.value = "_title.production.finishedproductreceipt.add";
form.value["formCode"] = generateDucumentNo("CPRK"); form.value["formCode"] = generateDucumentNo("RK");
visible.value = true; visible.value = true;
formType.value = false; formType.value = false;
}; };
@@ -258,13 +307,26 @@ const showItem = (row: any) => {
itemParentId.value = row.id; itemParentId.value = row.id;
itemVisible.value = true; itemVisible.value = true;
}; };
const submit = (form: any, formRef: FormInstance | undefined) => { const submit = async (formData: any, formRef: FormInstance | undefined) => {
setName(); setName();
if (formRef !== undefined) { if (formRef !== undefined) {
const deviceItems = formData.deviceItems || [];
const localDuplicateError = validateDeviceDuplicates(deviceItems);
if (localDuplicateError) {
ElMessage.error(localDuplicateError);
return;
}
const backendDuplicateError = await validateDevicesWithBackend(deviceItems);
if (backendDuplicateError) {
ElMessage.error(backendDuplicateError);
return;
}
formRef.validate(valid => { formRef.validate(valid => {
if (valid) { if (valid) {
if (formType.value) useEdit(editUrl, form, visible); if (formType.value) useEdit(editUrl, formData, visible);
else useAdd(addUrl, form, visible); else useAdd(addUrl, formData, visible);
} }
}); });
} }
@@ -304,7 +366,7 @@ const setName = () => {
form.value["storeName"] = warehouseSelectRef.value?.getLabel(); form.value["storeName"] = warehouseSelectRef.value?.getLabel();
}; };
const handleReset = () => { const handleReset = () => {
form.value["formCode"] = generateDucumentNo("CPRK"); form.value["formCode"] = generateDucumentNo("RK");
}; };
</script> </script>
<template> <template>

View File

@@ -30,8 +30,7 @@ const getDetailUrl = "/production/finishedproductshipment/getFinishedProductShip
const getWarehouseSelectListUrl = "/warehouse/warehouse/getWarehouseSelectList"; const getWarehouseSelectListUrl = "/warehouse/warehouse/getWarehouseSelectList";
const itemArrayName = "shipmentItems"; const itemArrayName = "shipmentItems";
const searchers = [ const searchers = [
{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.production.finishedproductshipment.formCode") }, { name: "searchCode", type: "text" as const, placeholder: $t("_prop.production.finishedproductshipment.searchCode") },
{ name: "formName", type: "text" as const, placeholder: $t("_prop.production.finishedproductshipment.formName") },
]; ];
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
formCode: [ formCode: [
@@ -240,12 +239,12 @@ const operateButtonClick = (eventName: string, row: any) => {
}; };
const authShowFunc = (row: any, button: globalThis.ButtonProp) => { const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
// 已审核(formStatus=1)时不显示审核按钮,显示反审按钮 // 已审核(status=1)时不显示审核按钮,显示反审按钮
// 未审核(formStatus=0)时显示审核按钮,不显示反审按钮 // 未审核(status=0)时显示审核按钮,不显示反审按钮
if (row.formStatus === 0 && button.eventName === "reject") return false; if (row.status === 0 && button.eventName === "reject") return false;
if (row.formStatus === 1 && button.eventName === "approve") return false; if (row.status === 1 && button.eventName === "approve") return false;
// 已审核后不能编辑和删除 // 已审核后不能编辑和删除
if (row.formStatus === 1 && (button.eventName === "edit" || button.eventName === "remove")) return false; if (row.status === 1 && (button.eventName === "edit" || button.eventName === "remove")) return false;
return true; return true;
}; };
@@ -282,7 +281,7 @@ const handleReset = () => {
<el-table-column :label="$t('_prop.production.finishedproductshipment.formMark')" prop="formMark" /> <el-table-column :label="$t('_prop.production.finishedproductshipment.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" /> <el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn <DefaultStatusSwitchColumn
status-param-name="formStatus" status-param-name="status"
:status-label-mapping="getFormStatusLabel" :status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType" :tag-type-mapping="getFormStatusTagType"
/> />

View File

@@ -24,6 +24,7 @@ const getItemUrl = "/production/productionissue/getProductionIssueItemList";
const approveUrl = "/production/productionissue/approvingProductionIssue"; const approveUrl = "/production/productionissue/approvingProductionIssue";
const rejectUrl = "/production/productionissue/rejectProductionIssue"; const rejectUrl = "/production/productionissue/rejectProductionIssue";
const generateProductionReturnUrl = "/production/productionissue/productionReturn"; const generateProductionReturnUrl = "/production/productionissue/productionReturn";
const searchers = [{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.common.searchCode") }];
/** /**
* 基本不变通用变量 * 基本不变通用变量
*/ */
@@ -178,6 +179,7 @@ const generateProductionReturn = async () => {
<ExpandablePageableTable <ExpandablePageableTable
:url="getPageUrl" :url="getPageUrl"
ref="tableRef" ref="tableRef"
:searchers="searchers"
:item-url="getItemUrl" :item-url="getItemUrl"
item-id-key="id" item-id-key="id"
item-id-name="issueId" item-id-name="issueId"

View File

@@ -65,6 +65,7 @@ const dialogTableData = ref([]);
const dialogSelectable = ref(false); const dialogSelectable = ref(false);
const transferWarehouse = ref(); const transferWarehouse = ref();
const issueDialogVisible = ref(false); const issueDialogVisible = ref(false);
const productionPlanStoreNo = ref();
/** /**
* 可以自定义的变量 * 可以自定义的变量
*/ */
@@ -140,6 +141,8 @@ const viewMaterialShortage = async () => {
const rows = validateSelection(true); // 需要校验状态为 0 const rows = validateSelection(true); // 需要校验状态为 0
if (!rows) return; if (!rows) return;
productionPlanStoreNo.value = rows[0].storeNo;
const params = new URLSearchParams(); const params = new URLSearchParams();
rows.forEach(({ id }) => params.append("ids", id)); rows.forEach(({ id }) => params.append("ids", id));
@@ -514,7 +517,12 @@ const handleReset = () => {
<template #attachment="{ checkPermission }"> <template #attachment="{ checkPermission }">
<div v-if="checkPermission('generateTransferOrder')" style="display: flex; align-items: center"> <div v-if="checkPermission('generateTransferOrder')" style="display: flex; align-items: center">
<span style="width: 200px; padding-right: 20px">{{ $t("_prop.production.production_plan.outStoreNo") }}</span> <span style="width: 200px; padding-right: 20px">{{ $t("_prop.production.production_plan.outStoreNo") }}</span>
<BaseSelect v-model="transferWarehouse" :url="getWarehouseSelectListUrl" ref="transferWarehouseSelectRef" /> <BaseSelect
v-model="transferWarehouse"
:url="getWarehouseSelectListUrl"
:parse="(options: any) => options.filter((w: any) => w.value !== productionPlanStoreNo)"
ref="transferWarehouseSelectRef"
/>
</div> </div>
</template> </template>
</BaseTableForm> </BaseTableForm>

View File

@@ -18,6 +18,7 @@ const getPageUrl = "/production/productionreturn/getProductionReturnPage";
const getItemUrl = "/production/productionreturn/getProductionReturnItemList"; const getItemUrl = "/production/productionreturn/getProductionReturnItemList";
const approveUrl = "/production/productionreturn/approvingProductionReturn"; const approveUrl = "/production/productionreturn/approvingProductionReturn";
const rejectUrl = "/production/productionreturn/rejectProductionReturn"; const rejectUrl = "/production/productionreturn/rejectProductionReturn";
const searchers = [{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.common.searchCode") }];
/** /**
* 基本不变通用变量 * 基本不变通用变量
*/ */
@@ -79,6 +80,7 @@ const getFormStatusTagType = (code: number | null): string => {
<ExpandablePageableTable <ExpandablePageableTable
:url="getPageUrl" :url="getPageUrl"
ref="tableRef" ref="tableRef"
:searchers="searchers"
:item-url="getItemUrl" :item-url="getItemUrl"
item-id-key="id" item-id-key="id"
item-id-name="returnId" item-id-name="returnId"

View File

@@ -16,6 +16,8 @@ import type { FieldMappingConfig } from "@/components/base/base-form-with-table/
import { ref } from "vue"; import { ref } from "vue";
import { useStatus } from "@/common/languages/mapping/base-info-mapping"; import { useStatus } from "@/common/languages/mapping/base-info-mapping";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
import ExpandablePageableTable from "@/components/base/expandable-pageable-table/ExpandablePageableTable.vue";
import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue";
/** /**
* 必须要的变量 * 必须要的变量
@@ -27,8 +29,9 @@ const editUrl = "/purchase/purchaseplan/updatePurchasePlan";
const removeUrl = "/purchase/purchaseplan/deletePurchasePlan"; const removeUrl = "/purchase/purchaseplan/deletePurchasePlan";
const getItemsUrl = "/purchase/purchaseplan/getPurchasePlanItemsWithVendorSuggestions"; const getItemsUrl = "/purchase/purchaseplan/getPurchasePlanItemsWithVendorSuggestions";
const generateOrderUrl = "/purchase/purchaseplan/generatePurchaseOrder"; const generateOrderUrl = "/purchase/purchaseplan/generatePurchaseOrder";
const getItemUrl = "/purchase/purchaseplan/getPurchasePlanItems";
const searchers = [ const searchers = [
{ name: "vendorName", type: "text" as const, placeholder: $t("_prop.purchase.purchase_plan.vendorName") }, { name: "searchCode", type: "text" as const, placeholder: $t("_prop.purchase.purchase_plan.searchCode") },
]; ];
const { getPurchasePlanStatusLabel, getPurchasePlanItemStatusLabel } = useStatus(); const { getPurchasePlanStatusLabel, getPurchasePlanItemStatusLabel } = useStatus();
@@ -53,9 +56,9 @@ const itemArrayName = "planItems";
/** /**
* 基本不变通用变量 * 基本不变通用变量
*/ */
const tableRef = ref<InstanceType<typeof BasePageableTable> | null>(null); const tableRef = ref<InstanceType<typeof ExpandablePageableTable> | null>(null);
const { useAdd, useEdit, useRemove, useGeneralPageRef } = usePage(tableRef); const { useAdd, useEdit, useRemove, useGeneralPageRef } = usePage(tableRef);
const { title, visible, formType, form } = useGeneralPageRef(); const { title, visible, formType, form, itemVisible, itemParentId } = useGeneralPageRef();
const baseFormWithTableRef = ref<InstanceType<typeof BaseFormWithTable>>(); const baseFormWithTableRef = ref<InstanceType<typeof BaseFormWithTable>>();
const generateOrderVisible = ref(false); const generateOrderVisible = ref(false);
const generateOrderForm = reactive({ const generateOrderForm = reactive({
@@ -86,6 +89,10 @@ const edit = (row: any) => {
const remove = (row: any) => { const remove = (row: any) => {
useRemove(removeUrl, row.id, "_message.purchase.purchase_plan.delete_message"); useRemove(removeUrl, row.id, "_message.purchase.purchase_plan.delete_message");
}; };
const showItem = (row: any) => {
itemParentId.value = row.id;
itemVisible.value = true;
};
const submit = (form: any, formRef: FormInstance | undefined) => { const submit = (form: any, formRef: FormInstance | undefined) => {
if (formRef !== undefined) { if (formRef !== undefined) {
formRef.validate(valid => { formRef.validate(valid => {
@@ -132,6 +139,9 @@ const operateButtonClick = (eventName: string, row: any) => {
case "generatePurchaseOrder": case "generatePurchaseOrder":
showGenerateOrderDialog(row); showGenerateOrderDialog(row);
break; break;
case "showItem":
showItem(row);
break;
} }
}; };
@@ -333,7 +343,7 @@ const generatePurchaseOrder = async () => {
const mappingConfig: FieldMappingConfig = { const mappingConfig: FieldMappingConfig = {
partNumber: { partNumber: {
sourceKey: "商品编号", sourceKey: "物料编号",
defaultValue: "", defaultValue: "",
}, },
@@ -366,24 +376,49 @@ const mappingConfig: FieldMappingConfig = {
}; };
</script> </script>
<template> <template>
<BasePageableTable :url="getPageUrl" :searchers="searchers" ref="tableRef"> <ExpandablePageableTable
:url="getPageUrl"
:searchers="searchers"
ref="tableRef"
:item-url="getItemUrl"
item-id-key="id"
item-id-name="planId"
item-field-name="planItems"
>
<template #tool-button> <template #tool-button>
<DefaultToolButton @top-button-click="topButtonClick" /> <DefaultToolButton @top-button-click="topButtonClick" />
</template> </template>
<template #columns> <template #columns>
<el-table-column :label="$t('_prop.purchase.purchase_plan.planNo')" prop="planNo" /> <el-table-column :label="$t('_prop.purchase.purchase_plan.planNo')" prop="planNo" width="150" />
<el-table-column :label="$t('_prop.purchase.purchase_plan.planName')" prop="planName" /> <el-table-column :label="$t('_prop.purchase.purchase_plan.planName')" prop="planName" width="180" />
<el-table-column :label="$t('_prop.purchase.purchase_plan.storeName')" prop="storeName" /> <el-table-column :label="$t('_prop.purchase.purchase_plan.storeName')" prop="storeName" width="150" />
<DefaultStatusSwitchColumn <DefaultStatusSwitchColumn
status-param-name="planStatus" status-param-name="planStatus"
:status-label-mapping="getPurchasePlanStatusLabel" :status-label-mapping="getPurchasePlanStatusLabel"
:tag-type-mapping="getPurchasePlanStatusTagType" :tag-type-mapping="getPurchasePlanStatusTagType"
/> />
<el-table-column :label="$t('_prop.purchase.purchase_plan.remask')" prop="remask" /> <el-table-column :label="$t('_prop.purchase.purchase_plan.remask')" prop="remask" width="200" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" /> <el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" width="150" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" /> <DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template> </template>
</BasePageableTable> <template #item-content="{ itemData }">
<el-table :data="itemData" size="small" border stripe>
<el-table-column :label="$t('_prop.purchase.purchase_plan_item.partNumber')" prop="partNumber" width="150" />
<el-table-column :label="$t('_prop.purchase.purchase_plan.model')" prop="productSpecs" width="150" />
<el-table-column
:label="$t('_prop.purchase.purchase_plan_item.purchaseCount')"
prop="purchaseCount"
width="120"
/>
<el-table-column :label="$t('_prop.purchase.purchase_plan_item.price')" prop="price" width="120" />
<el-table-column
:label="$t('_prop.purchase.purchase_plan_item.currentCount')"
prop="currentCount"
width="120"
/>
</el-table>
</template>
</ExpandablePageableTable>
<BaseFormWithTable <BaseFormWithTable
ref="baseFormWithTableRef" ref="baseFormWithTableRef"
v-model:visible="visible" v-model:visible="visible"
@@ -594,4 +629,23 @@ const mappingConfig: FieldMappingConfig = {
</el-button> </el-button>
</template> </template>
</BaseTableForm> </BaseTableForm>
<BaseItemDialog
:title="$t('_title.purchase.purchase_plan.showItem')"
v-model:visible="itemVisible"
:url="getItemUrl"
parent-param-name="planId"
v-model:parent-param-value="itemParentId"
>
<template #columns>
<el-table-column :label="$t('_prop.purchase.purchase_plan_item.partNumber')" prop="partNumber" width="150" />
<el-table-column :label="$t('_prop.purchase.purchase_plan.model')" prop="productSpecs" width="150" />
<el-table-column
:label="$t('_prop.purchase.purchase_plan_item.purchaseCount')"
prop="purchaseCount"
width="120"
/>
<el-table-column :label="$t('_prop.purchase.purchase_plan_item.price')" prop="price" width="120" />
<el-table-column :label="$t('_prop.purchase.purchase_plan_item.currentCount')" prop="currentCount" width="120" />
</template>
</BaseItemDialog>
</template> </template>

View File

@@ -25,21 +25,37 @@ const approveUrl = "/sale/saleorder/approveSaleOrder";
const rejectUrl = "/sale/saleorder/unapproveSaleOrder"; const rejectUrl = "/sale/saleorder/unapproveSaleOrder";
const itemUrl = "/sale/saleorder/getSaleOrderItemList"; const itemUrl = "/sale/saleorder/getSaleOrderItemList";
const keyAccountSelectUrl = "/sys/keyaccount/getKeyAccountSelectList"; const keyAccountSelectUrl = "/sys/keyaccount/getKeyAccountSelectList";
const partNumberExistsUrl = "/warehouse/warehouseitem/existsWarehouseItem";
const searchers = [ const searchers = [{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.sale.saleorder.searchCode") }];
{
name: "customerName",
type: "text" as const,
placeholder: $t("_prop.sale.saleorder.customerName"),
clearable: true,
},
];
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
formCode: [{ required: true, message: $t("_message.sale.saleorder.input_formCode"), trigger: "blur" }], formCode: [{ required: true, message: $t("_message.sale.saleorder.input_formCode"), trigger: "blur" }],
formName: [{ required: true, message: $t("_message.sale.saleorder.input_formName"), trigger: "blur" }], formName: [{ required: true, message: $t("_message.sale.saleorder.input_formName"), trigger: "blur" }],
customerId: [{ required: true, message: $t("_message.sale.saleorder.select_customerId"), trigger: "change" }], customerId: [{ required: true, message: $t("_message.sale.saleorder.select_customerId"), trigger: "change" }],
customerName: [{ required: true, message: $t("_message.sale.saleorder.select_customerId"), trigger: "change" }], customerName: [{ required: true, message: $t("_message.sale.saleorder.select_customerId"), trigger: "change" }],
partNumber: [
{
required: true,
validator: (rule, value, callback) => {
if (!value) {
callback($t("_message.sale.saleorder.input_partNumber"));
}
(async () => {
try {
const isExists = await get(partNumberExistsUrl, { partNumber: value }).then(res => res.data);
if (isExists) {
callback();
}
callback($t("_message.purchase.purchase_order.part_number_not_exists"));
} catch (err: any) {
callback(new Error(err));
}
})();
},
trigger: "blur",
},
],
saleOrderItems: [ saleOrderItems: [
{ {
required: true, required: true,
@@ -96,7 +112,7 @@ const generateFormCode = () => {
String(now.getHours()).padStart(2, "0") + String(now.getHours()).padStart(2, "0") +
String(now.getMinutes()).padStart(2, "0") + String(now.getMinutes()).padStart(2, "0") +
String(now.getSeconds()).padStart(2, "0"); String(now.getSeconds()).padStart(2, "0");
return "SO" + dateStr; return "PO" + dateStr;
}; };
// 监听客户选择变化,同步更新客户名称 // 监听客户选择变化,同步更新客户名称

View File

@@ -7,7 +7,8 @@ import type { FormInstance, FormItemRule, FormRules } from "element-plus";
import { formatDate } from "@/common/utils/format-utils"; import { formatDate } from "@/common/utils/format-utils";
import ExpandablePageableTable from "@/components/base/expandable-pageable-table/ExpandablePageableTable.vue"; import ExpandablePageableTable from "@/components/base/expandable-pageable-table/ExpandablePageableTable.vue";
import BaseFormWithTable from "@/components/base/base-form-with-table/BaseFormWithTable.vue"; import BaseFormWithTable from "@/components/base/base-form-with-table/BaseFormWithTable.vue";
import { get } from "@/common/http/request"; import { get, post } from "@/common/http/request";
import { ElMessage } from "element-plus";
import { type FieldMappingConfig } from "@/components/base/base-form-with-table/type"; import { type FieldMappingConfig } from "@/components/base/base-form-with-table/type";
import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultStatusSwitchColumn.vue"; import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultStatusSwitchColumn.vue";
import { useStatus } from "@/common/languages/mapping/base-info-mapping"; import { useStatus } from "@/common/languages/mapping/base-info-mapping";
@@ -28,6 +29,7 @@ const approveUrl = "/warehouse/stocktransferorder/approveStockTransferOrder";
const rejectUrl = "/warehouse/stocktransferorder/rejectStockTransferOrder"; const rejectUrl = "/warehouse/stocktransferorder/rejectStockTransferOrder";
const warehouseSelectUrl = "/warehouse/warehouse/getWarehouseSelectList"; const warehouseSelectUrl = "/warehouse/warehouse/getWarehouseSelectList";
const partNumberExistsUrl = "/warehouse/warehouseitem/existsWarehouseItem"; const partNumberExistsUrl = "/warehouse/warehouseitem/existsWarehouseItem";
const checkStockUrl = "/warehouse/stock/checkStock";
const searchers = [ const searchers = [
{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.warehouse.stocktransferorder.searchCode") }, { name: "searchCode", type: "text" as const, placeholder: $t("_prop.warehouse.stocktransferorder.searchCode") },
{ name: "partNumber", type: "text" as const, placeholder: $t("_prop.warehouse.stocktransferorder.partNumber") }, { name: "partNumber", type: "text" as const, placeholder: $t("_prop.warehouse.stocktransferorder.partNumber") },
@@ -96,6 +98,31 @@ const rules = reactive<FormRules>({
trigger: "change", trigger: "change",
}, },
], ],
stockCheck: [
{
validator: (rule, value, callback) => {
const items = form.value.transferOrderItems || [];
const partCountMap: Record<string, number> = {};
items.forEach((item: any) => {
if (item.partNumber) {
partCountMap[item.partNumber] = (partCountMap[item.partNumber] || 0) + (item.productCount || 0);
}
});
const stockCheckItems = Object.keys(partCountMap).map(partNumber => ({
partNumber,
requiredCount: partCountMap[partNumber],
}));
post(checkStockUrl, { storeNo: form.value.outStoreNo, items: stockCheckItems })
.then(() => {
callback();
})
.catch((err: any) => {
callback(new Error(err));
});
},
trigger: "submit",
},
],
}); });
const itemArrayName = "transferOrderItems"; const itemArrayName = "transferOrderItems";
@@ -226,8 +253,11 @@ const submit = (form: any, formRef: FormInstance | undefined) => {
if (targetRef !== undefined) { if (targetRef !== undefined) {
targetRef.validate((valid: any) => { targetRef.validate((valid: any) => {
if (valid) { if (valid) {
if (formType.value) useEdit(editUrl, form, visible); if (formType.value) {
else useAdd(addUrl, form, visible); useEdit(editUrl, form, visible);
} else {
useAdd(addUrl, form, visible);
}
} }
}); });
} }
@@ -262,8 +292,8 @@ const operateButtonClick = (eventName: string, row: any) => {
}; };
const authShowFunc = (row: any, button: globalThis.ButtonProp) => { const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
if (row.formStatus === 0 && button.eventName === "reject") return false; if (row.status === 0 && button.eventName === "reject") return false;
if (row.formStatus === 1 && button.eventName === "approve") return false; if (row.status === 1 && button.eventName === "approve") return false;
return true; return true;
}; };
</script> </script>
@@ -288,7 +318,7 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.formMark')" prop="formMark" /> <el-table-column :label="$t('_prop.warehouse.stocktransferorder.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" /> <el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn <DefaultStatusSwitchColumn
status-param-name="formStatus" status-param-name="status"
:status-label-mapping="getFormStatusLabel" :status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType" :tag-type-mapping="getFormStatusTagType"
/> />

View File

@@ -156,9 +156,9 @@ const operateButtonClick = (eventName: string, row: any) => {
}; };
const authShowFunc = (row: any, button: globalThis.ButtonProp) => { const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
if (row.formStatus === 0 && button.eventName === "reject") return false; if (row.status === 0 && button.eventName === "reject") return false;
if (row.formStatus === 1 && button.eventName === "approve") return false; if (row.status === 1 && button.eventName === "approve") return false;
if (row.formStatus === 1 && button.eventName === "edit") return false; if (row.status === 1 && button.eventName === "edit") return false;
return true; return true;
}; };
@@ -216,7 +216,7 @@ const submit = (form: any, formRef: FormInstance | undefined) => {
<el-table-column :label="$t('_prop.warehouse.warehousereceipt.formMark')" prop="formMark" /> <el-table-column :label="$t('_prop.warehouse.warehousereceipt.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" /> <el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn <DefaultStatusSwitchColumn
status-param-name="formStatus" status-param-name="status"
:status-label-mapping="getFormStatusLabel" :status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType" :tag-type-mapping="getFormStatusTagType"
/> />

View File

@@ -11,15 +11,18 @@ import { formatDate } from "@/common/utils/format-utils";
/** /**
* 必须要的变量 * 必须要的变量
*/ */
// defineOptions({
// name: "store",
// });
const getPageUrl = "/warehouse/warehouse/getWarehousePage"; const getPageUrl = "/warehouse/warehouse/getWarehousePage";
const addUrl = "/warehouse/warehouse/addWarehouse"; const addUrl = "/warehouse/warehouse/addWarehouse";
const editUrl = "/warehouse/warehouse/updateWarehouse"; const editUrl = "/warehouse/warehouse/updateWarehouse";
const removeUrl = "/warehouse/warehouse/deleteWarehouse"; const removeUrl = "/warehouse/warehouse/deleteWarehouse";
const searchers = [ const searchers = [{ name: "storeName", type: "text" as const, placeholder: $t("_prop.systemset.store.storeName") }];
{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.warehouse.warehouse.searchCode") }, const rules = reactive<FormRules>({
]; storeName: [{ required: true, message: $t("_message.systemset.store.input_storeName"), trigger: "blur" }],
const rules = reactive<FormRules>({}); storeMark: [{ required: false, message: $t("_message.systemset.store.input_storeMark"), trigger: "blur" }],
});
/** /**
* 基本不变通用变量 * 基本不变通用变量
*/ */
@@ -32,18 +35,18 @@ const { title, visible, formType, form } = useGeneralPageRef();
const add = () => { const add = () => {
form.value = {}; form.value = {};
title.value = "_title.warehouse.warehouse.add"; title.value = "_title.systemset.store.add";
visible.value = true; visible.value = true;
formType.value = false; formType.value = false;
}; };
const edit = (row: any) => { const edit = (row: any) => {
title.value = "_title.warehouse.warehouse.edit"; title.value = "_title.systemset.store.edit";
form.value = { ...row }; form.value = { ...row };
visible.value = true; visible.value = true;
formType.value = true; formType.value = true;
}; };
const remove = (row: any) => { const remove = (row: any) => {
useRemove(removeUrl, row.id, "_message.warehouse.warehouse.delete_message"); useRemove(removeUrl, row.id, "_message.systemset.store.delete_message");
}; };
const submit = (form: any, formRef: FormInstance | undefined) => { const submit = (form: any, formRef: FormInstance | undefined) => {
if (formRef !== undefined) { if (formRef !== undefined) {
@@ -79,9 +82,8 @@ const operateButtonClick = (eventName: string, row: any) => {
<DefaultToolButton @top-button-click="topButtonClick" /> <DefaultToolButton @top-button-click="topButtonClick" />
</template> </template>
<template #columns> <template #columns>
<el-table-column prop="id" type="hidden" width="40" /> <el-table-column :label="$t('_prop.systemset.store.storeName')" prop="storeName" />
<el-table-column :label="$t('_prop.warehouse.warehouse.storeName')" prop="storeName" /> <el-table-column :label="$t('_prop.systemset.store.storeMark')" prop="storeMark" />
<el-table-column :label="$t('_prop.warehouse.warehouse.storeMark')" prop="storeMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" /> <el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" /> <DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
</template> </template>
@@ -89,6 +91,12 @@ const operateButtonClick = (eventName: string, row: any) => {
<BaseForm v-model:visible="visible" @submit="submit" v-model:form="form" :title="$t(title)" :rules="rules"> <BaseForm v-model:visible="visible" @submit="submit" v-model:form="form" :title="$t(title)" :rules="rules">
<template #form-items> <template #form-items>
<el-form-item prop="id" v-if="false"><el-input v-model="form.id" /></el-form-item> <el-form-item prop="id" v-if="false"><el-input v-model="form.id" /></el-form-item>
<el-form-item :label="$t('_prop.systemset.store.storeName')" prop="storeName">
<el-input v-model="form.storeName" :placeholder="$t('_message.systemset.store.input_storeName')" />
</el-form-item>
<el-form-item :label="$t('_prop.systemset.store.storeMark')" prop="storeMark">
<el-input v-model="form.storeMark" :placeholder="$t('_message.systemset.store.input_storeMark')" />
</el-form-item>
</template> </template>
</BaseForm> </BaseForm>
</template> </template>