| | |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="车间:"> |
| | | <el-select v-model="searchForm.workshop" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 160px;" |
| | | @change="handleQuery"> |
| | | <el-option label="车间1" |
| | | value="1" /> |
| | | <el-option label="车间2" |
| | | value="2" /> |
| | | <el-option label="车间3" |
| | | value="3" /> |
| | | </el-select> |
| | | <el-form-item label="客户名称:"> |
| | | <el-input v-model="searchForm.customerName" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="状态:"> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 160px;" |
| | | @change="handleQuery"> |
| | | <el-option label="待处理" |
| | | value="pending" /> |
| | | <el-option label="进行中" |
| | | value="processing" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | </el-select> |
| | | <el-form-item label="产品名称:"> |
| | | <el-input v-model="searchForm.productName" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品规格:"> |
| | | <el-input v-model="searchForm.specification" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="物料编码:"> |
| | | <el-input v-model="searchForm.materialCode" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="申请单编号:"> |
| | | <el-input v-model="searchForm.applyNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划日期范围:"> |
| | | <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> |
| | | <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="getLoadProdData">拉取数据</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> |
| | | <el-button type="primary" |
| | | @click="handleMerge">和并下发</el-button> |
| | | <el-button type="info" |
| | | @click="showCategorySummaryDialog = true">产品类别汇总</el-button> |
| | | <!-- <el-button type="danger" |
| | | @click="handleDelete">删除</el-button> --> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | height="calc(100vh - 350px)" |
| | | :tableLoading="tableLoading" |
| | | :isSelection="true" |
| | | :selectable="isSelectable" |
| | |
| | | <!-- 合并下发弹窗 --> |
| | | <el-dialog v-model="isShowNewModal" |
| | | title="合并下发" |
| | | width="500px"> |
| | | width="600px"> |
| | | <el-form :model="mergeForm" |
| | | label-width="120px"> |
| | | <el-form-item label="生产计划号"> |
| | | <el-input v-model="mergeForm.productionPlanNo" |
| | | disabled /> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="10"> |
| | | <el-form-item label="物料编码"> |
| | | <div class="info-display">{{ mergeForm.materialCode || '-' }}</div> |
| | | </el-form-item> |
| | | </el-col> |
| | | <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 :span="10"> |
| | | <el-form-item label="产品规格"> |
| | | <div class="info-display">{{ mergeForm.specification || '-' }}</div> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="10"> |
| | | <el-form-item label="长*宽*高"> |
| | | <div class="info-display">{{ mergeForm.length || '-' }}*{{ mergeForm.width || '-' }}*{{ mergeForm.height || '-' }}</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.totalManufactureQuantity" |
| | | :min="1" |
| | | :step="1" |
| | | <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-item label="备注"> |
| | | <!-- <el-form-item label="备注"> |
| | | <el-input v-model="mergeForm.remark" |
| | | type="textarea" /> |
| | | </el-form-item> |
| | | </el-form-item> --> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 产品类别汇总弹窗 --> |
| | | <el-dialog v-model="showCategorySummaryDialog" |
| | | title="产品类别汇总统计" |
| | | width="400px"> |
| | | <el-table :data="categorySummary" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="productCategory" |
| | | label="产品类别" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="totalManufactureQuantity" |
| | | label="总制造数量" |
| | | align="center" /> |
| | | </el-table> |
| | | <!-- 追踪进度弹窗 --> |
| | | <el-dialog v-model="showTrackProgressDialog" |
| | | :title="`追踪进度 - ${trackProgressForm.materialCode || ''}`" |
| | | width="600px"> |
| | | <el-form :model="trackProgressForm" |
| | | label-width="120px"> |
| | | <el-form-item label="物料编码"> |
| | | <el-input v-model="trackProgressForm.materialCode" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="当前状态"> |
| | | <el-select v-model="trackProgressForm.currentStatus" |
| | | placeholder="请选择状态"> |
| | | <el-option label="待处理" |
| | | value="pending" /> |
| | | <el-option label="进行中" |
| | | value="processing" /> |
| | | <el-option label="已完成" |
| | | value="completed" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="完成进度"> |
| | | <el-progress :percentage="trackProgressForm.completionRate" |
| | | :status="trackProgressForm.completionRate === 100 ? 'success' : ''" /> |
| | | </el-form-item> |
| | | <el-form-item label="进度详情"> |
| | | <el-table :data="trackProgressForm.progressDetails" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="step" |
| | | label="步骤" |
| | | align="center" |
| | | width="100" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'completed' ? 'success' : scope.row.status === 'processing' ? 'warning' : 'info'"> |
| | | {{ scope.row.status === 'completed' ? '已完成' : scope.row.status === 'processing' ? '进行中' : '待开始' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="startTime" |
| | | label="开始时间" |
| | | align="center" |
| | | width="180" /> |
| | | <el-table-column prop="endTime" |
| | | label="结束时间" |
| | | align="center" |
| | | width="180" /> |
| | | </el-table> |
| | | </el-form-item> |
| | | <el-form-item label="备注"> |
| | | <el-input v-model="trackProgressForm.remark" |
| | | type="textarea" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="showCategorySummaryDialog = false">关闭</el-button> |
| | | <el-button @click="showTrackProgressDialog = false">关闭</el-button> |
| | | <el-button type="primary" |
| | | @click="handleUpdateProgress">更新进度</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" |
| | | :title="operationType === 'add' ? '新增生产计划' : '编辑生产计划'" |
| | | width="600px"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="120px"> |
| | | <el-form-item label="申请单编号" |
| | | prop="applyNo"> |
| | | <el-input v-model="form.applyNo" |
| | | placeholder="请输入申请单编号" /> |
| | | </el-form-item> |
| | | <el-form-item label="客户名称" |
| | | prop="customerName"> |
| | | <el-input v-model="form.customerName" |
| | | placeholder="请输入客户名称" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品名称" |
| | | prop="productMaterialId"> |
| | | <el-tree-select v-model="form.productMaterialId" |
| | | placeholder="请选择" |
| | | clearable |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | filterable |
| | | @change="handleProductChange" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品规格" |
| | | prop="productMaterialSkuId"> |
| | | <el-select v-model="form.productMaterialSkuId" |
| | | @change="handleChangeSpecification" |
| | | filterable |
| | | placeholder="请选择"> |
| | | <el-option v-for="item in specificationOptions" |
| | | :key="item.skuId" |
| | | :label="item.specification" |
| | | :value="item.skuId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="块数" |
| | | prop="quantity"> |
| | | <el-input-number v-model="form.quantity" |
| | | :min="0" |
| | | placeholder="请输入块数" /> |
| | | </el-form-item> |
| | | <el-form-item label="方数" |
| | | prop="volume"> |
| | | <el-input-number v-model="form.volume" |
| | | :min="0" |
| | | placeholder="请输入方数" /> |
| | | </el-form-item> |
| | | <el-form-item label="长" |
| | | prop="length"> |
| | | <el-input-number v-model="form.length" |
| | | :min="0" |
| | | placeholder="请输入长度" /> |
| | | </el-form-item> |
| | | <el-form-item label="宽" |
| | | prop="width"> |
| | | <el-input-number v-model="form.width" |
| | | :min="0" |
| | | placeholder="请输入宽度" /> |
| | | </el-form-item> |
| | | <el-form-item label="高" |
| | | prop="height"> |
| | | <el-input-number v-model="form.height" |
| | | :min="0" |
| | | placeholder="请输入高度" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划开始日期" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="form.startDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="请选择计划开始日期" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划结束日期" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="form.endDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | placeholder="请选择计划结束日期" /> |
| | | </el-form-item> |
| | | <el-form-item label="强度" |
| | | prop="strength"> |
| | | <el-select v-model="form.strength" |
| | | placeholder="请选择强度" |
| | | style="width: 100%"> |
| | | <el-option label="A3.5" |
| | | value="A3.5" /> |
| | | <el-option label="A5.0" |
| | | value="A5.0" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="备注 1" |
| | | prop="remarkOne"> |
| | | <el-input v-model="form.remarkOne" |
| | | placeholder="请输入备注 1" /> |
| | | </el-form-item> |
| | | <el-form-item label="备注 2" |
| | | prop="remarkTwo"> |
| | | <el-input v-model="form.remarkTwo" |
| | | placeholder="请输入备注 2" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from "vue"; |
| | | import { onMounted, ref, reactive, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import { productOrderListPage } from "@/api/productionManagement/productionOrder.js"; |
| | | import ImportDialog from "@/components/Dialog/ImportDialog.vue"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import { |
| | | productionPlanListPage, |
| | | loadProdData, |
| | | exportProductionPlan, |
| | | productionPlanAdd, |
| | | productionPlanUpdate, |
| | | productionPlanDelete, |
| | | productionPlanCombine, |
| | | } from "@/api/productionPlan/productionPlan.js"; |
| | | import PIMTable from "./components/PIMTable.vue"; |
| | | import { |
| | | modelListPage, |
| | | productTreeList, |
| | | productTreeListQuery, |
| | | } from "@/api/basicData/newProduct.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "来源", |
| | | prop: "source", |
| | | label: "数据来源", |
| | | width: "100px", |
| | | prop: "dataSourceType", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 2: "warning", |
| | | 1: "primary", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | }, |
| | | formatData: cell => (cell == 1 ? "钉钉同步" : "手动新增"), |
| | | }, |
| | | { |
| | | label: "状态", |
| | | prop: "status", |
| | | width: "80px", |
| | | }, |
| | | { |
| | | label: "审核状态", |
| | | prop: "auditStatus", |
| | | width: "100px", |
| | | }, |
| | | { |
| | | label: "订单号", |
| | | prop: "orderNo", |
| | | width: "120px", |
| | | }, |
| | | { |
| | | label: "生产计划号", |
| | | prop: "productionPlanNo", |
| | | width: "140px", |
| | | }, |
| | | { |
| | | label: "零件号", |
| | | prop: "partNo", |
| | | width: "120px", |
| | | }, |
| | | { |
| | | label: "零件", |
| | | prop: "partName", |
| | | width: "120px", |
| | | }, |
| | | { |
| | | label: "产品类别", |
| | | prop: "productCategory", |
| | | width: "100px", |
| | | }, |
| | | { |
| | | label: "工艺文件号", |
| | | prop: "processFileNo", |
| | | width: "140px", |
| | | }, |
| | | { |
| | | label: "销售数量", |
| | | prop: "salesQuantity", |
| | | width: "100px", |
| | | align: "right", |
| | | }, |
| | | { |
| | | label: "制造数量", |
| | | prop: "manufactureQuantity", |
| | | width: "100px", |
| | | align: "right", |
| | | }, |
| | | { |
| | | label: "零件单位", |
| | | prop: "partUnit", |
| | | width: "80px", |
| | | }, |
| | | { |
| | | label: "主计划需求日期", |
| | | prop: "mainPlanDemandDate", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: "140px", |
| | | }, |
| | | { |
| | | label: "承诺日期", |
| | | prop: "commitmentDate", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: "120px", |
| | | }, |
| | | { |
| | | label: "制造属性", |
| | | prop: "manufactureProperty", |
| | | width: "100px", |
| | | }, |
| | | { |
| | | label: "备注", |
| | | prop: "remark", |
| | | label: "申请单编号", |
| | | prop: "applyNo", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "更新时间", |
| | | prop: "updateTime", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: "120px", |
| | | label: "客户名称", |
| | | prop: "customerName", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "更新人", |
| | | prop: "updateBy", |
| | | width: "100px", |
| | | label: "产品名称", |
| | | prop: "productName", |
| | | width: "200px", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | // const typeMap = { |
| | | // 板材: "primary", |
| | | // 砌块: "warning", |
| | | // }; |
| | | // return typeMap[params] || "info"; |
| | | return "primary"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "创建时间", |
| | | prop: "createTime", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: "120px", |
| | | label: "产品规格", |
| | | prop: "specification", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | | }, |
| | | { |
| | | label: "创建人", |
| | | prop: "createBy", |
| | | width: "100px", |
| | | label: "物料编码", |
| | | prop: "materialCode", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "块数", |
| | | prop: "quantity", |
| | | formatData: cell => (cell ? `${cell}块` : ""), |
| | | }, |
| | | { |
| | | label: "方数", |
| | | prop: "volume", |
| | | width: "150px", |
| | | className: "volume-cell", |
| | | formatData: cell => (cell ? `${cell}方` : ""), |
| | | }, |
| | | { |
| | | label: "下发状态", |
| | | prop: "status", |
| | | width: "150px", |
| | | 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: "assignedQuantity", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | | formatData: cell => (cell ? `${cell}方` : 0), |
| | | }, |
| | | { |
| | | label: "长", |
| | | prop: "length", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "宽", |
| | | prop: "width", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "高", |
| | | prop: "height", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | // { |
| | | // label: "流水号", |
| | | // prop: "serialNo", |
| | | // width: "150px", |
| | | // className: "code-cell", |
| | | // }, |
| | | { |
| | | label: "计划开始日期", |
| | | prop: "startDate", |
| | | width: "150px", |
| | | className: "date-cell", |
| | | formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), |
| | | }, |
| | | { |
| | | label: "计划结束日期", |
| | | prop: "endDate", |
| | | width: "150px", |
| | | className: "date-cell", |
| | | formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), |
| | | }, |
| | | { |
| | | label: "强度", |
| | | prop: "strength", |
| | | }, |
| | | |
| | | { |
| | | label: "备注 1", |
| | | width: "150px", |
| | | prop: "remarkOne", |
| | | }, |
| | | { |
| | | label: "备注 2", |
| | | width: "150px", |
| | | prop: "remarkTwo", |
| | | }, |
| | | |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 200, |
| | | width: 300, |
| | | operation: [ |
| | | { |
| | | name: "编辑", |
| | | type: "primary", |
| | | link: true, |
| | | showHide: row => { |
| | | return row.status == 0; |
| | | //status,0:待下发,1:部分下发,2:已下发 |
| | | }, |
| | | clickFun: row => { |
| | | handleEdit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "删除", |
| | | type: "danger", |
| | | link: true, |
| | | showHide: row => { |
| | | return row.status == 0; |
| | | }, |
| | | clickFun: row => { |
| | | handleDelete(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "下发", |
| | | type: "text", |
| | | showHide: row => { |
| | | // 计算剩余方数 |
| | | const remainingVolume = |
| | | (row.volume || 0) - (row.assignedQuantity || 0); |
| | | // 如果剩余方数小于等于0,禁止选择 |
| | | return remainingVolume > 0; |
| | | }, |
| | | clickFun: row => { |
| | | // 单独下发操作 |
| | | // 设置表单数据 |
| | | mergeForm.productionPlanNo = row.productionPlanNo; |
| | | mergeForm.totalManufactureQuantity = row.manufactureQuantity; |
| | | mergeForm.remark = ""; |
| | | |
| | | mergeForm.ids = [row.id]; |
| | | mergeForm.materialCode = row.materialCode; |
| | | mergeForm.productName = row.productName || ""; |
| | | mergeForm.specification = row.specification || ""; |
| | | mergeForm.length = row.length || 0; |
| | | mergeForm.width = row.width || 0; |
| | | mergeForm.height = row.height || 0; |
| | | mergeForm.totalAssignedQuantity = |
| | | (Number(row.volume) - Number(row.assignedQuantity)).toFixed(4) || 0; |
| | | mergeForm.planCompleteTime = row.planCompleteTime || ""; |
| | | sumAssignedQuantity.value = mergeForm.totalAssignedQuantity; |
| | | // 打开弹窗 |
| | | isShowNewModal.value = true; |
| | | }, |
| | |
| | | name: "追踪进度", |
| | | type: "text", |
| | | clickFun: row => { |
| | | // 追踪进度操作 |
| | | ElMessage.warning("追踪进度功能待开发"); |
| | | handleTrackProgress(row); |
| | | }, |
| | | }, |
| | | ], |
| | |
| | | const isShowNewModal = ref(false); |
| | | // 合并下发表单数据 |
| | | const mergeForm = reactive({ |
| | | productionPlanNo: "", |
| | | totalManufactureQuantity: 0, |
| | | materialCode: "", |
| | | productName: "", |
| | | specification: "", |
| | | length: 0, |
| | | width: 0, |
| | | height: 0, |
| | | totalAssignedQuantity: 0, |
| | | planCompleteTime: "", |
| | | }); |
| | | |
| | | // 追踪进度弹窗控制 |
| | | const showTrackProgressDialog = ref(false); |
| | | // 追踪进度表单数据 |
| | | const trackProgressForm = reactive({ |
| | | materialCode: "", |
| | | currentStatus: "", |
| | | completionRate: 0, |
| | | progressDetails: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | // 导入相关 |
| | | 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 formRef = ref(null); |
| | | const form = reactive({ |
| | | id: undefined, |
| | | applyNo: "", |
| | | customerName: "", |
| | | productMaterialId: undefined, |
| | | productMaterialSkuId: undefined, |
| | | productName: "", |
| | | specification: "", |
| | | materialCode: "", |
| | | quantity: 0, |
| | | volume: 0, |
| | | length: 0, |
| | | width: 0, |
| | | height: 0, |
| | | startDate: "", |
| | | endDate: "", |
| | | strength: "", |
| | | remarkOne: "", |
| | | remarkTwo: "", |
| | | }); |
| | | const rules = reactive({ |
| | | applyNo: [{ required: true, message: "请输入申请单编号", trigger: "blur" }], |
| | | customerName: [ |
| | | { required: true, message: "请输入客户名称", trigger: "blur" }, |
| | | ], |
| | | productMaterialSkuId: [ |
| | | { required: true, message: "请选择产品规格", trigger: "change" }, |
| | | ], |
| | | volume: [{ required: true, message: "请输入方数", trigger: "blur" }], |
| | | productMaterialId: [ |
| | | { required: true, message: "请选择产品", trigger: "change" }, |
| | | ], |
| | | }); |
| | | |
| | | // 处理追踪进度按钮点击 |
| | | const handleTrackProgress = row => { |
| | | // 设置表单数据 |
| | | trackProgressForm.materialCode = row.materialCode; |
| | | trackProgressForm.currentStatus = row.status; |
| | | |
| | | // 生成模拟进度数据 |
| | | trackProgressForm.progressDetails = generateProgressDetails(row.status); |
| | | |
| | | // 计算完成率 |
| | | trackProgressForm.completionRate = calculateCompletionRate( |
| | | trackProgressForm.progressDetails |
| | | ); |
| | | trackProgressForm.remark = ""; |
| | | |
| | | // 打开弹窗 |
| | | showTrackProgressDialog.value = true; |
| | | }; |
| | | const onBlur = value => { |
| | | // 限制四位小数 |
| | | mergeForm.totalAssignedQuantity = Number(value.toFixed(4)); |
| | | }; |
| | | |
| | | const fetchProductOptions = () => { |
| | | return productTreeList({ type: 2 }).then(res => { |
| | | productOptions.value = convertIdToValue(res.data); |
| | | return res; |
| | | }); |
| | | }; |
| | | |
| | | const convertIdToValue = data => { |
| | | return data.map(item => { |
| | | const newItem = { |
| | | value: `config_${item.configId}`, // 使用config_前缀确保唯一性 |
| | | label: item.configName, |
| | | disabled: item.materialList.length === 0, |
| | | }; |
| | | if (item.materialList && item.materialList.length > 0) { |
| | | newItem.children = item.materialList.map(material => ({ |
| | | value: material.id, // 使用material的id作为value |
| | | label: material.materialName, // 使用materialName作为label |
| | | })); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | }; |
| | | |
| | | const handleProductChange = value => { |
| | | form.productMaterialSkuId = undefined; |
| | | fetchSpecificationOptions(value); |
| | | }; |
| | | |
| | | const fetchSpecificationOptions = materialId => { |
| | | specificationOptions.value = []; |
| | | if (materialId) { |
| | | modelListPage({ materialId: materialId, size: -1, current: -1 }).then( |
| | | res => { |
| | | specificationOptions.value = res.data.records; |
| | | } |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | const handleChangeSpecification = value => { |
| | | form.materialCode = undefined; |
| | | const selectedModel = specificationOptions.value.find( |
| | | item => item.skuId === value |
| | | ); |
| | | if (selectedModel) { |
| | | form.materialCode = selectedModel.materialCode; |
| | | // 解析规格字符串获取长宽高 |
| | | const specification = selectedModel.specification; |
| | | if (specification) { |
| | | const dimensions = specification.match(/^(\d+)\*(\d+)\*(\d+)$/); |
| | | if (dimensions && dimensions.length === 4) { |
| | | form.length = parseInt(dimensions[1]); |
| | | form.width = parseInt(dimensions[2]); |
| | | form.height = parseInt(dimensions[3]); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 生成模拟进度详情数据 |
| | | const generateProgressDetails = status => { |
| | | const details = [ |
| | | { |
| | | step: "计划确认", |
| | | status: "completed", |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "物料准备", |
| | | status: |
| | | status === "completed" |
| | | ? "completed" |
| | | : status === "processing" |
| | | ? "completed" |
| | | : "pending", |
| | | startTime: |
| | | status === "completed" || status === "processing" |
| | | ? "2026-03-01 10:30:00" |
| | | : "", |
| | | endTime: |
| | | status === "completed" || status === "processing" |
| | | ? "2026-03-02 16:00:00" |
| | | : "", |
| | | }, |
| | | { |
| | | step: "生产加工", |
| | | status: |
| | | status === "completed" |
| | | ? "completed" |
| | | : status === "processing" |
| | | ? "processing" |
| | | : "pending", |
| | | startTime: |
| | | status === "completed" || status === "processing" |
| | | ? "2026-03-03 08:00:00" |
| | | : "", |
| | | endTime: status === "completed" ? "2026-03-08 17:00:00" : "", |
| | | }, |
| | | { |
| | | step: "质量检验", |
| | | status: status === "completed" ? "completed" : "pending", |
| | | startTime: status === "completed" ? "2026-03-09 09:00:00" : "", |
| | | endTime: status === "completed" ? "2026-03-09 15:00:00" : "", |
| | | }, |
| | | { |
| | | step: "入库", |
| | | status: status === "completed" ? "completed" : "pending", |
| | | startTime: status === "completed" ? "2026-03-10 10:00:00" : "", |
| | | endTime: status === "completed" ? "2026-03-10 11:00:00" : "", |
| | | }, |
| | | ]; |
| | | return details; |
| | | }; |
| | | |
| | | // 计算完成率 |
| | | const calculateCompletionRate = details => { |
| | | const completedSteps = details.filter( |
| | | step => step.status === "completed" |
| | | ).length; |
| | | return Math.round((completedSteps / details.length) * 100); |
| | | }; |
| | | |
| | | // 处理进度更新 |
| | | const handleUpdateProgress = () => { |
| | | // 这里可以添加更新进度的逻辑 |
| | | ElMessage.success("进度更新成功"); |
| | | showTrackProgressDialog.value = false; |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | customerName: "", |
| | | salesContractNo: "", |
| | | projectName: "", |
| | | productCategory: "", |
| | | specificationModel: "", |
| | | productName: "", |
| | | specification: "", |
| | | materialCode: "", |
| | | applyNo: "", |
| | | dateRange: [], |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | /** 重置按钮操作 */ |
| | | const handleReset = () => { |
| | | Object.assign(searchForm.value, { |
| | | customerName: "", |
| | | productName: "", |
| | | specification: "", |
| | | materialCode: "", |
| | | applyNo: "", |
| | | dateRange: [], |
| | | }); |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | |
| | | |
| | | // 遍历表格数据,按产品类别汇总 |
| | | tableData.value.forEach(row => { |
| | | const category = row.productCategory; |
| | | const category = row.materialCode; |
| | | if (!summary[category]) { |
| | | summary[category] = { |
| | | productCategory: category, |
| | | totalManufactureQuantity: 0, |
| | | materialCode: category, |
| | | totalAssignedQuantity: 0, |
| | | }; |
| | | } |
| | | summary[category].totalManufactureQuantity += row.manufactureQuantity; |
| | | summary[category].totalAssignedQuantity += ( |
| | | Number(row.volume) - Number(row.assignedQuantity) |
| | | ).toFixed(4); |
| | | }); |
| | | |
| | | // 转换为数组格式 |
| | |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | // 构造一个新的对象,不包含entryDate字段 |
| | | // 构造搜索参数 |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | tableData.value = [ |
| | | { |
| | | id: 1, |
| | | source: "销售订单", |
| | | status: "待处理", |
| | | auditStatus: "已审核", |
| | | orderNo: "SO20260301001", |
| | | productionPlanNo: "PP20260301001", |
| | | partNo: "P001", |
| | | partName: "零件A", |
| | | productCategory: "类别1", |
| | | processFileNo: "PF20260301001", |
| | | salesQuantity: 100, |
| | | manufactureQuantity: 105, |
| | | partUnit: "个", |
| | | mainPlanDemandDate: "2026-03-15", |
| | | commitmentDate: "2026-03-10", |
| | | manufactureProperty: "常规", |
| | | remark: "", |
| | | updateTime: "2026-03-01", |
| | | updateBy: "admin", |
| | | createTime: "2026-03-01", |
| | | createBy: "admin", |
| | | }, |
| | | { |
| | | id: 2, |
| | | source: "销售订单", |
| | | status: "待处理", |
| | | auditStatus: "已审核", |
| | | orderNo: "SO20260301002", |
| | | productionPlanNo: "PP20260301001", |
| | | partNo: "P002", |
| | | partName: "零件B", |
| | | productCategory: "类别1", |
| | | processFileNo: "PF20260301002", |
| | | salesQuantity: 200, |
| | | manufactureQuantity: 210, |
| | | partUnit: "个", |
| | | mainPlanDemandDate: "2026-03-15", |
| | | commitmentDate: "2026-03-10", |
| | | manufactureProperty: "常规", |
| | | remark: "", |
| | | updateTime: "2026-03-01", |
| | | updateBy: "admin", |
| | | createTime: "2026-03-01", |
| | | createBy: "admin", |
| | | }, |
| | | { |
| | | id: 3, |
| | | source: "销售订单", |
| | | status: "进行中", |
| | | auditStatus: "已审核", |
| | | orderNo: "SO20260301003", |
| | | productionPlanNo: "PP20260301002", |
| | | partNo: "P003", |
| | | partName: "零件C", |
| | | productCategory: "类别2", |
| | | processFileNo: "PF20260301003", |
| | | salesQuantity: 150, |
| | | manufactureQuantity: 155, |
| | | partUnit: "个", |
| | | mainPlanDemandDate: "2026-03-20", |
| | | commitmentDate: "2026-03-15", |
| | | manufactureProperty: "常规", |
| | | remark: "", |
| | | updateTime: "2026-03-01", |
| | | updateBy: "admin", |
| | | createTime: "2026-03-01", |
| | | createBy: "admin", |
| | | }, |
| | | { |
| | | id: 4, |
| | | source: "销售订单", |
| | | status: "进行中", |
| | | auditStatus: "已审核", |
| | | orderNo: "SO20260301004", |
| | | productionPlanNo: "PP20260301002", |
| | | partNo: "P004", |
| | | partName: "零件D", |
| | | productCategory: "类别2", |
| | | processFileNo: "PF20260301004", |
| | | salesQuantity: 300, |
| | | manufactureQuantity: 315, |
| | | partUnit: "个", |
| | | mainPlanDemandDate: "2026-03-20", |
| | | commitmentDate: "2026-03-15", |
| | | manufactureProperty: "常规", |
| | | remark: "", |
| | | updateTime: "2026-03-01", |
| | | updateBy: "admin", |
| | | createTime: "2026-03-01", |
| | | createBy: "admin", |
| | | }, |
| | | { |
| | | id: 5, |
| | | source: "销售订单", |
| | | status: "已完成", |
| | | auditStatus: "已审核", |
| | | orderNo: "SO20260301005", |
| | | productionPlanNo: "PP20260301003", |
| | | partNo: "P005", |
| | | partName: "零件E", |
| | | productCategory: "类别3", |
| | | processFileNo: "PF20260301005", |
| | | salesQuantity: 250, |
| | | manufactureQuantity: 260, |
| | | partUnit: "个", |
| | | mainPlanDemandDate: "2026-03-10", |
| | | commitmentDate: "2026-03-05", |
| | | manufactureProperty: "常规", |
| | | remark: "", |
| | | updateTime: "2026-03-01", |
| | | updateBy: "admin", |
| | | createTime: "2026-03-01", |
| | | createBy: "admin", |
| | | }, |
| | | ]; |
| | | tableLoading.value = false; |
| | | page.total = tableData.value.length; |
| | | // 计算产品类别汇总统计 |
| | | calculateCategorySummary(); |
| | | // productOrderListPage(params) |
| | | // .then(res => { |
| | | // tableLoading.value = false; |
| | | params.startDate = params.dateRange ? params.dateRange[0] : ""; |
| | | params.endDate = params.dateRange ? 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; |
| | | // }); |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | // 计算产品类别汇总统计 |
| | | calculateCategorySummary(); |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 选中的生产计划号 |
| | | const selectedProductionPlanNo = ref(""); |
| | | // 选中的序列号 |
| | | const selectedserialNo = ref(""); |
| | | |
| | | // 表格选择数据 |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | // 如果有选中的行,记录第一个选中行的生产计划号 |
| | | // 如果有选中的行,记录第一个选中行的序列号 |
| | | if (selection.length > 0) { |
| | | selectedProductionPlanNo.value = selection[0].productionPlanNo; |
| | | selectedserialNo.value = selection[0].materialCode; |
| | | } else { |
| | | // 如果没有选中的行,清空生产计划号 |
| | | selectedProductionPlanNo.value = ""; |
| | | // 如果没有选中的行,清空序列号 |
| | | selectedserialNo.value = ""; |
| | | } |
| | | }; |
| | | |
| | | // 判断行是否可选择 |
| | | const isSelectable = row => { |
| | | // 计算剩余方数 |
| | | const remainingVolume = (row.volume || 0) - (row.assignedQuantity || 0); |
| | | // 如果剩余方数小于等于0,禁止选择 |
| | | if (remainingVolume <= 0) { |
| | | return false; |
| | | } |
| | | // 如果没有选中的行,所有行都可选择 |
| | | if (!selectedProductionPlanNo.value) { |
| | | if (!selectedserialNo.value) { |
| | | return true; |
| | | } |
| | | // 如果有选中的行,只有生产计划号相同的行才可选择 |
| | | return row.productionPlanNo === selectedProductionPlanNo.value; |
| | | // 如果有选中的行,只有序列号相同的行才可选择 |
| | | return row.materialCode === selectedserialNo.value; |
| | | }; |
| | | |
| | | // 拉取数据按钮操作 |
| | | const getLoadProdData = () => { |
| | | loadProdData() |
| | | .then(res => { |
| | | getList(); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | const sumAssignedQuantity = ref(0); |
| | | // 处理合并下发按钮点击 |
| | | const handleMerge = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | ElMessage.warning("请选择要合并下发的生产计划"); |
| | | return; |
| | | } |
| | | |
| | | console.log(selectedRows.value); |
| | | // 计算总制造数量 |
| | | const totalQuantity = selectedRows.value.reduce((sum, row) => { |
| | | return sum + row.manufactureQuantity; |
| | | const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => { |
| | | return ( |
| | | sum + |
| | | (row.volume == null |
| | | ? 0 |
| | | : (Number(row.volume) - Number(row.assignedQuantity)).toFixed(4)) |
| | | ); |
| | | }, 0); |
| | | |
| | | sumAssignedQuantity.value = totalAssignedQuantity; |
| | | console.log(totalAssignedQuantity); |
| | | // 设置表单数据 |
| | | mergeForm.productionPlanNo = selectedProductionPlanNo.value; |
| | | mergeForm.totalManufactureQuantity = totalQuantity; |
| | | mergeForm.remark = ""; |
| | | const firstRow = selectedRows.value[0]; |
| | | mergeForm.materialCode = selectedserialNo.value; |
| | | mergeForm.productName = firstRow.productName || ""; |
| | | mergeForm.specification = firstRow.specification || ""; |
| | | mergeForm.length = firstRow.length || 0; |
| | | mergeForm.width = firstRow.width || 0; |
| | | mergeForm.height = firstRow.height || 0; |
| | | mergeForm.totalAssignedQuantity = totalAssignedQuantity; |
| | | mergeForm.planCompleteTime = firstRow.planCompleteTime || ""; |
| | | mergeForm.ids = selectedRows.value.map(row => row.id); |
| | | |
| | | // 打开弹窗 |
| | | isShowNewModal.value = true; |
| | |
| | | |
| | | // 处理合并下发提交 |
| | | const handleMergeSubmit = () => { |
| | | // 这里可以添加下发逻辑 |
| | | ElMessage.success("合并下发成功"); |
| | | isShowNewModal.value = false; |
| | | if (mergeForm.totalAssignedQuantity === 0) { |
| | | ElMessage.warning("请输入生产方数"); |
| | | return; |
| | | } |
| | | console.log(sumAssignedQuantity.value, "sumAssignedQuantity"); |
| | | // 计算当前选中行的总方数 |
| | | const totalVolume = selectedRows.value.reduce((sum, row) => { |
| | | return sum + (Number(row.volume) - Number(row.assignedQuantity) || 0); |
| | | }, 0); |
| | | |
| | | // 验证totalAssignedQuantity不能大于总方数 |
| | | if (mergeForm.totalAssignedQuantity > sumAssignedQuantity.value) { |
| | | ElMessage.error("生产方数不能大于当前计算的总值"); |
| | | return; |
| | | } |
| | | |
| | | console.log(mergeForm, "mergeForm"); |
| | | productionPlanCombine(mergeForm) |
| | | .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.message || "导入失败"); |
| | | } |
| | | }; |
| | | |
| | | // 导入失败 |
| | | 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, { |
| | | applyNo: "", |
| | | customerName: "", |
| | | productName: "", |
| | | productMaterialId: undefined, |
| | | productMaterialSkuId: undefined, |
| | | specification: "", |
| | | materialCode: "", |
| | | quantity: 0, |
| | | volume: 0, |
| | | length: 0, |
| | | width: 0, |
| | | height: 0, |
| | | startDate: "", |
| | | endDate: "", |
| | | strength: "", |
| | | remarkOne: "", |
| | | remarkTwo: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | fetchProductOptions(); |
| | | }; |
| | | |
| | | // 编辑 |
| | | const handleEdit = row => { |
| | | operationType.value = "edit"; |
| | | Object.assign(form, { |
| | | id: row.id, |
| | | applyNo: row.applyNo || "", |
| | | customerName: row.customerName || "", |
| | | productName: row.productName || "", |
| | | productMaterialId: row.productMaterialId || undefined, |
| | | productMaterialSkuId: row.productMaterialSkuId || undefined, |
| | | specification: row.specification || "", |
| | | materialCode: row.materialCode || "", |
| | | quantity: row.quantity || 0, |
| | | volume: row.volume || 0, |
| | | length: row.length || 0, |
| | | width: row.width || 0, |
| | | height: row.height || 0, |
| | | startDate: row.startDate || "", |
| | | endDate: row.endDate || "", |
| | | strength: row.strength || "", |
| | | remarkOne: row.remarkOne || "", |
| | | remarkTwo: row.remarkTwo || "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | fetchProductOptions(); |
| | | fetchSpecificationOptions(row.productMaterialId); |
| | | }; |
| | | |
| | | // 删除 |
| | | 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.volume === 0) { |
| | | proxy.$modal.msgError("方数不能为0"); |
| | | return; |
| | | } |
| | | if (form.v === "add") { |
| | | payload.id = null; |
| | | } |
| | | const payload = { ...form }; |
| | | if (operationType.value === "add") { |
| | | payload.id = null; |
| | | productionPlanAdd(payload) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess( |
| | | operationType.value === "add" ? "新增成功" : "修改成功" |
| | | ); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError( |
| | | operationType.value === "add" ? "新增失败" : "修改失败" |
| | | ); |
| | | }); |
| | | } |
| | | if (operationType.value === "edit") { |
| | | productionPlanUpdate(payload) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess( |
| | | operationType.value === "add" ? "新增成功" : "修改成功" |
| | | ); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError( |
| | | operationType.value === "add" ? "新增失败" : "修改失败" |
| | | ); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | padding: 24px; |
| | | background-color: #f0f2f5; |
| | | min-height: calc(100vh - 48px); |
| | | } |
| | | |
| | | .search_form { |
| | | align-items: start; |
| | | } |
| | | |
| | | .summary-section { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .horizontal-summary { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 20px; |
| | | padding: 10px 0; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | .summary-item { |
| | | flex: 1; |
| | | min-width: 120px; |
| | | text-align: center; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | .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); |
| | | } |
| | | |
| | | :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% |
| | | ); |
| | | } |
| | | |
| | | // 数值字段样式 |
| | | .quantity-cell, |
| | | .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; |
| | | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | 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% |
| | | ); |
| | | } |
| | | } |
| | | |
| | | .summary-label { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | // 信息展示样式 |
| | | .info-display { |
| | | border-radius: 6px; |
| | | color: #303133; |
| | | font-size: 14px; |
| | | min-height: 32px; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | ::v-deep .yellow { |
| | | background-color: #faf0de; |
| | | .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; |
| | | } |
| | | |
| | | ::v-deep .pink { |
| | | background-color: #fae1de; |
| | | :deep(.el-button) { |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: translateY(-1px); |
| | | } |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | background-color: #f80202; |
| | | :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; |
| | | } |
| | | } |
| | | |
| | | ::v-deep .purple { |
| | | background-color: #f4defa; |
| | | :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; |
| | | // } |
| | | // } |
| | | </style> |