From 1ce1ffad5a036f7fd81bb2f096759a514a82376b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=98=8A=E5=A4=A9?= <15984976+n2319_0@user.noreply.gitee.com> Date: Mon, 27 Apr 2026 10:40:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BA=BA=E7=83=AD=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=88js=EF=BC=89=EF=BC=8C=E5=88=9D=E7=89=88=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MatProductAdditionController.java | 72 ++++ .../controller/MatProductLaborController.java | 49 +++ .../java/com/gear/mat/domain/MatProduct.java | 5 + .../gear/mat/domain/MatProductAddition.java | 35 ++ .../com/gear/mat/domain/MatProductLabor.java | 27 ++ .../mat/domain/bo/MatProductAdditionBo.java | 31 ++ .../com/gear/mat/domain/bo/MatProductBo.java | 4 + .../gear/mat/domain/bo/MatProductLaborBo.java | 19 ++ .../mat/domain/vo/MatProductAdditionVo.java | 40 +++ .../gear/mat/domain/vo/MatProductLaborVo.java | 28 ++ .../com/gear/mat/domain/vo/MatProductVo.java | 6 + .../domain/vo/MatProductWithMaterialsVo.java | 6 + .../mat/mapper/MatProductAdditionMapper.java | 15 + .../mat/mapper/MatProductLaborMapper.java | 9 + .../service/IMatProductAdditionService.java | 47 +++ .../mat/service/IMatProductLaborService.java | 19 ++ .../impl/MatProductAdditionServiceImpl.java | 54 +++ .../impl/MatProductLaborServiceImpl.java | 45 +++ .../service/impl/MatProductServiceImpl.java | 7 +- .../service/impl/SysOssServiceImpl.java | 2 +- .../resources/mapper/system/SysOssMapper.xml | 1 + .../.npm-cache/_update-notifier-last-checked | 0 gear-ui3/src/api/mat/productAddition.js | 36 ++ gear-ui3/src/api/mat/productLabor.js | 33 ++ gear-ui3/src/views/mat/product/detail.vue | 168 ++++++++- gear-ui3/src/views/mat/product/index.vue | 318 ++++++++++++++++-- .../sql/mysql/update/update_v0.8.2~v0.8.3.sql | 19 ++ 27 files changed, 1067 insertions(+), 28 deletions(-) create mode 100644 gear-mat/src/main/java/com/gear/mat/controller/MatProductAdditionController.java create mode 100644 gear-mat/src/main/java/com/gear/mat/controller/MatProductLaborController.java create mode 100644 gear-mat/src/main/java/com/gear/mat/domain/MatProductAddition.java create mode 100644 gear-mat/src/main/java/com/gear/mat/domain/MatProductLabor.java create mode 100644 gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductAdditionBo.java create mode 100644 gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductLaborBo.java create mode 100644 gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductAdditionVo.java create mode 100644 gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductLaborVo.java create mode 100644 gear-mat/src/main/java/com/gear/mat/mapper/MatProductAdditionMapper.java create mode 100644 gear-mat/src/main/java/com/gear/mat/mapper/MatProductLaborMapper.java create mode 100644 gear-mat/src/main/java/com/gear/mat/service/IMatProductAdditionService.java create mode 100644 gear-mat/src/main/java/com/gear/mat/service/IMatProductLaborService.java create mode 100644 gear-mat/src/main/java/com/gear/mat/service/impl/MatProductAdditionServiceImpl.java create mode 100644 gear-mat/src/main/java/com/gear/mat/service/impl/MatProductLaborServiceImpl.java create mode 100644 gear-ui3/.npm-cache/_update-notifier-last-checked create mode 100644 gear-ui3/src/api/mat/productAddition.js create mode 100644 gear-ui3/src/api/mat/productLabor.js diff --git a/gear-mat/src/main/java/com/gear/mat/controller/MatProductAdditionController.java b/gear-mat/src/main/java/com/gear/mat/controller/MatProductAdditionController.java new file mode 100644 index 0000000..1ac1cd3 --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/controller/MatProductAdditionController.java @@ -0,0 +1,72 @@ +package com.gear.mat.controller; + +import com.gear.common.core.domain.R; +import com.gear.common.core.page.TableDataInfo; +import com.gear.common.enums.BusinessType; +import com.gear.common.annotation.Log; +import com.gear.common.core.controller.BaseController; +import com.gear.mat.domain.bo.MatProductAdditionBo; +import com.gear.mat.domain.vo.MatProductAdditionVo; +import com.gear.mat.service.IMatProductAdditionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +/** + * 产品属性附加表Controller + * + * @author gear + * @date 2026-04-22 + */ +@RestController +@RequestMapping("/api/mat/productAddition") +public class MatProductAdditionController extends BaseController { + + @Autowired + private IMatProductAdditionService productAdditionService; + + /** + * 查询产品属性附加表列表 + * + * @param productAdditionBo 产品属性附加表业务对象 + * @return 产品属性附加表列表 + */ + @GetMapping("/list") + public TableDataInfo list(MatProductAdditionBo productAdditionBo) { + List list = productAdditionService.listProductAddition(productAdditionBo); + return TableDataInfo.build(list); + } + + /** + * 新增产品属性附加表 + */ + @Log(title = "产品属性附加表", businessType = BusinessType.INSERT) + @PostMapping + public R add(@RequestBody MatProductAdditionBo productAdditionBo) { + boolean result = productAdditionService.addProductAddition(productAdditionBo); + return R.ok(result); + } + + /** + * 修改产品属性附加表 + */ + @Log(title = "产品属性附加表", businessType = BusinessType.UPDATE) + @PutMapping + public R update(@RequestBody MatProductAdditionBo productAdditionBo) { + boolean result = productAdditionService.updateProductAddition(productAdditionBo); + return R.ok(result); + } + + /** + * 删除产品属性附加表 + * + * @param addId 主键ID + * @return 删除结果 + */ + @Log(title = "产品属性附加表", businessType = BusinessType.DELETE) + @DeleteMapping("/{addId}") + public R del(@PathVariable Long addId) { + boolean result = productAdditionService.delProductAddition(addId); + return R.ok(result); + } +} diff --git a/gear-mat/src/main/java/com/gear/mat/controller/MatProductLaborController.java b/gear-mat/src/main/java/com/gear/mat/controller/MatProductLaborController.java new file mode 100644 index 0000000..05a92ac --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/controller/MatProductLaborController.java @@ -0,0 +1,49 @@ +package com.gear.mat.controller; + +import com.gear.common.annotation.Log; +import com.gear.common.core.controller.BaseController; +import com.gear.common.core.domain.R; +import com.gear.common.core.page.TableDataInfo; +import com.gear.common.enums.BusinessType; +import com.gear.mat.domain.bo.MatProductLaborBo; +import com.gear.mat.domain.vo.MatProductLaborVo; +import com.gear.mat.service.IMatProductLaborService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/mat/productLabor") +public class MatProductLaborController extends BaseController { + + @Autowired + private IMatProductLaborService productLaborService; + + @GetMapping("/list") + public TableDataInfo list(MatProductLaborBo productLaborBo) { + List list = productLaborService.listProductLabor(productLaborBo); + return TableDataInfo.build(list); + } + + @Log(title = "产品手动工价", businessType = BusinessType.INSERT) + @PostMapping + public R add(@RequestBody MatProductLaborBo productLaborBo) { + boolean result = productLaborService.addProductLabor(productLaborBo); + return R.ok(result); + } + + @Log(title = "产品手动工价", businessType = BusinessType.UPDATE) + @PutMapping + public R update(@RequestBody MatProductLaborBo productLaborBo) { + boolean result = productLaborService.updateProductLabor(productLaborBo); + return R.ok(result); + } + + @Log(title = "产品手动工价", businessType = BusinessType.DELETE) + @DeleteMapping("/{laborId}") + public R del(@PathVariable Long laborId) { + boolean result = productLaborService.delProductLabor(laborId); + return R.ok(result); + } +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/MatProduct.java b/gear-mat/src/main/java/com/gear/mat/domain/MatProduct.java index 682f7b7..e946972 100644 --- a/gear-mat/src/main/java/com/gear/mat/domain/MatProduct.java +++ b/gear-mat/src/main/java/com/gear/mat/domain/MatProduct.java @@ -59,4 +59,9 @@ public class MatProduct extends BaseEntity { */ private String productImages; + /** + * 产品说明书,多个PDF以逗号分隔 + */ + private String productPdfs; + } diff --git a/gear-mat/src/main/java/com/gear/mat/domain/MatProductAddition.java b/gear-mat/src/main/java/com/gear/mat/domain/MatProductAddition.java new file mode 100644 index 0000000..9e8ff4c --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/domain/MatProductAddition.java @@ -0,0 +1,35 @@ +package com.gear.mat.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.gear.common.core.domain.BaseEntity; +import lombok.Data; + +/** + * 产品属性附加表 + * + * @author gear + * @date 2026-04-22 + */ +@Data +@TableName("gear_product_addition") +public class MatProductAddition extends BaseEntity { + /** 主键ID */ + @TableId(type = IdType.AUTO) + private Long addId; + + /** 属性名 */ + private String attrName; + + /** 属性值 */ + private String attrValue; + + /** 产品ID */ + private Long productId; + + /** 备注 */ + private String remark; + + /** 删除标识 0-未删除 1-已删除 */ + @TableLogic + private Integer delFlag; +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/MatProductLabor.java b/gear-mat/src/main/java/com/gear/mat/domain/MatProductLabor.java new file mode 100644 index 0000000..a89c32f --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/domain/MatProductLabor.java @@ -0,0 +1,27 @@ +package com.gear.mat.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.gear.common.core.domain.BaseEntity; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@TableName("gear_product_labor") +public class MatProductLabor extends BaseEntity { + + @TableId(type = IdType.AUTO) + private Long laborId; + + private Long productId; + + private String laborName; + + private BigDecimal laborPrice; + + @TableLogic + private Integer delFlag; +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductAdditionBo.java b/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductAdditionBo.java new file mode 100644 index 0000000..46cfa0a --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductAdditionBo.java @@ -0,0 +1,31 @@ +package com.gear.mat.domain.bo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import java.util.Date; + +/** + * 产品属性附加表业务对象 + * + * @author gear + * @date 2026-04-22 + */ +@Data +public class MatProductAdditionBo { + /** 主键ID */ + private Long addId; + + /** 属性名 */ + private String attrName; + + /** 属性值 */ + private String attrValue; + + /** 产品ID */ + private Long productId; + + /** 备注 */ + private String remark; +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductBo.java b/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductBo.java index d4396c8..79befa3 100644 --- a/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductBo.java +++ b/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductBo.java @@ -57,5 +57,9 @@ public class MatProductBo extends BaseEntity { */ private String productImages; + /** + * 产品说明书,多个PDF以逗号分隔 + */ + private String productPdfs; } diff --git a/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductLaborBo.java b/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductLaborBo.java new file mode 100644 index 0000000..ffc0a6d --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/domain/bo/MatProductLaborBo.java @@ -0,0 +1,19 @@ +package com.gear.mat.domain.bo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class MatProductLaborBo { + + private Long laborId; + + private Long productId; + + private String laborName; + + private BigDecimal laborPrice; + + private String remark; +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductAdditionVo.java b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductAdditionVo.java new file mode 100644 index 0000000..c9c89ec --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductAdditionVo.java @@ -0,0 +1,40 @@ +package com.gear.mat.domain.vo; + +import lombok.Data; +import java.util.Date; + +/** + * 产品属性附加表视图对象 + * + * @author gear + * @date 2026-04-22 + */ +@Data +public class MatProductAdditionVo { + /** 主键ID */ + private Long addId; + + /** 属性名 */ + private String attrName; + + /** 属性值 */ + private String attrValue; + + /** 产品ID */ + private Long productId; + + /** 创建时间 */ + private Date createTime; + + /** 创建人 */ + private String createBy; + + /** 更新时间 */ + private Date updateTime; + + /** 更新人 */ + private String updateBy; + + /** 备注 */ + private String remark; +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductLaborVo.java b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductLaborVo.java new file mode 100644 index 0000000..491b6cc --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductLaborVo.java @@ -0,0 +1,28 @@ +package com.gear.mat.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class MatProductLaborVo { + + private Long laborId; + + private Long productId; + + private String laborName; + + private BigDecimal laborPrice; + + private Date createTime; + + private String createBy; + + private Date updateTime; + + private String updateBy; + + private String remark; +} diff --git a/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductVo.java b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductVo.java index f659dc9..57c7be9 100644 --- a/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductVo.java +++ b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductVo.java @@ -64,5 +64,11 @@ public class MatProductVo { @ExcelProperty(value = "产品图片") private String productImages; + /** + * 产品说明书,多个PDF以逗号分隔 + */ + @ExcelProperty(value = "产品说明书") + private String productPdfs; + } diff --git a/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductWithMaterialsVo.java b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductWithMaterialsVo.java index acc5324..d9f948b 100644 --- a/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductWithMaterialsVo.java +++ b/gear-mat/src/main/java/com/gear/mat/domain/vo/MatProductWithMaterialsVo.java @@ -63,6 +63,12 @@ public class MatProductWithMaterialsVo { @ExcelProperty(value = "产品图片") private String productImages; + /** + * 产品PDF文件,多个PDF以逗号分隔 + */ + @ExcelProperty(value = "产品PDF文件") + private String productPdfs; + /** * 关联的配料信息列表 */ diff --git a/gear-mat/src/main/java/com/gear/mat/mapper/MatProductAdditionMapper.java b/gear-mat/src/main/java/com/gear/mat/mapper/MatProductAdditionMapper.java new file mode 100644 index 0000000..2ef6f4e --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/mapper/MatProductAdditionMapper.java @@ -0,0 +1,15 @@ +package com.gear.mat.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.gear.mat.domain.MatProductAddition; +import org.apache.ibatis.annotations.Mapper; + +/** + * 产品属性附加表Mapper接口 + * + * @author gear + * @date 2026-04-22 + */ +@Mapper +public interface MatProductAdditionMapper extends BaseMapper { +} diff --git a/gear-mat/src/main/java/com/gear/mat/mapper/MatProductLaborMapper.java b/gear-mat/src/main/java/com/gear/mat/mapper/MatProductLaborMapper.java new file mode 100644 index 0000000..04de1cd --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/mapper/MatProductLaborMapper.java @@ -0,0 +1,9 @@ +package com.gear.mat.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.gear.mat.domain.MatProductLabor; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MatProductLaborMapper extends BaseMapper { +} diff --git a/gear-mat/src/main/java/com/gear/mat/service/IMatProductAdditionService.java b/gear-mat/src/main/java/com/gear/mat/service/IMatProductAdditionService.java new file mode 100644 index 0000000..5fd2200 --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/service/IMatProductAdditionService.java @@ -0,0 +1,47 @@ +package com.gear.mat.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gear.mat.domain.MatProductAddition; +import com.gear.mat.domain.bo.MatProductAdditionBo; +import com.gear.mat.domain.vo.MatProductAdditionVo; +import java.util.List; + +/** + * 产品属性附加表Service接口 + * + * @author gear + * @date 2026-04-22 + */ +public interface IMatProductAdditionService extends IService { + /** + * 查询产品属性附加表列表 + * + * @param productAdditionBo 产品属性附加表业务对象 + * @return 产品属性附加表列表 + */ + List listProductAddition(MatProductAdditionBo productAdditionBo); + + /** + * 新增产品属性附加表 + * + * @param productAdditionBo 产品属性附加表业务对象 + * @return 新增结果 + */ + boolean addProductAddition(MatProductAdditionBo productAdditionBo); + + /** + * 修改产品属性附加表 + * + * @param productAdditionBo 产品属性附加表业务对象 + * @return 修改结果 + */ + boolean updateProductAddition(MatProductAdditionBo productAdditionBo); + + /** + * 删除产品属性附加表 + * + * @param addId 主键ID + * @return 删除结果 + */ + boolean delProductAddition(Long addId); +} diff --git a/gear-mat/src/main/java/com/gear/mat/service/IMatProductLaborService.java b/gear-mat/src/main/java/com/gear/mat/service/IMatProductLaborService.java new file mode 100644 index 0000000..ea0431c --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/service/IMatProductLaborService.java @@ -0,0 +1,19 @@ +package com.gear.mat.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gear.mat.domain.MatProductLabor; +import com.gear.mat.domain.bo.MatProductLaborBo; +import com.gear.mat.domain.vo.MatProductLaborVo; + +import java.util.List; + +public interface IMatProductLaborService extends IService { + + List listProductLabor(MatProductLaborBo productLaborBo); + + boolean addProductLabor(MatProductLaborBo productLaborBo); + + boolean updateProductLabor(MatProductLaborBo productLaborBo); + + boolean delProductLabor(Long laborId); +} diff --git a/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductAdditionServiceImpl.java b/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductAdditionServiceImpl.java new file mode 100644 index 0000000..8fa263d --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductAdditionServiceImpl.java @@ -0,0 +1,54 @@ +package com.gear.mat.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gear.mat.domain.MatProductAddition; +import com.gear.mat.domain.bo.MatProductAdditionBo; +import com.gear.mat.domain.vo.MatProductAdditionVo; +import com.gear.mat.mapper.MatProductAdditionMapper; +import com.gear.mat.service.IMatProductAdditionService; +import com.gear.common.utils.BeanCopyUtils; +import org.springframework.stereotype.Service; +import java.util.List; + +/** + * 产品属性附加表Service实现类 + * + * @author gear + * @date 2026-04-22 + */ +@Service +public class MatProductAdditionServiceImpl extends ServiceImpl implements IMatProductAdditionService { + + @Override + public List listProductAddition(MatProductAdditionBo productAdditionBo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (productAdditionBo.getProductId() != null) { + queryWrapper.eq(MatProductAddition::getProductId, productAdditionBo.getProductId()); + } + if (productAdditionBo.getAttrName() != null) { + queryWrapper.eq(MatProductAddition::getAttrName, productAdditionBo.getAttrName()); + } + queryWrapper.eq(MatProductAddition::getDelFlag, 0); + + List list = baseMapper.selectList(queryWrapper); + return BeanCopyUtils.copyList(list, MatProductAdditionVo.class); + } + + @Override + public boolean addProductAddition(MatProductAdditionBo productAdditionBo) { + MatProductAddition productAddition = BeanCopyUtils.copy(productAdditionBo, MatProductAddition.class); + return save(productAddition); + } + + @Override + public boolean updateProductAddition(MatProductAdditionBo productAdditionBo) { + MatProductAddition productAddition = BeanCopyUtils.copy(productAdditionBo, MatProductAddition.class); + return updateById(productAddition); + } + + @Override + public boolean delProductAddition(Long addId) { + return removeById(addId); + } +} diff --git a/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductLaborServiceImpl.java b/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductLaborServiceImpl.java new file mode 100644 index 0000000..d4a4157 --- /dev/null +++ b/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductLaborServiceImpl.java @@ -0,0 +1,45 @@ +package com.gear.mat.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gear.common.utils.BeanCopyUtils; +import com.gear.mat.domain.MatProductLabor; +import com.gear.mat.domain.bo.MatProductLaborBo; +import com.gear.mat.domain.vo.MatProductLaborVo; +import com.gear.mat.mapper.MatProductLaborMapper; +import com.gear.mat.service.IMatProductLaborService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class MatProductLaborServiceImpl extends ServiceImpl implements IMatProductLaborService { + + @Override + public List listProductLabor(MatProductLaborBo productLaborBo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (productLaborBo.getProductId() != null) { + queryWrapper.eq(MatProductLabor::getProductId, productLaborBo.getProductId()); + } + queryWrapper.eq(MatProductLabor::getDelFlag, 0); + List list = baseMapper.selectList(queryWrapper); + return BeanCopyUtils.copyList(list, MatProductLaborVo.class); + } + + @Override + public boolean addProductLabor(MatProductLaborBo productLaborBo) { + MatProductLabor labor = BeanCopyUtils.copy(productLaborBo, MatProductLabor.class); + return save(labor); + } + + @Override + public boolean updateProductLabor(MatProductLaborBo productLaborBo) { + MatProductLabor labor = BeanCopyUtils.copy(productLaborBo, MatProductLabor.class); + return updateById(labor); + } + + @Override + public boolean delProductLabor(Long laborId) { + return removeById(laborId); + } +} diff --git a/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductServiceImpl.java b/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductServiceImpl.java index aac4f11..63f155c 100644 --- a/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductServiceImpl.java +++ b/gear-mat/src/main/java/com/gear/mat/service/impl/MatProductServiceImpl.java @@ -112,7 +112,8 @@ public class MatProductServiceImpl implements IMatProductService { productWithMaterialsVo.setModel(productVo.getModel()); productWithMaterialsVo.setUnitPrice(productVo.getUnitPrice()); productWithMaterialsVo.setRemark(productVo.getRemark()); -// productWithMaterialsVo.setProductImages(productVo.getProductImages()); + productWithMaterialsVo.setProductImages(productVo.getProductImages()); + productWithMaterialsVo.setProductPdfs(productVo.getProductPdfs()); // 查询并设置关联的配料信息 List relations = productMaterialRelationService.queryList( @@ -169,7 +170,9 @@ public class MatProductServiceImpl implements IMatProductService { */ @Override public Boolean insertByBo(MatProductBo bo) { + System.out.println("插入产品,productPdfs: " + bo.getProductPdfs()); MatProduct add = BeanUtil.toBean(bo, MatProduct.class); + System.out.println("转换后,productPdfs: " + add.getProductPdfs()); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { @@ -183,7 +186,9 @@ public class MatProductServiceImpl implements IMatProductService { */ @Override public Boolean updateByBo(MatProductBo bo) { + System.out.println("修改产品,productPdfs: " + bo.getProductPdfs()); MatProduct update = BeanUtil.toBean(bo, MatProduct.class); + System.out.println("转换后,productPdfs: " + update.getProductPdfs()); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } diff --git a/gear-system/src/main/java/com/gear/system/service/impl/SysOssServiceImpl.java b/gear-system/src/main/java/com/gear/system/service/impl/SysOssServiceImpl.java index 65ab290..8834a08 100644 --- a/gear-system/src/main/java/com/gear/system/service/impl/SysOssServiceImpl.java +++ b/gear-system/src/main/java/com/gear/system/service/impl/SysOssServiceImpl.java @@ -94,7 +94,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return lqw; } - @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") + @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "'v2:' + #ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); diff --git a/gear-system/src/main/resources/mapper/system/SysOssMapper.xml b/gear-system/src/main/resources/mapper/system/SysOssMapper.xml index 8568d4d..2a60491 100644 --- a/gear-system/src/main/resources/mapper/system/SysOssMapper.xml +++ b/gear-system/src/main/resources/mapper/system/SysOssMapper.xml @@ -5,6 +5,7 @@ + diff --git a/gear-ui3/.npm-cache/_update-notifier-last-checked b/gear-ui3/.npm-cache/_update-notifier-last-checked new file mode 100644 index 0000000..e69de29 diff --git a/gear-ui3/src/api/mat/productAddition.js b/gear-ui3/src/api/mat/productAddition.js new file mode 100644 index 0000000..298aef9 --- /dev/null +++ b/gear-ui3/src/api/mat/productAddition.js @@ -0,0 +1,36 @@ +import request from '@/utils/request' + +// 查询产品属性附加列表 +export function listProductAddition(query) { + return request({ + url: '/api/mat/productAddition/list', + method: 'get', + params: query + }) +} + +// 新增产品属性附加 +export function addProductAddition(data) { + return request({ + url: '/api/mat/productAddition', + method: 'post', + data: data + }) +} + +// 修改产品属性附加 +export function updateProductAddition(data) { + return request({ + url: '/api/mat/productAddition', + method: 'put', + data: data + }) +} + +// 删除产品属性附加 +export function delProductAddition(addId) { + return request({ + url: '/api/mat/productAddition/' + addId, + method: 'delete' + }) +} diff --git a/gear-ui3/src/api/mat/productLabor.js b/gear-ui3/src/api/mat/productLabor.js new file mode 100644 index 0000000..a2636a5 --- /dev/null +++ b/gear-ui3/src/api/mat/productLabor.js @@ -0,0 +1,33 @@ +import request from '@/utils/request' + +export function listProductLabor(query) { + return request({ + url: '/api/mat/productLabor/list', + method: 'get', + params: query + }) +} + +export function addProductLabor(data) { + return request({ + url: '/api/mat/productLabor', + method: 'post', + data: data + }) +} + +export function updateProductLabor(data) { + return request({ + url: '/api/mat/productLabor', + method: 'put', + data: data + }) +} + +export function delProductLabor(laborId) { + return request({ + url: '/api/mat/productLabor/' + laborId, + method: 'delete' + }) +} + diff --git a/gear-ui3/src/views/mat/product/detail.vue b/gear-ui3/src/views/mat/product/detail.vue index 18f0bfc..236a238 100644 --- a/gear-ui3/src/views/mat/product/detail.vue +++ b/gear-ui3/src/views/mat/product/detail.vue @@ -49,6 +49,21 @@ /> +
+

产品说明书

+
+
+ + {{ pdf.name }} + 预览 + 下载 +
+
+
@@ -104,9 +119,9 @@ - +
-

工本

+

工价

@@ -123,7 +138,22 @@
- 工本小计:{{ formatDecimal(laborMaterials.reduce((sum, item) => sum + item.subtotal, 0)) }} 元 + 工价小计:{{ formatDecimal(laborMaterials.reduce((sum, item) => sum + item.subtotal, 0)) }} 元 +
+
+ +
+

工价(手动)

+ + + + + + +
+ 工价(手动)小计:{{ formatDecimal(productLaborList.reduce((sum, item) => sum + (Number(item.laborPrice) || 0), 0)) }} 元
@@ -145,7 +175,10 @@ import { getProduct } from "@/api/mat/product"; import { listProductMaterialRelation } from "@/api/mat/productMaterialRelation"; import { getMaterial } from "@/api/mat/material"; import { listProductAddition } from "@/api/mat/productAddition"; +import { listProductLabor } from "@/api/mat/productLabor"; +import { listByIds, listOss } from "@/api/system/oss"; import { formatDecimal } from '@/utils/gear'; +import { Document } from '@element-plus/icons-vue'; const router = useRouter(); const route = useRoute(); @@ -154,12 +187,15 @@ const productDetail = ref({}); const loading = ref(true); const materialLoading = ref(false); const additionLoading = ref(false); +const pdfFiles = ref([]); +const pdfUrlFiles = ref([]); // 材料明细数据 const productMaterialRelationList = ref([]); // 产品附加属性数据 const productAdditionList = ref([]); +const productLaborList = ref([]); // 计算主材、辅材和工本 const mainMaterials = computed(() => { @@ -189,7 +225,7 @@ const auxiliaryMaterials = computed(() => { }); const laborMaterials = computed(() => { - // 工本(材料类型为3) + // 工价(材料类型为3) return productMaterialRelationList.value .filter(item => item.material && item.material.materialType === 3) .map(item => ({ @@ -206,7 +242,24 @@ const totalAmount = computed(() => { const mainTotal = mainMaterials.value.reduce((sum, item) => sum + item.subtotal, 0); const auxiliaryTotal = auxiliaryMaterials.value.reduce((sum, item) => sum + item.subtotal, 0); const laborTotal = laborMaterials.value.reduce((sum, item) => sum + item.subtotal, 0); - return mainTotal + auxiliaryTotal + laborTotal; + const manualLaborTotal = productLaborList.value.reduce((sum, item) => { + const price = item && item.laborPrice !== undefined && item.laborPrice !== null ? Number(item.laborPrice) : 0; + return sum + (Number.isFinite(price) ? price : 0); + }, 0); + return mainTotal + auxiliaryTotal + laborTotal + manualLaborTotal; +}); + +const isOssIdList = (val) => { + if (val === null || val === undefined) return false; + const str = String(val).trim(); + return /^[0-9]+(,[0-9]+)*$/.test(str); +}; + +const pdfDisplayList = computed(() => { + const raw = String(productDetail.value.productPdfs || '').trim(); + if (!raw) return []; + if (isOssIdList(raw)) return pdfFiles.value; + return pdfUrlFiles.value; }); // 获取产品详情 @@ -217,10 +270,12 @@ function getProductDetail() { getProduct(productId).then(response => { productDetail.value = response.data; loading.value = false; + resolvePdfFiles(); // 获取材料明细 getMaterialDetail(productId); // 获取产品附加属性 getProductAddition(productId); + getProductLabor(productId); }).catch(error => { console.error('获取产品详情失败:', error); loading.value = false; @@ -262,11 +317,82 @@ function getMaterialDetail(productId) { }); } +function getProductLabor(productId) { + listProductLabor({ productId }).then(response => { + productLaborList.value = response.rows || []; + }).catch(() => { + productLaborList.value = []; + }); +} + +function resolvePdfFiles() { + const raw = String(productDetail.value.productPdfs || '').trim(); + if (!raw) { + pdfFiles.value = []; + pdfUrlFiles.value = []; + return; + } + if (isOssIdList(raw)) { + pdfUrlFiles.value = []; + listByIds(raw).then(res => { + const list = res.data || []; + pdfFiles.value = list.map(oss => ({ + name: oss.originalName || oss.fileName || String(oss.ossId), + url: oss.url, + ossId: oss.ossId + })); + }).catch(() => { + pdfFiles.value = []; + }); + return; + } + + pdfFiles.value = []; + const urls = raw.split(',').map(s => String(s).trim()).filter(Boolean); + Promise.all(urls.map((url) => + listOss({ url, pageNum: 1, pageSize: 1 }) + .then(r => (r && r.rows && r.rows[0]) ? r.rows[0] : null) + .catch(() => null) + )).then(rows => { + pdfUrlFiles.value = urls.map((url, index) => { + const row = rows[index]; + return { + name: (row && (row.originalName || row.fileName)) || getFileName(url), + url: (row && row.url) || url, + ossId: row && row.ossId + }; + }); + }).catch(() => { + pdfUrlFiles.value = urls.map(url => ({ name: getFileName(url), url })); + }); +} + // 返回列表 function handleBack() { router.back(); } +// 获取文件名 +function getFileName(url) { + if (!url) return ''; + return url.substring(url.lastIndexOf('/') + 1); +} + +// 预览PDF +function previewPdf(pdf) { + if (!pdf || !pdf.url) return; + window.open(pdf.url, '_blank'); +} + +// 下载PDF +function downloadPdf(pdf) { + if (!pdf || !pdf.url) return; + const link = document.createElement('a'); + link.href = pdf.url; + link.download = pdf.name || getFileName(pdf.url); + link.click(); +} + onMounted(() => { getProductDetail(); }); @@ -312,6 +438,36 @@ onMounted(() => { margin-top: 10px; } +.product-pdfs { + margin-top: 20px; +} + +.pdf-list { + margin-top: 10px; +} + +.pdf-item { + display: flex; + align-items: center; + gap: 10px; + padding: 10px; + background-color: #f5f7fa; + border-radius: 4px; + margin-bottom: 8px; +} + +.pdf-icon { + font-size: 24px; + color: #409eff; +} + +.pdf-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .material-section { margin: 20px 0; } @@ -357,4 +513,4 @@ onMounted(() => { font-weight: bold; color: #f56c6c; } - \ No newline at end of file + diff --git a/gear-ui3/src/views/mat/product/index.vue b/gear-ui3/src/views/mat/product/index.vue index 807fca6..6171ca8 100644 --- a/gear-ui3/src/views/mat/product/index.vue +++ b/gear-ui3/src/views/mat/product/index.vue @@ -67,6 +67,7 @@ 详情 修改 附加属性 + 工价 删除 @@ -113,6 +114,25 @@ + + + 上传说明书 + + + + +
+ 添加工价 + + + + + + + + + + + +
+ +
+
@@ -190,6 +239,8 @@ import { listProduct, getProduct, delProduct, addProduct, updateProduct } from " import { listProductMaterialRelation } from "@/api/mat/productMaterialRelation"; import { getMaterial } from "@/api/mat/material"; import { listProductAddition, addProductAddition, updateProductAddition, delProductAddition } from "@/api/mat/productAddition"; +import { listProductLabor, addProductLabor, updateProductLabor, delProductLabor } from "@/api/mat/productLabor"; +import { listByIds, listOss } from "@/api/system/oss"; import bom from "@/views/mat/components/bom.vue"; import StickyDragContainer from "@/components/StickyDragContainer/index.vue"; import Raw from '@/components/Renderer/Raw.vue'; @@ -199,6 +250,7 @@ import { getToken } from '@/utils/auth'; const router = useRouter(); const bomOpen = ref(false); const additionOpen = ref(false); +const laborOpen = ref(false); const { proxy } = getCurrentInstance(); @@ -216,16 +268,45 @@ const currentProductId = ref(null); const currentProduct = ref({}); const appContainer = ref(null); const imageFileList = ref([]); +const pdfFileList = ref([]); +const pdfUseOssId = ref(true); const additionList = ref([]); +const laborList = ref([]); const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/system/oss/upload'); const headers = ref({ Authorization: "Bearer " + getToken() }); +const isOssIdList = (val) => { + if (val === null || val === undefined) return false; + const str = String(val).trim(); + return /^[0-9]+(,[0-9]+)*$/.test(str); +}; + +const getFileNameFromUrl = (url) => { + if (!url) return ''; + const str = String(url); + return str.substring(str.lastIndexOf('/') + 1); +}; + const formatterTime = (time) => { return proxy.parseTime(time, '{y}-{m}-{d}') } const data = reactive({ - form: {}, + form: { + productId: null, + productName: null, + spec: null, + model: null, + unitPrice: null, + delFlag: null, + createTime: null, + createBy: null, + updateTime: null, + updateBy: null, + remark: null, + productImages: null, + productPdfs: null + }, queryParams: { pageNum: 1, pageSize: 10, @@ -269,9 +350,12 @@ function reset() { updateTime: null, updateBy: null, remark: null, - productImages: null + productImages: null, + productPdfs: null }; imageFileList.value = []; + pdfFileList.value = []; + pdfUseOssId.value = true; proxy.resetForm("productRef"); } @@ -305,50 +389,168 @@ function handleAdd() { function handleUpdate(row) { loading.value = true reset(); - const _productId = row.productId || ids.value + const _productId = row.productId || ids.value; getProduct(_productId).then(response => { loading.value = false; form.value = response.data; + // 处理图片文件列表 if (form.value.productImages) { - imageFileList.value = form.value.productImages.split(',').map(url => ({ + imageFileList.value = form.value.productImages.split(',').map((url, index) => ({ name: url.substring(url.lastIndexOf('/') + 1), - url: url + url: url, + uid: Date.now() + index })); + } else { + imageFileList.value = []; } + + // 处理PDF文件列表 + if (form.value.productPdfs) { + const raw = String(form.value.productPdfs).trim(); + if (isOssIdList(raw)) { + pdfUseOssId.value = true; + listByIds(raw).then(res => { + const list = res.data || []; + pdfFileList.value = list.map((oss, index) => ({ + name: oss.originalName || oss.fileName || String(oss.ossId), + url: oss.url, + ossId: oss.ossId, + uid: Date.now() + index + 100 + })); + }).catch(() => { + pdfFileList.value = []; + }); + } else { + const urls = raw.split(',').map(s => String(s).trim()).filter(Boolean); + Promise.all(urls.map((url) => + listOss({ url, pageNum: 1, pageSize: 1 }) + .then(r => (r && r.rows && r.rows[0]) ? r.rows[0] : null) + .catch(() => null) + )).then(rows => { + const mapped = urls.map((url, index) => { + const row = rows[index]; + return { + name: (row && (row.originalName || row.fileName)) || getFileNameFromUrl(url), + url: (row && row.url) || url, + ossId: row && row.ossId, + uid: Date.now() + index + 100 + }; + }); + pdfFileList.value = mapped; + const ossIds = mapped.map(f => f.ossId).filter(Boolean); + const allResolved = ossIds.length === mapped.length && mapped.length > 0; + pdfUseOssId.value = allResolved; + if (allResolved) { + form.value.productPdfs = ossIds.join(','); + } else { + form.value.productPdfs = urls.join(','); + } + }).catch(() => { + pdfUseOssId.value = false; + pdfFileList.value = urls.map((url, index) => ({ + name: getFileNameFromUrl(url), + url, + uid: Date.now() + index + 100 + })); + }); + } + } else { + pdfFileList.value = []; + } + open.value = true; title.value = "修改产品基础信息"; + }).catch(err => { + proxy.$modal.msgError("获取产品信息失败"); + loading.value = false; }); } /** 图片上传成功处理 */ -function handleImageUploadSuccess(response, uploadFile) { +function handleImageUploadSuccess(response, uploadFile, uploadFiles) { if (response.code === 200) { const imageUrl = response.data.url; - if (form.value.productImages) { - form.value.productImages += ',' + imageUrl; - } else { - form.value.productImages = imageUrl; - } + uploadFile.url = imageUrl; + + const urls = uploadFiles + .map(f => f.url) + .filter(url => url && !url.startsWith('blob:')); + form.value.productImages = urls.join(','); } else { proxy.$modal.msgError('上传失败:' + response.msg); + const index = imageFileList.value.findIndex(f => f.uid === uploadFile.uid); + if (index > -1) { + imageFileList.value.splice(index, 1); + } } } /** 图片移除处理 */ function handleImageRemove(uploadFile, uploadFiles) { - const imageUrl = uploadFile.url; - if (form.value.productImages) { - const images = form.value.productImages.split(','); - const index = images.indexOf(imageUrl); + const urls = uploadFiles + .map(f => f.url) + .filter(url => url && !url.startsWith('blob:')); + form.value.productImages = urls.length > 0 ? urls.join(',') : null; +} + +/** PDF上传成功处理 */ +function handlePdfUploadSuccess(response, uploadFile, uploadFiles) { + if (response.code === 200) { + uploadFile.url = response.data.url; + uploadFile.name = response.data.fileName || uploadFile.name; + uploadFile.ossId = response.data.ossId; + + if (pdfUseOssId.value) { + const ossIds = uploadFiles + .map(f => f.ossId) + .filter(Boolean); + form.value.productPdfs = ossIds.length > 0 ? ossIds.join(',') : null; + } else { + const urls = uploadFiles + .map(f => f.url) + .filter(url => url && !url.startsWith('blob:')); + form.value.productPdfs = urls.length > 0 ? urls.join(',') : null; + } + } else { + proxy.$modal.msgError('上传失败:' + response.msg); + const index = pdfFileList.value.findIndex(f => f.uid === uploadFile.uid); if (index > -1) { - images.splice(index, 1); - // 当所有图片都被删除时,设置为null而不是空字符串 - form.value.productImages = images.length > 0 ? images.join(',') : null; + pdfFileList.value.splice(index, 1); } } } +/** PDF移除处理 */ +function handlePdfRemove(uploadFile, uploadFiles) { + if (pdfUseOssId.value) { + const ossIds = uploadFiles + .map(f => f.ossId) + .filter(Boolean); + form.value.productPdfs = ossIds.length > 0 ? ossIds.join(',') : null; + } else { + const urls = uploadFiles + .map(f => f.url) + .filter(url => url && !url.startsWith('blob:')); + form.value.productPdfs = urls.length > 0 ? urls.join(',') : null; + } +} + +/** PDF上传前校验 */ +function beforePdfUpload(file) { + const isPdf = file.type === 'application/pdf'; + const isLt10M = file.size / 1024 / 1024 < 10; + if (!isPdf) { + proxy.$modal.msgError('只能上传 PDF 格式的文件'); + return false; + } + if (!isLt10M) { + proxy.$modal.msgError('PDF文件大小不能超过 10MB'); + return false; + } + return true; +} + /** 图片上传前校验 */ function beforeUpload(file) { const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; @@ -366,26 +568,47 @@ function beforeUpload(file) { /** 提交按钮 */ function submitForm() { + if (buttonLoading.value) { + return; + } + proxy.$refs["productRef"].validate(valid => { if (valid) { + const submitData = { + productId: form.value.productId, + productName: form.value.productName, + spec: form.value.spec, + model: form.value.model, + unitPrice: form.value.unitPrice, + remark: form.value.remark, + productImages: form.value.productImages, + productPdfs: form.value.productPdfs + }; + buttonLoading.value = true; if (form.value.productId != null) { - updateProduct(form.value).then(response => { + updateProduct(submitData).then(response => { proxy.$modal.msgSuccess("修改成功"); open.value = false; getList(); + }).catch(err => { + proxy.$modal.msgError("修改失败"); }).finally(() => { buttonLoading.value = false; }); } else { - addProduct(form.value).then(response => { + addProduct(submitData).then(response => { proxy.$modal.msgSuccess("新增成功"); open.value = false; getList(); + }).catch(err => { + proxy.$modal.msgError("新增失败"); }).finally(() => { buttonLoading.value = false; }); } + } else { + return; } }); } @@ -486,6 +709,61 @@ function saveAdditions() { additionOpen.value = false; } +function handleLabor(row) { + currentProductId.value = row.productId; + laborList.value = []; + listProductLabor({ productId: row.productId }).then(response => { + if (response.code === 200) { + laborList.value = response.rows || []; + } + }); + laborOpen.value = true; +} + +function addLaborItem() { + laborList.value.push({ laborName: '', laborPrice: 0 }); +} + +function removeLaborItem(index) { + const item = laborList.value[index]; + if (item && item.laborId) { + delProductLabor(item.laborId).finally(() => { + laborList.value.splice(index, 1); + }); + return; + } + laborList.value.splice(index, 1); +} + +async function saveLabors() { + const valid = laborList.value + .map(item => ({ + ...item, + laborName: item.laborName ? String(item.laborName).trim() : '' + })) + .filter(item => item.laborName); + + const tasks = valid.map(item => { + const data = { + laborId: item.laborId, + productId: currentProductId.value, + laborName: item.laborName, + laborPrice: item.laborPrice ?? 0 + }; + if (data.laborId) return updateProductLabor(data); + return addProductLabor(data); + }); + + try { + await Promise.all(tasks); + proxy.$modal.msgSuccess('保存成功'); + } catch (e) { + proxy.$modal.msgError('保存失败'); + } finally { + laborOpen.value = false; + } +} + getList(); diff --git a/script/sql/mysql/update/update_v0.8.2~v0.8.3.sql b/script/sql/mysql/update/update_v0.8.2~v0.8.3.sql index 1a73c25..3603498 100644 --- a/script/sql/mysql/update/update_v0.8.2~v0.8.3.sql +++ b/script/sql/mysql/update/update_v0.8.2~v0.8.3.sql @@ -186,3 +186,22 @@ CREATE TABLE gear_worker ( KEY idx_worker_name (worker_name), KEY idx_status (status) ) ENGINE=InnoDB COMMENT='工人主数据表'; + +-- ================================ +-- 产品手动工价(详情页挂接,手动录入名称与价格) +-- ================================ +DROP TABLE IF EXISTS gear_product_labor; +CREATE TABLE gear_product_labor ( + labor_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '工价ID', + product_id bigint(20) NOT NULL COMMENT '产品ID', + labor_name varchar(255) NOT NULL COMMENT '工价名称/说明(手动输入)', + labor_price decimal(12,2) NOT NULL DEFAULT 0.00 COMMENT '工价金额', + del_flag tinyint(1) NOT NULL DEFAULT 0 COMMENT '删除标识 0-未删除 1-已删除', + create_by varchar(64) DEFAULT '' COMMENT '创建者', + create_time datetime COMMENT '创建时间', + update_by varchar(64) DEFAULT '' COMMENT '更新者', + update_time datetime COMMENT '更新时间', + remark varchar(500) DEFAULT NULL COMMENT '备注', + PRIMARY KEY (labor_id), + KEY idx_product_id (product_id) +) ENGINE=InnoDB COMMENT='产品手动工价表';