diff --git a/klp-ui/src/views/wms/index.vue b/.vscode/settings.json similarity index 100% rename from klp-ui/src/views/wms/index.vue rename to .vscode/settings.json diff --git a/klp-ui/babel.config.js b/klp-ui/babel.config.js index b99f0014..cd9772fd 100644 --- a/klp-ui/babel.config.js +++ b/klp-ui/babel.config.js @@ -1,13 +1,13 @@ module.exports = { presets: [ - // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app '@vue/cli-plugin-babel/preset' ], - 'env': { - 'development': { - // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). - // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. - 'plugins': ['dynamic-import-node'] + plugins: [ + '@babel/plugin-proposal-optional-chaining' + ], + env: { + development: { + plugins: ['dynamic-import-node'] } } } diff --git a/klp-ui/package.json b/klp-ui/package.json index 25f40b5e..c5a1f7a6 100644 --- a/klp-ui/package.json +++ b/klp-ui/package.json @@ -38,11 +38,13 @@ "@babel/parser": "7.7.4", "@riophae/vue-treeselect": "0.4.0", "axios": "0.24.0", + "bpmn-js-token-simulation": "0.10.0", "clipboard": "2.0.8", "core-js": "3.25.3", "echarts": "5.4.0", "element-ui": "2.15.12", "file-saver": "2.0.5", + "frappe-gantt": "^1.0.3", "fuse.js": "6.4.3", "highlight.js": "10.5.0", "js-beautify": "1.13.0", @@ -59,15 +61,18 @@ "vue-router": "3.4.9", "vuedraggable": "2.24.3", "vuex": "3.6.0", - "xml-js": "1.6.11", - "bpmn-js-token-simulation": "0.10.0" + "xml-js": "1.6.11" }, "devDependencies": { + "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@vue/cli-plugin-babel": "4.4.6", "@vue/cli-plugin-eslint": "4.4.6", "@vue/cli-service": "4.4.6", "babel-eslint": "10.1.0", "babel-plugin-dynamic-import-node": "2.3.3", + "bpmn-js": "7.5.0", + "bpmn-js-properties-panel": "0.37.2", + "camunda-bpmn-moddle": "4.4.1", "chalk": "4.1.0", "compression-webpack-plugin": "5.0.2", "connect": "3.6.6", @@ -79,10 +84,7 @@ "sass-loader": "10.1.1", "script-ext-html-webpack-plugin": "2.1.5", "svg-sprite-loader": "5.1.1", - "vue-template-compiler": "2.6.12", - "bpmn-js": "7.5.0", - "bpmn-js-properties-panel": "0.37.2", - "camunda-bpmn-moddle": "4.4.1" + "vue-template-compiler": "2.6.12" }, "engines": { "node": ">=8.9", diff --git a/klp-ui/src/api/wms/productionLine.js b/klp-ui/src/api/wms/productionLine.js index d9f954ea..7d8942bd 100644 --- a/klp-ui/src/api/wms/productionLine.js +++ b/klp-ui/src/api/wms/productionLine.js @@ -42,3 +42,12 @@ export function delProductionLine(lineId) { method: 'delete' }) } + +// 新增:产线甘特图接口 +export function ganttProductionLine(params) { + return request({ + url: '/wms/productionLine/gantt', + method: 'get', + params + }); +} diff --git a/klp-ui/src/api/wms/schedulePlan.js b/klp-ui/src/api/wms/schedulePlan.js new file mode 100644 index 00000000..ee4118b2 --- /dev/null +++ b/klp-ui/src/api/wms/schedulePlan.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询排产计划列表 +export function listSchedulePlan(query) { + return request({ + url: '/wms/schedulePlan/list', + method: 'get', + params: query + }) +} + +// 查询排产计划详细 +export function getSchedulePlan(planId) { + return request({ + url: '/wms/schedulePlan/' + planId, + method: 'get' + }) +} + +// 新增排产计划 +export function addSchedulePlan(data) { + return request({ + url: '/wms/schedulePlan', + method: 'post', + data: data + }) +} + +// 修改排产计划 +export function updateSchedulePlan(data) { + return request({ + url: '/wms/schedulePlan', + method: 'put', + data: data + }) +} + +// 删除排产计划 +export function delSchedulePlan(planId) { + return request({ + url: '/wms/schedulePlan/' + planId, + method: 'delete' + }) +} diff --git a/klp-ui/src/api/wms/schedulePlanDetail.js b/klp-ui/src/api/wms/schedulePlanDetail.js new file mode 100644 index 00000000..779d44c2 --- /dev/null +++ b/klp-ui/src/api/wms/schedulePlanDetail.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询排产计划明细列表 +export function listSchedulePlanDetail(query) { + return request({ + url: '/wms/schedulePlanDetail/list', + method: 'get', + params: query + }) +} + +// 查询排产计划明细详细 +export function getSchedulePlanDetail(detailId) { + return request({ + url: '/wms/schedulePlanDetail/' + detailId, + method: 'get' + }) +} + +// 新增排产计划明细 +export function addSchedulePlanDetail(data) { + return request({ + url: '/wms/schedulePlanDetail', + method: 'post', + data: data + }) +} + +// 修改排产计划明细 +export function updateSchedulePlanDetail(data) { + return request({ + url: '/wms/schedulePlanDetail', + method: 'put', + data: data + }) +} + +// 删除排产计划明细 +export function delSchedulePlanDetail(detailId) { + return request({ + url: '/wms/schedulePlanDetail/' + detailId, + method: 'delete' + }) +} diff --git a/klp-ui/src/views/wms/productionLine/GanttChart.vue b/klp-ui/src/views/wms/productionLine/GanttChart.vue new file mode 100644 index 00000000..88be2de0 --- /dev/null +++ b/klp-ui/src/views/wms/productionLine/GanttChart.vue @@ -0,0 +1,135 @@ + + + + + + + \ No newline at end of file diff --git a/klp-ui/src/views/wms/productionLine/GanttChartEcharts.vue b/klp-ui/src/views/wms/productionLine/GanttChartEcharts.vue new file mode 100644 index 00000000..19842cf6 --- /dev/null +++ b/klp-ui/src/views/wms/productionLine/GanttChartEcharts.vue @@ -0,0 +1,181 @@ + + + + + \ No newline at end of file diff --git a/klp-ui/src/views/wms/productionLine/index.vue b/klp-ui/src/views/wms/productionLine/index.vue index 01c503f8..9bc067ac 100644 --- a/klp-ui/src/views/wms/productionLine/index.vue +++ b/klp-ui/src/views/wms/productionLine/index.vue @@ -1,158 +1,182 @@ + + diff --git a/klp-ui/src/views/wms/schedulePlan/detail.vue b/klp-ui/src/views/wms/schedulePlan/detail.vue new file mode 100644 index 00000000..4059f668 --- /dev/null +++ b/klp-ui/src/views/wms/schedulePlan/detail.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/klp-ui/src/views/wms/schedulePlan/index.vue b/klp-ui/src/views/wms/schedulePlan/index.vue new file mode 100644 index 00000000..91fd033d --- /dev/null +++ b/klp-ui/src/views/wms/schedulePlan/index.vue @@ -0,0 +1,357 @@ + + + + \ No newline at end of file diff --git a/klp-ui/vue.config.js b/klp-ui/vue.config.js index 7ba9a9d9..a4091ae8 100644 --- a/klp-ui/vue.config.js +++ b/klp-ui/vue.config.js @@ -27,6 +27,9 @@ module.exports = { lintOnSave: process.env.NODE_ENV === 'development', // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 productionSourceMap: false, + transpileDependencies: [ + 'frappe-gantt' + ], // webpack-dev-server 相关配置 devServer: { host: '0.0.0.0', diff --git a/klp-wms/src/main/java/com/klp/controller/WmsProductionLineController.java b/klp-wms/src/main/java/com/klp/controller/WmsProductionLineController.java index 8e9f0efa..eee96d3c 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsProductionLineController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsProductionLineController.java @@ -1,8 +1,11 @@ package com.klp.controller; -import java.util.List; -import java.util.Arrays; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.util.*; +import com.klp.domain.bo.WmsSchedulePlanDetailBo; +import com.klp.domain.vo.*; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; @@ -18,10 +21,14 @@ import com.klp.common.core.validate.AddGroup; import com.klp.common.core.validate.EditGroup; import com.klp.common.enums.BusinessType; import com.klp.common.utils.poi.ExcelUtil; -import com.klp.domain.vo.WmsProductionLineVo; import com.klp.domain.bo.WmsProductionLineBo; import com.klp.service.IWmsProductionLineService; import com.klp.common.core.page.TableDataInfo; +import com.klp.service.IWmsSchedulePlanDetailService; +import com.klp.service.IWmsOrderService; +import com.klp.service.IWmsOrderDetailService; +import com.klp.service.IWmsProductService; +import com.klp.service.IWmsSchedulePlanService; /** * 产线 @@ -36,6 +43,11 @@ import com.klp.common.core.page.TableDataInfo; public class WmsProductionLineController extends BaseController { private final IWmsProductionLineService iWmsProductionLineService; + private final IWmsSchedulePlanDetailService iWmsSchedulePlanDetailService; + private final IWmsOrderService iWmsOrderService; + private final IWmsOrderDetailService iWmsOrderDetailService; + private final IWmsProductService iWmsProductService; + private final IWmsSchedulePlanService iWmsSchedulePlanService; /** * 查询产线列表 @@ -97,4 +109,77 @@ public class WmsProductionLineController extends BaseController { @PathVariable Long[] lineIds) { return toAjax(iWmsProductionLineService.deleteWithValidByIds(Arrays.asList(lineIds), true)); } + + /** + * 产线甘特图接口 + * @param lineId 产线ID + * @return 甘特图数据 + */ + @GetMapping("/gantt") + public R gantt(@RequestParam Long lineId) { + // 查询排产计划明细(按产线过滤,返回所有订单的任务) + WmsSchedulePlanDetailBo detailBo = new WmsSchedulePlanDetailBo(); + detailBo.setLineId(lineId); + List allDetails = iWmsSchedulePlanDetailService.queryList(detailBo); + // 查询产品、订单信息,补充remark + Set orderIdSet = new HashSet<>(); + for (WmsSchedulePlanDetailVo detail : allDetails) { + // 查询产品名和订单编号 + String productName = null; + if (detail.getProductId() != null) { + WmsProductVo product = iWmsProductService.queryById(detail.getProductId()); + if (product != null) { + productName = product.getProductName(); + } + } + String orderCode = null; + Long orderId = null; + if (detail.getPlanId() != null) { + WmsSchedulePlanVo planVo = iWmsSchedulePlanService.queryById(detail.getPlanId()); + if (planVo != null && planVo.getOrderId() != null) { + orderId = planVo.getOrderId(); + WmsOrderVo orderVo = iWmsOrderService.queryById(orderId); + if (orderVo != null) { + orderCode = orderVo.getOrderCode(); + } + } + } + // 设置remark为“订单编号-产品名” + String remark = (orderCode != null ? orderCode : "") + (productName != null ? ("-" + productName) : ""); + detail.setRemark(remark); + // 查询产线日产能 + BigDecimal capacity = null; + if (detail.getLineId() != null) { + WmsProductionLineVo lineVo = iWmsProductionLineService.queryById(detail.getLineId()); + if (lineVo != null) { + capacity = lineVo.getCapacity(); + } + } + // 计算天数和总产能 + BigDecimal totalCapacity = null; + int days = 0; + if (detail.getStartDate() != null && detail.getEndDate() != null) { + long diff = detail.getEndDate().getTime() - detail.getStartDate().getTime(); + days = (int) (diff / (1000 * 3600 * 24)) + 1; + if (capacity != null) { + totalCapacity = capacity.multiply(new BigDecimal(days)); + } + } + // 计划生产数量 + BigDecimal planQuantity = detail.getQuantity(); + + // 直接set字段,无需反射 + detail.setCapacity(capacity); + detail.setTotalCapacity(totalCapacity); + detail.setDays(days); + detail.setPlanQuantity(planQuantity); + } + // 查询所有相关订单信息 + List orderList = new ArrayList<>(); + // 返回结构 + return R.ok(new HashMap() {{ + put("tasks", allDetails); + put("orders", orderList); + }}); + } } diff --git a/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanBo.java b/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanBo.java index 37ff222c..d5387dc5 100644 --- a/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanBo.java +++ b/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanBo.java @@ -32,6 +32,11 @@ public class WmsSchedulePlanBo extends BaseEntity { */ private Long orderId; + /** + * 版本 + */ + private String version; + /** * 状态(0=新建,1=已排产,2=生产中,3=已完成) */ diff --git a/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanDetailBo.java b/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanDetailBo.java index a9c9b42d..54977cf9 100644 --- a/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanDetailBo.java +++ b/klp-wms/src/main/java/com/klp/domain/bo/WmsSchedulePlanDetailBo.java @@ -48,11 +48,13 @@ public class WmsSchedulePlanDetailBo extends BaseEntity { /** * 计划开始日期 */ + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", timezone = "GMT+8") private Date startDate; /** * 计划结束日期 */ + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", timezone = "GMT+8") private Date endDate; /** diff --git a/klp-wms/src/main/java/com/klp/domain/vo/WmsSchedulePlanDetailVo.java b/klp-wms/src/main/java/com/klp/domain/vo/WmsSchedulePlanDetailVo.java index b49c4665..6b1961fc 100644 --- a/klp-wms/src/main/java/com/klp/domain/vo/WmsSchedulePlanDetailVo.java +++ b/klp-wms/src/main/java/com/klp/domain/vo/WmsSchedulePlanDetailVo.java @@ -70,5 +70,24 @@ public class WmsSchedulePlanDetailVo { @ExcelProperty(value = "备注") private String remark; + /** + * 产线日产能 + */ + private BigDecimal capacity; + + /** + * 总产能 + */ + private BigDecimal totalCapacity; + + /** + * 目标生产数量 + */ + private BigDecimal planQuantity; + + /** + * 天数 + */ + private Integer days; } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsPurchasePlanDetailServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsPurchasePlanDetailServiceImpl.java index ff957520..3a139320 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsPurchasePlanDetailServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsPurchasePlanDetailServiceImpl.java @@ -74,6 +74,7 @@ public class WmsPurchasePlanDetailServiceImpl implements IWmsPurchasePlanDetailS */ @Override public Boolean insertByBo(WmsPurchasePlanDetailBo bo) { + WmsPurchasePlanDetail add = BeanUtil.toBean(bo, WmsPurchasePlanDetail.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsSchedulePlanDetailServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsSchedulePlanDetailServiceImpl.java index 867d473c..19e276ad 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsSchedulePlanDetailServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsSchedulePlanDetailServiceImpl.java @@ -74,6 +74,16 @@ public class WmsSchedulePlanDetailServiceImpl implements IWmsSchedulePlanDetailS */ @Override public Boolean insertByBo(WmsSchedulePlanDetailBo bo) { + // 校验产线时间段是否已排产 + List existList = baseMapper.selectList( + Wrappers.lambdaQuery(WmsSchedulePlanDetail.class) + .eq(WmsSchedulePlanDetail::getLineId, bo.getLineId()) + .le(WmsSchedulePlanDetail::getStartDate, bo.getEndDate()) + .ge(WmsSchedulePlanDetail::getEndDate, bo.getStartDate()) + ); + if (existList != null && !existList.isEmpty()) { + throw new RuntimeException("该产线该时间段已排产,请选择其他时间或产线!"); + } WmsSchedulePlanDetail add = BeanUtil.toBean(bo, WmsSchedulePlanDetail.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0;