<template>
|
<div class="app-container">
|
<div class="search_form">
|
<el-form :model="searchForm"
|
ref="queryRef"
|
:inline="true">
|
<!-- 简化版搜索条件 -->
|
<el-form-item label="主生产计划号:"
|
prop="mpsNo">
|
<el-input v-model="searchForm.mpsNo"
|
placeholder="请输入"
|
clearable
|
style="width: 160px;"
|
@keyup.enter="handleQuery" />
|
</el-form-item>
|
<el-form-item label="需求日期范围:"
|
prop="dateRange">
|
<el-date-picker v-model="searchForm.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
value-format="YYYY-MM-DD"
|
style="width: 240px;"
|
@change="handleQuery" />
|
</el-form-item>
|
<el-form-item label="下发状态:"
|
prop="status">
|
<el-select v-model="searchForm.status"
|
placeholder="请选择状态"
|
clearable
|
filterable
|
style="width: 100px">
|
<el-option label="待下发"
|
value="0" />
|
<el-option label="部分下发"
|
value="1" />
|
<el-option label="已下发"
|
value="2" />
|
</el-select>
|
</el-form-item>
|
<!-- 展开版搜索条件 -->
|
<el-form-item label="产品名称:"
|
prop="productName">
|
<el-input v-model="searchForm.productName"
|
placeholder="请输入"
|
clearable
|
style="width: 160px;"
|
@keyup.enter="handleQuery" />
|
</el-form-item>
|
<el-form-item label="产品规格:"
|
prop="model">
|
<el-input v-model="searchForm.model"
|
placeholder="请输入"
|
clearable
|
style="width: 160px;"
|
@keyup.enter="handleQuery" />
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary"
|
@click="handleQuery">搜索</el-button>
|
<el-button type="info"
|
@click="handleReset">重置</el-button>
|
<el-button type="primary"
|
@click="handleAdd">新增</el-button>
|
<el-button type="warning"
|
@click="handleMerge">合并下发</el-button>
|
<el-button type="warning"
|
@click="handleImport">导入</el-button>
|
<el-button type="warning"
|
@click="handleExport">导出</el-button>
|
</el-form-item>
|
</el-form>
|
<div>
|
</div>
|
</div>
|
<div class="table_list">
|
<PIMTable rowKey="id"
|
:column="tableColumn"
|
:tableData="tableData"
|
:page="page"
|
height="calc(100vh - 350px)"
|
:tableLoading="tableLoading"
|
:isSelection="true"
|
:selectable="isSelectable"
|
@selection-change="handleSelectionChange"
|
@pagination="pagination">
|
<template #qtyRequired="{ row }">
|
{{ row.qtyRequired || '-' }}<span style="color:rgba(12, 46, 40, 0.76)"> {{ row.unit || '方' }}</span>
|
</template>
|
<template #salesContractNo="{ row }">
|
<el-button type="primary"
|
text
|
link
|
@click="showDetail(row)">{{ row.salesContractNo }}
|
</el-button>
|
</template>
|
</PIMTable>
|
</div>
|
<!-- 合并下发弹窗 -->
|
<el-dialog v-model="isShowNewModal"
|
destroy-on-close
|
title="合并下发"
|
width="600px">
|
<el-form :model="mergeForm"
|
label-width="120px">
|
<el-row :gutter="20">
|
<el-col :span="10">
|
<el-form-item label="产品名称">
|
<el-tag class="info-display">{{ mergeForm.productName || '-' }}</el-tag>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col>
|
<el-form-item label="产品规格">
|
<div class="info-display">{{ mergeForm.model || '-' }}</div>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-form-item label="计划完成时间">
|
<el-date-picker v-model="mergeForm.planCompleteTime"
|
type="date"
|
value-format="YYYY-MM-DD"
|
style="width: 100%" />
|
</el-form-item>
|
<el-form-item label="生产数量">
|
<el-input-number v-model="mergeForm.totalAssignedQuantity"
|
:min="0"
|
:max="sumAssignedQuantity"
|
@change="onBlur"
|
style="width: 100%" />
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary" @click="handleMergeSubmit">确定下发</el-button>
|
<el-button @click="isShowNewModal = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<!-- 导入弹窗 -->
|
<ImportDialog ref="importDialogRef"
|
v-model="importDialogVisible"
|
title="导入生产计划"
|
:action="importAction"
|
:headers="importHeaders"
|
:auto-upload="false"
|
:on-success="handleImportSuccess"
|
:on-error="handleImportError"
|
@confirm="handleImportConfirm"
|
@download-template="handleDownloadTemplate"
|
@close="handleImportClose" />
|
<!-- 新增/编辑弹窗 -->
|
<el-dialog v-model="dialogVisible"
|
destroy-on-close
|
:title="operationType === 'add' ? '新增生产计划' : '编辑生产计划'"
|
width="600px">
|
<el-form ref="formRef"
|
:model="form"
|
:rules="rules"
|
label-width="120px">
|
<el-form-item label="主生产计划号"
|
prop="mpsNo">
|
<el-input v-model="form.mpsNo"
|
disabled
|
placeholder="新增后自动生成" />
|
</el-form-item>
|
<el-form-item label="产品名称"
|
prop="productId">
|
<el-tree-select v-model="form.productId"
|
placeholder="请选择"
|
clearable
|
:data="productOptions"
|
:render-after-expand="false"
|
filterable
|
@change="handleProductChange"
|
style="width: 100%" />
|
</el-form-item>
|
<el-form-item label="产品规格"
|
prop="productModelId">
|
<el-select v-model="form.productModelId"
|
@change="handleChangeSpecification"
|
filterable
|
style="width: 100%"
|
placeholder="请选择">
|
<el-option v-for="item in specificationOptions"
|
:key="item.id"
|
:label="item.model"
|
:value="item.id" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="所需数量"
|
prop="qtyRequired">
|
<el-input-number v-model="form.qtyRequired"
|
:min="0"
|
style="width: 100%"
|
placeholder="请输入数量" />
|
</el-form-item>
|
<el-form-item label="单位"
|
prop="unit">
|
<el-input v-model="form.unit"
|
placeholder="请输入单位" />
|
</el-form-item>
|
<el-form-item label="需求日期"
|
prop="requiredDate">
|
<el-date-picker v-model="form.requiredDate"
|
type="date"
|
value-format="YYYY-MM-DD"
|
style="width: 100%"
|
placeholder="请选择需求日期" />
|
</el-form-item>
|
<el-form-item label="承诺日期"
|
prop="promisedDeliveryDate">
|
<el-date-picker v-model="form.promisedDeliveryDate"
|
type="date"
|
value-format="YYYY-MM-DD"
|
style="width: 100%"
|
placeholder="请选择承诺日期" />
|
</el-form-item>
|
<el-form-item label="备注"
|
prop="remark">
|
<el-input v-model="form.remark"
|
type="textarea"
|
placeholder="请输入备注" />
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
<el-button @click="dialogVisible = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import {
|
reactive,
|
ref,
|
onMounted,
|
toRefs,
|
getCurrentInstance,
|
computed,
|
} from "vue";
|
import { useRouter } from "vue-router";
|
import dayjs from "dayjs";
|
import { ElMessage } from "element-plus";
|
import { ArrowUp, ArrowDown } from "@element-plus/icons-vue";
|
import { getToken } from "@/utils/auth";
|
import { useDict } from "@/utils/dict";
|
import {
|
productionPlanListPage,
|
productionPlanAdd,
|
productionPlanUpdate,
|
productionPlanDelete,
|
productionPlanCombine,
|
} from "@/api/productionPlan/productionPlan.js";
|
import { productTreeList, modelListPage } from "@/api/basicData/product.js";
|
import PIMTable from "./components/PIMTable.vue";
|
import ImportDialog from "@/components/Dialog/ImportDialog.vue";
|
|
const { proxy } = getCurrentInstance();
|
const router = useRouter();
|
|
const loadProdData = () => {
|
console.log("Mock loadProdData called");
|
return Promise.resolve({ code: 200, msg: "同步成功" });
|
};
|
|
const exportProductionPlan = () => {
|
console.log("Mock exportProductionPlan called");
|
return Promise.resolve();
|
};
|
|
// const productionPlanCombine = payload => {
|
// console.log("Mock productionPlanCombine called with:", payload);
|
// return Promise.resolve({ code: 200, msg: "合并下发成功" });
|
// };
|
|
const tableColumn = ref([
|
{
|
label: "主生产计划号",
|
prop: "mpsNo",
|
width: "150px",
|
},
|
{
|
label: "来源",
|
prop: "source",
|
width: "150px",
|
dataType: "tag",
|
formatType: params => {
|
return params == "销售" ? "primary" : "info";
|
},
|
formatData: params => {
|
return params == "销售" ? "销售" : "内部";
|
},
|
},
|
|
{
|
label: "产品名称",
|
prop: "productName",
|
width: "200px",
|
dataType: "tag",
|
formatType: params => {
|
return "primary";
|
},
|
},
|
{
|
label: "产品规格",
|
prop: "model",
|
width: "150px",
|
className: "spec-cell",
|
},
|
{
|
label: "单位",
|
prop: "unit",
|
width: "100px",
|
},
|
{
|
label: "所需数量",
|
prop: "qtyRequired",
|
width: "150px",
|
align: "right",
|
dataType: "slot",
|
slot: "qtyRequired",
|
className: "volume-cell",
|
},
|
{
|
label: "下发状态",
|
prop: "status",
|
width: "120px",
|
className: "status-cell",
|
dataType: "tag",
|
formatType: params => {
|
const typeMap = {
|
0: "warning",
|
1: "primary",
|
2: "info",
|
};
|
return typeMap[params] || "info";
|
},
|
formatData: cell => {
|
const statusMap = {
|
0: "待下发",
|
1: "部分下发",
|
2: "已下发",
|
};
|
return statusMap[cell] || "";
|
},
|
},
|
{
|
label: "已下发数量",
|
prop: "quantityIssued",
|
width: "120px",
|
className: "spec-cell",
|
// formatData: (cell, row) => (cell ? `${cell}${row.unit || "方"}` : 0),
|
},
|
{
|
label: "需求日期",
|
prop: "requiredDate",
|
width: "160px",
|
className: "date-cell",
|
formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
|
},
|
{
|
label: "承诺日期",
|
prop: "promisedDeliveryDate",
|
width: "160px",
|
className: "date-cell",
|
formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
|
},
|
{
|
label: "销售合同号",
|
prop: "salesContractNo",
|
width: "200px",
|
dataType: "slot",
|
slot: "salesContractNo",
|
},
|
{
|
label: "客户名称",
|
prop: "customerName",
|
width: "150px",
|
},
|
{
|
label: "项目名称",
|
prop: "projectName",
|
width: "150px",
|
},
|
{
|
label: "备注",
|
width: "150px",
|
prop: "remark",
|
},
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 250,
|
operation: [
|
{
|
name: "编辑",
|
type: "primary",
|
link: true,
|
showHide: row => {
|
return row.status == 0 && row.source != "销售";
|
},
|
clickFun: row => {
|
handleEdit(row);
|
},
|
},
|
{
|
name: "下发",
|
type: "text",
|
showHide: row => {
|
return row.status != 2;
|
},
|
clickFun: row => {
|
mergeForm.productName = row.productName || "";
|
mergeForm.model = row.model || "";
|
mergeForm.totalAssignedQuantity =
|
Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
|
mergeForm.planCompleteTime = row.requiredDate || "";
|
mergeForm.productId = row.productId || "";
|
mergeForm.ids = [row.id];
|
sumAssignedQuantity.value =
|
Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
|
isShowNewModal.value = true;
|
},
|
},
|
{
|
name: "删除",
|
type: "danger",
|
link: true,
|
showHide: row => {
|
return row.status == 0;
|
},
|
clickFun: row => {
|
handleDelete(row);
|
},
|
},
|
],
|
},
|
]);
|
const tableData = ref([]);
|
const tableLoading = ref(false);
|
const page = reactive({
|
current: 1,
|
size: 100,
|
total: 0,
|
});
|
const selectedRows = ref([]);
|
|
// 产品类别汇总统计数据
|
const categorySummary = ref([]);
|
// 产品类别汇总弹窗控制
|
const showCategorySummaryDialog = ref(false);
|
|
// 合并下发弹窗控制
|
const isShowNewModal = ref(false);
|
// 合并下发表单数据
|
const mergeForm = reactive({
|
productName: "",
|
model: "",
|
totalAssignedQuantity: 0,
|
planCompleteTime: "",
|
productId: "",
|
});
|
|
// 导入相关
|
const importDialogRef = ref(null);
|
const importDialogVisible = ref(false);
|
const importAction =
|
import.meta.env.VITE_APP_BASE_API + "/productionPlan/import";
|
const importHeaders = ref({
|
Authorization: `Bearer ${getToken()}`,
|
});
|
|
// 新增/编辑相关
|
const dialogVisible = ref(false);
|
const operationType = ref("add"); // add | edit
|
const productOptions = ref([]);
|
const specificationOptions = ref([]);
|
const queryRef = ref(null);
|
const formRef = ref(null);
|
const form = reactive({
|
id: undefined,
|
mpsNo: "",
|
productId: undefined,
|
productModelId: undefined,
|
productName: "",
|
model: "",
|
qtyRequired: 0,
|
unit: "方",
|
requiredDate: "",
|
promisedDeliveryDate: "",
|
remark: "",
|
});
|
const rules = reactive({
|
productId: [{ required: true, message: "请选择产品", trigger: "change" }],
|
productModelId: [
|
{ required: true, message: "请选择产品规格", trigger: "change" },
|
],
|
qtyRequired: [{ required: true, message: "请输入数量", trigger: "blur" }],
|
requiredDate: [
|
{ required: true, message: "请选择需求日期", trigger: "change" },
|
],
|
});
|
|
// 处理追踪进度按钮点击
|
const handleTrackProgress = row => {
|
// 跳转到追踪进度页面
|
router.push({
|
path: "/productionPlan/trackProgress",
|
query: {
|
id: row.id,
|
productName: row.productName,
|
model: row.model,
|
},
|
});
|
};
|
const onBlur = value => {
|
// 限制四位小数
|
mergeForm.totalAssignedQuantity = Number(value.toFixed(4));
|
};
|
|
const fetchProductOptions = () => {
|
return productTreeList().then(res => {
|
productOptions.value = convertIdToValue(res || []);
|
});
|
};
|
|
const convertIdToValue = data => {
|
return data.map(item => {
|
const newItem = {
|
value: item.id,
|
label: item.label,
|
};
|
if (item.children && item.children.length > 0) {
|
newItem.children = convertIdToValue(item.children);
|
}
|
return newItem;
|
});
|
};
|
|
const handleProductChange = value => {
|
form.productModelId = undefined;
|
form.model = undefined;
|
// 查找选中的产品名称
|
const findProductName = (options, val) => {
|
for (const option of options) {
|
if (option.value === val) {
|
return option.label;
|
}
|
if (option.children) {
|
const found = findProductName(option.children, val);
|
if (found) {
|
return found;
|
}
|
}
|
}
|
return "";
|
};
|
form.productName = findProductName(productOptions.value, value);
|
fetchSpecificationOptions(value);
|
};
|
|
const fetchSpecificationOptions = productId => {
|
specificationOptions.value = [];
|
if (productId) {
|
modelListPage({ id: productId, size: 1000, current: 1 }).then(res => {
|
specificationOptions.value = res.records || [];
|
});
|
}
|
};
|
|
const handleChangeSpecification = value => {
|
form.model = undefined;
|
form.unit = "";
|
const selectedModel = specificationOptions.value.find(
|
item => item.id === value
|
);
|
if (selectedModel) {
|
form.model = selectedModel.model;
|
form.unit = selectedModel.unit || "方";
|
}
|
};
|
|
const data = reactive({
|
searchForm: {
|
mpsNo: "",
|
productName: "",
|
model: "",
|
status: "",
|
dateRange: [],
|
},
|
searchFormExpanded: false,
|
});
|
const { searchForm, searchFormExpanded } = toRefs(data);
|
|
// 切换搜索表单展开/收起状态
|
const toggleSearchForm = () => {
|
data.searchFormExpanded = !data.searchFormExpanded;
|
};
|
|
// 查询列表
|
/** 搜索按钮操作 */
|
const handleQuery = () => {
|
page.current = 1;
|
getList();
|
};
|
|
/** 重置按钮操作 */
|
const handleReset = () => {
|
if (proxy.resetForm) {
|
proxy.resetForm("queryRef");
|
}
|
Object.assign(searchForm.value, {
|
mpsNo: "",
|
productName: "",
|
model: "",
|
status: "",
|
dateRange: [],
|
});
|
page.current = 1;
|
getList();
|
};
|
const pagination = obj => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList();
|
};
|
// 计算产品类别汇总统计
|
const calculateCategorySummary = () => {
|
const summary = {};
|
|
// 遍历表格数据,按产品类别汇总
|
tableData.value.forEach(row => {
|
const category = row.productName || "未知产品";
|
if (!summary[category]) {
|
summary[category] = {
|
materialCode: category,
|
totalAssignedQuantity: 0,
|
};
|
}
|
summary[category].totalAssignedQuantity += Number(
|
(Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0)).toFixed(
|
4
|
)
|
);
|
});
|
|
// 转换为数组格式
|
categorySummary.value = Object.values(summary);
|
};
|
|
const getList = () => {
|
tableLoading.value = true;
|
// 构造搜索参数
|
const params = { ...searchForm.value, ...page };
|
params.requiredDateStart =
|
params.dateRange && params.dateRange.length > 0 ? params.dateRange[0] : "";
|
params.requiredDateEnd =
|
params.dateRange && params.dateRange.length > 1 ? params.dateRange[1] : "";
|
delete params.dateRange;
|
productionPlanListPage(params)
|
.then(res => {
|
tableLoading.value = false;
|
tableData.value = res.data.records;
|
page.total = res.data.total;
|
// 计算产品类别汇总统计
|
calculateCategorySummary();
|
})
|
.catch(() => {
|
tableLoading.value = false;
|
});
|
};
|
|
// 选中的产品规格ID
|
const selectedProductModelId = ref("");
|
|
// 表格选择数据
|
const handleSelectionChange = selection => {
|
selectedRows.value = selection;
|
// 如果有选中的行,记录第一个选中行的产品规格ID
|
if (selection.length > 0) {
|
selectedProductModelId.value = selection[0].productModelId;
|
} else {
|
// 如果没有选中的行,清空产品规格ID
|
selectedProductModelId.value = "";
|
}
|
};
|
|
// 判断行是否可选择
|
const isSelectable = row => {
|
// 如果是已下发状态,禁止勾选
|
if (row.status == 2) {
|
return false;
|
}
|
// 计算剩余数量
|
const remainingQty = (row.qtyRequired || 0) - (row.quantityIssued || 0);
|
// 如果剩余数量小于等于0,禁止选择
|
if (remainingQty <= 0) {
|
return false;
|
}
|
// 如果没有选中的行,所有行都可选择
|
if (!selectedProductModelId.value) {
|
return true;
|
}
|
// 如果有选中的行,只有产品规格ID相同的行才可选择
|
return row.productModelId === selectedProductModelId.value;
|
};
|
// 拉取数据按钮操作
|
const loadProdDataLoading = ref(false);
|
const sumAssignedQuantity = ref(0);
|
|
// 处理合并下发按钮点击
|
const handleMerge = () => {
|
if (selectedRows.value.length === 0) {
|
ElMessage.warning("请选择要合并下发的生产计划");
|
return;
|
}
|
console.log(selectedRows.value);
|
const firstRow = selectedRows.value[0];
|
const productName = firstRow.productName || "";
|
|
// 计算总制造数量 (默认qtyRequired的和)
|
const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => {
|
return sum + Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
|
}, 0);
|
sumAssignedQuantity.value = totalAssignedQuantity;
|
console.log(totalAssignedQuantity);
|
// 设置表单数据
|
mergeForm.productName = productName;
|
mergeForm.model = firstRow.model || "";
|
mergeForm.totalAssignedQuantity = totalAssignedQuantity;
|
mergeForm.planCompleteTime = firstRow.requiredDate || "";
|
mergeForm.productId = firstRow.productId || "";
|
mergeForm.ids = selectedRows.value.map(row => row.id);
|
|
// 打开弹窗
|
isShowNewModal.value = true;
|
};
|
const showDetail = row => {
|
router.push({
|
path: "/salesManagement/salesLedger",
|
query: {
|
salesContractNo: row.salesContractNo,
|
},
|
});
|
};
|
|
// 处理合并下发提交
|
const handleMergeSubmit = () => {
|
if (mergeForm.totalAssignedQuantity === 0) {
|
ElMessage.warning("请输入生产数量");
|
return;
|
}
|
console.log(sumAssignedQuantity.value, "sumAssignedQuantity");
|
|
// 验证totalAssignedQuantity不能大于总方数
|
if (mergeForm.totalAssignedQuantity > sumAssignedQuantity.value) {
|
ElMessage.error("生产数量不能大于当前计算的总值");
|
return;
|
}
|
|
console.log(mergeForm, "mergeForm");
|
const payload = {
|
...mergeForm,
|
};
|
productionPlanCombine(payload)
|
.then(res => {
|
if (res.code === 200) {
|
ElMessage.success("下发成功");
|
getList();
|
isShowNewModal.value = false;
|
// 可以选择刷新列表或其他操作
|
getList();
|
} else {
|
ElMessage.error(res.message || "下发失败");
|
}
|
})
|
.catch(err => {
|
console.error("合并下发异常:", err);
|
ElMessage.error("系统异常,合并下发失败");
|
});
|
// 可以选择刷新列表或其他操作
|
};
|
|
// 导入
|
const handleImport = () => {
|
importDialogVisible.value = true;
|
};
|
|
// 导出
|
const handleExport = () => {
|
const fileName = `生产计划.xlsx`;
|
exportProductionPlan()
|
.then(res => {
|
// 返回的数据是否为空
|
if (!res) {
|
proxy.$modal.msgError("导出失败,返回数据为空");
|
return;
|
}
|
|
const blob = new Blob([res], {
|
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
});
|
const downloadElement = document.createElement("a");
|
const href = window.URL.createObjectURL(blob);
|
|
downloadElement.style.display = "none";
|
downloadElement.href = href;
|
downloadElement.download = fileName;
|
|
document.body.appendChild(downloadElement);
|
downloadElement.click();
|
|
document.body.removeChild(downloadElement);
|
window.URL.revokeObjectURL(href);
|
|
proxy.$modal.msgSuccess("导出成功");
|
})
|
.catch(err => {
|
console.error("导出异常:", err);
|
proxy.$modal.msgError("系统异常,导出失败");
|
});
|
};
|
|
// 导入成功
|
const handleImportSuccess = response => {
|
if (response.code === 200) {
|
ElMessage.success("导入成功");
|
importDialogVisible.value = false;
|
getList();
|
} else {
|
ElMessage.error(response.msg || "导入失败");
|
}
|
};
|
|
// 导入失败
|
const handleImportError = error => {
|
ElMessage.error("导入失败,请检查文件格式是否正确");
|
};
|
|
// 确认导入
|
const handleImportConfirm = () => {
|
if (importDialogRef.value) {
|
importDialogRef.value.submit();
|
}
|
};
|
|
// 下载模板
|
const handleDownloadTemplate = () => {
|
proxy.download(
|
"/productionPlan/downloadTemplate",
|
{},
|
"生产计划导入模板.xlsx"
|
);
|
};
|
|
// 关闭导入弹窗
|
const handleImportClose = () => {
|
importDialogVisible.value = false;
|
};
|
|
// 新增
|
const handleAdd = () => {
|
operationType.value = "add";
|
Object.assign(form, {
|
id: undefined,
|
mpsNo: "",
|
productName: "",
|
productId: undefined,
|
productModelId: undefined,
|
model: "",
|
qtyRequired: 0,
|
unit: "方",
|
requiredDate: "",
|
promisedDeliveryDate: "",
|
remark: "",
|
});
|
dialogVisible.value = true;
|
fetchProductOptions();
|
};
|
|
// 编辑
|
const handleEdit = row => {
|
operationType.value = "edit";
|
Object.assign(form, {
|
id: row.id,
|
mpsNo: row.mpsNo || "",
|
productName: row.productName || "",
|
productId: row.productId || undefined,
|
productModelId: row.productModelId || undefined,
|
model: row.model || "",
|
qtyRequired: row.qtyRequired || 0,
|
unit: row.unit || "方",
|
requiredDate: row.requiredDate || "",
|
promisedDeliveryDate: row.promisedDeliveryDate || "",
|
remark: row.remark || "",
|
});
|
dialogVisible.value = true;
|
fetchProductOptions();
|
fetchSpecificationOptions(row.productId);
|
};
|
|
// 删除
|
const handleDelete = row => {
|
proxy.$modal
|
.confirm("确认删除该生产计划?", "提示", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
productionPlanDelete([row.id])
|
.then(() => {
|
proxy.$modal.msgSuccess("删除成功");
|
getList();
|
})
|
.catch(() => {
|
proxy.$modal.msgError("删除失败");
|
});
|
})
|
.catch(() => {});
|
};
|
|
// 提交表单
|
const handleSubmit = () => {
|
formRef.value.validate(valid => {
|
if (valid) {
|
if (form.qtyRequired === 0) {
|
proxy.$modal.msgError("数量不能为0");
|
return;
|
}
|
const payload = { ...form };
|
if (operationType.value === "add") {
|
payload.id = null;
|
productionPlanAdd(payload)
|
.then(() => {
|
proxy.$modal.msgSuccess("新增成功");
|
dialogVisible.value = false;
|
getList();
|
})
|
.catch(() => {
|
proxy.$modal.msgError("新增失败");
|
});
|
}
|
if (operationType.value === "edit") {
|
productionPlanUpdate(payload)
|
.then(() => {
|
proxy.$modal.msgSuccess("修改成功");
|
dialogVisible.value = false;
|
getList();
|
})
|
.catch(() => {
|
proxy.$modal.msgError("修改失败");
|
});
|
}
|
}
|
});
|
};
|
|
onMounted(() => {
|
getList();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.app-container {
|
padding: 24px;
|
background-color: #f0f2f5;
|
min-height: calc(100vh - 48px);
|
}
|
|
.search_form {
|
// margin-bottom: 24px;
|
padding: 20px;
|
background-color: #ffffff;
|
border-radius: 6px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
transition: all 0.3s ease;
|
|
&:hover {
|
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
|
}
|
}
|
|
.search-header {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
// margin-bottom: 5px;
|
// padding-bottom: 5px;
|
position: relative;
|
bottom: 35px;
|
// border-bottom: 1px solid #ebeef5;
|
}
|
|
.search-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.search-header .el-button {
|
color: #606266;
|
transition: all 0.3s ease;
|
}
|
|
.search-header .el-button:hover {
|
color: #409eff;
|
}
|
|
.search-header .el-icon {
|
margin-right: 4px;
|
}
|
|
.table_list {
|
// margin-bottom: 24px;
|
background-color: #ffffff;
|
border-radius: 6px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
overflow: hidden;
|
height: calc(100vh - 250px);
|
margin-top: 0px !important;
|
}
|
|
:deep(.el-table) {
|
border: none;
|
border-radius: 6px;
|
overflow: hidden;
|
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.1);
|
|
.el-table__header-wrapper {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
th {
|
background: transparent;
|
font-weight: 600;
|
color: #ffffff;
|
border-bottom: none;
|
padding: 16px 0;
|
letter-spacing: 0.5px;
|
}
|
}
|
|
.el-table__body-wrapper {
|
tr {
|
transition: all 0.3s ease;
|
|
&:hover {
|
background: linear-gradient(
|
90deg,
|
rgba(102, 126, 234, 0.05) 0%,
|
rgba(118, 75, 162, 0.05) 100%
|
);
|
transform: scale(1.002);
|
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
|
}
|
|
td {
|
border-bottom: 1px solid #f0f0f0;
|
padding: 14px 0;
|
color: #303133;
|
}
|
}
|
|
tr.current-row {
|
background: linear-gradient(
|
90deg,
|
rgba(102, 126, 234, 0.08) 0%,
|
rgba(118, 75, 162, 0.08) 100%
|
);
|
}
|
|
// 数值字段样式
|
.volume-cell,
|
.dimension-cell {
|
font-weight: 600;
|
color: #409eff;
|
font-family: "Courier New", monospace;
|
text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
|
}
|
|
// 规格字段样式
|
.spec-cell {
|
color: #67c23a;
|
font-weight: 500;
|
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
|
// 编码字段样式
|
.code-cell {
|
color: #e6a23c;
|
font-family: "Courier New", monospace;
|
font-weight: 500;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
|
// 日期字段样式
|
.date-cell {
|
color: #909399;
|
font-style: italic;
|
}
|
|
// 状态标签样式
|
.status-tag {
|
&.pending {
|
background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);
|
color: #d63031;
|
padding: 4px 12px;
|
border-radius: 12px;
|
font-weight: 500;
|
box-shadow: 0 2px 4px rgba(253, 203, 110, 0.3);
|
}
|
|
&.processing {
|
background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
|
color: #ffffff;
|
padding: 4px 12px;
|
border-radius: 12px;
|
font-weight: 500;
|
box-shadow: 0 2px 4px rgba(9, 132, 227, 0.3);
|
}
|
|
&.completed {
|
background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);
|
color: #ffffff;
|
padding: 4px 12px;
|
border-radius: 12px;
|
font-weight: 500;
|
box-shadow: 0 2px 4px rgba(0, 184, 148, 0.3);
|
}
|
}
|
}
|
|
.el-table__empty-block {
|
padding: 60px 0;
|
background-color: #fafafa;
|
}
|
}
|
|
// 操作按钮样式
|
:deep(.el-table .cell .el-button--text) {
|
padding: 6px 10px;
|
border-radius: 4px;
|
transition: all 0.3s ease;
|
font-weight: 500;
|
|
&:hover {
|
background-color: rgba(64, 158, 255, 0.1);
|
transform: translateY(-1px);
|
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
|
}
|
|
&:nth-of-type(1) {
|
color: #409eff;
|
background: linear-gradient(
|
135deg,
|
rgba(64, 158, 255, 0.1) 0%,
|
rgba(64, 158, 255, 0.05) 100%
|
);
|
}
|
|
&:nth-of-type(2) {
|
color: #67c23a;
|
background: linear-gradient(
|
135deg,
|
rgba(103, 194, 58, 0.1) 0%,
|
rgba(103, 194, 58, 0.05) 100%
|
);
|
}
|
}
|
|
// 信息展示样式
|
.info-display {
|
border-radius: 6px;
|
color: #303133;
|
font-size: 14px;
|
min-height: 32px;
|
width: 100%;
|
display: flex;
|
align-items: center;
|
}
|
|
.pagination-container {
|
display: flex;
|
justify-content: flex-end;
|
padding: 16px 20px;
|
background-color: #ffffff;
|
border-top: 1px solid #ebeef5;
|
border-radius: 0 0 12px 12px;
|
}
|
|
:deep(.el-button) {
|
transition: all 0.3s ease;
|
|
&:hover {
|
transform: translateY(-1px);
|
}
|
}
|
|
:deep(.el-dialog) {
|
border-radius: 6px;
|
overflow: hidden;
|
|
.el-dialog__header {
|
background-color: #fafafa;
|
border-bottom: 1px solid #ebeef5;
|
padding: 20px 24px;
|
|
.el-dialog__title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #303133;
|
}
|
}
|
|
.el-dialog__body {
|
padding: 24px;
|
}
|
|
.el-dialog__footer {
|
padding: 16px 24px;
|
border-top: 1px solid #ebeef5;
|
background-color: #fafafa;
|
}
|
}
|
|
:deep(.el-form) {
|
.el-form-item {
|
margin-bottom: 20px;
|
|
.el-form-item__label {
|
font-weight: 500;
|
color: #303133;
|
}
|
|
.el-input,
|
.el-select,
|
.el-date-picker,
|
.el-input-number {
|
width: 100%;
|
|
// .el-input__inner {
|
// border-radius: 6px;
|
// border: 1px solid #dcdfe6;
|
// transition: all 0.3s ease;
|
|
// &:focus {
|
// border-color: #409eff;
|
// box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
// }
|
// }
|
}
|
}
|
}
|
|
:deep(.el-tag) {
|
border-radius: 4px;
|
padding: 2px 8px;
|
font-size: 12px;
|
}
|
|
@media (max-width: 768px) {
|
.app-container {
|
padding: 16px;
|
}
|
|
.search_form {
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 12px;
|
|
.el-form {
|
width: 100%;
|
|
.el-form-item {
|
width: 100%;
|
}
|
}
|
|
> div {
|
width: 100%;
|
display: flex;
|
gap: 12px;
|
|
.el-button {
|
flex: 1;
|
}
|
}
|
}
|
|
:deep(.el-table) {
|
th,
|
td {
|
padding: 10px 0;
|
font-size: 12px;
|
}
|
}
|
|
:deep(.el-dialog) {
|
width: 90% !important;
|
margin: 20px auto !important;
|
}
|
}
|
.consumption-value {
|
font-weight: bold;
|
color: #409eff;
|
}
|
|
.consumption-unit {
|
font-size: 12px;
|
color: #909399;
|
margin-left: 4px;
|
}
|
// .search_form {
|
// :deep(.el-form-item) {
|
// margin-bottom: 0px !important;
|
// }
|
// }
|
:deep(.el-table .el-table__body-wrapper tr td) {
|
background-color: #fff;
|
}
|
</style>
|