Forráskód Böngészése

feature:
组织管理-生产组织车间、产线、产线产品关系
数码查询-流向查询 功能提交
生产批次管理-生产任务上传记录、补打信息记录 页面提交

yingjian.wu 4 hónapja
szülő
commit
c285de6018
45 módosított fájl, 8158 hozzáadás és 909 törlés
  1. 209 189
      pom.xml
  2. 3 3
      src/main/java/com/qlm/controller/core/SearchController.java
  3. 76 0
      src/main/java/com/qlm/controller/jinzai/ProduceOrderController.java
  4. 146 0
      src/main/java/com/qlm/controller/organize/BrandController.java
  5. 211 0
      src/main/java/com/qlm/controller/organize/LineProductController.java
  6. 164 0
      src/main/java/com/qlm/controller/organize/ProductionLineController.java
  7. 147 0
      src/main/java/com/qlm/controller/organize/WorkshopController.java
  8. 109 0
      src/main/java/com/qlm/dto/BrandDto.java
  9. 164 0
      src/main/java/com/qlm/dto/LineProductDto.java
  10. 67 0
      src/main/java/com/qlm/dto/LineProductImportDto.java
  11. 194 0
      src/main/java/com/qlm/dto/ProductionLineDto.java
  12. 155 0
      src/main/java/com/qlm/dto/WorkshopDto.java
  13. 74 0
      src/main/java/com/qlm/netty/NettyServer.java
  14. 52 0
      src/main/java/com/qlm/netty/codec/CustomDecoder.java
  15. 33 0
      src/main/java/com/qlm/netty/codec/CustomEncoder.java
  16. 93 0
      src/main/java/com/qlm/netty/handler/NettyServerHandler.java
  17. 46 0
      src/main/java/com/qlm/netty/model/IndustrialControlData.java
  18. 35 0
      src/main/java/com/qlm/netty/protocol/CustomProtocol.java
  19. 24 0
      src/main/java/com/qlm/oss/OssConfig.java
  20. 180 0
      src/main/java/com/qlm/oss/OssUtil.java
  21. 631 631
      src/main/java/com/qlm/poi/ExcelUtil.java
  22. 69 69
      src/main/java/com/qlm/poi/PoiExcelHelperImpl07.java
  23. 32 0
      src/main/java/com/qlm/service/IBrandService.java
  24. 80 0
      src/main/java/com/qlm/service/ILineProductService.java
  25. 47 0
      src/main/java/com/qlm/service/IProductionLineService.java
  26. 42 0
      src/main/java/com/qlm/service/IWorkshopService.java
  27. 165 0
      src/main/java/com/qlm/service/impl/BrandServiceImpl.java
  28. 528 0
      src/main/java/com/qlm/service/impl/LineProductServiceImpl.java
  29. 333 0
      src/main/java/com/qlm/service/impl/ProductionLineServiceImpl.java
  30. 14 8
      src/main/java/com/qlm/service/impl/WorkshopServiceImpl.java
  31. 78 0
      src/main/java/com/qlm/tools/EasyExcelUtil.java
  32. 15 5
      src/main/resources/config.properties
  33. 3 3
      src/main/resources/job.properties
  34. 479 0
      src/main/webapp/page/jinzai/brand.jsp
  35. 1 1
      src/main/webapp/page/jinzai/factory.jsp
  36. 297 0
      src/main/webapp/page/jinzai/flow_search.jsp
  37. 592 0
      src/main/webapp/page/jinzai/line_product_relation.jsp
  38. 316 0
      src/main/webapp/page/jinzai/line_product_relation_add.jsp
  39. 322 0
      src/main/webapp/page/jinzai/production_line.jsp
  40. 371 0
      src/main/webapp/page/jinzai/production_line_add.jsp
  41. 341 0
      src/main/webapp/page/jinzai/production_task_upload.jsp
  42. 302 0
      src/main/webapp/page/jinzai/reprint_record.jsp
  43. 335 0
      src/main/webapp/page/jinzai/workshop.jsp
  44. 330 0
      src/main/webapp/page/jinzai/workshop_add.jsp
  45. 253 0
      src/main/webapp/page/product/edit.jsp

+ 209 - 189
pom.xml

@@ -1,193 +1,213 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<groupId>jinzai</groupId>
-	<artifactId>jinzai</artifactId>
-	<packaging>war</packaging>
-	<version>0.0.1-SNAPSHOT</version>
-	<name>xyk Maven Webapp</name>
-	<url>http://maven.apache.org</url>
-	<build>
-		<finalName>tmzn</finalName>
-		<plugins>
-			<plugin>
-				<artifactId>maven-compiler-plugin</artifactId>
-				<version>3.1</version>
-				<configuration>
-					<source>1.8</source>
-					<target>1.8</target>
-				</configuration>
-			</plugin>
-	</plugins>
-	</build>		
-	<dependencies>
-		<dependency>
-			<groupId>javax.servlet.jsp</groupId>
-			<artifactId>jsp-api</artifactId>
-			<version>2.2</version>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>javax.servlet</groupId>
-			<artifactId>javax.servlet-api</artifactId>
-			<version>3.1.0</version>
-			<scope>provided</scope>
-		</dependency>
-			<dependency>
-			<groupId>com.oracle.database.jdbc</groupId>
-			<artifactId>ojdbc8</artifactId>
-			<version>21.5.0.0</version>
-		</dependency>
-		<!-- IP地址转归属地 -->
-<dependency>
-   <groupId>org.lionsoul</groupId>
-   <artifactId>ip2region</artifactId>
-   <version>2.6.4</version>
-</dependency>
-<dependency>
-    <groupId>com.hankcs</groupId>
-    <artifactId>hanlp</artifactId>
-    <version>portable-1.8.2</version>
-</dependency>
-		  	<dependency>
-			<groupId>com.qiniu</groupId>
-			<artifactId>qiniu-java-sdk</artifactId>
-			<version>7.0.8</version>
-		</dependency>
-		<dependency>
-			<groupId>junit</groupId>
-			<artifactId>junit</artifactId>
-			<version>3.8.1</version>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>com.alibaba</groupId>
-			<artifactId>fastjson</artifactId>
-			<version>1.1.41</version>
-		</dependency>
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>jinzai</groupId>
+    <artifactId>jinzai</artifactId>
+    <packaging>war</packaging>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>xyk Maven Webapp</name>
+    <url>http://maven.apache.org</url>
+    <build>
+        <finalName>tmzn</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet.jsp</groupId>
+            <artifactId>jsp-api</artifactId>
+            <version>2.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>3.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.oracle.database.jdbc</groupId>
+            <artifactId>ojdbc8</artifactId>
+            <version>21.5.0.0</version>
+        </dependency>
+        <!-- IP地址转归属地 -->
+        <dependency>
+            <groupId>org.lionsoul</groupId>
+            <artifactId>ip2region</artifactId>
+            <version>2.6.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.hankcs</groupId>
+            <artifactId>hanlp</artifactId>
+            <version>portable-1.8.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qiniu</groupId>
+            <artifactId>qiniu-java-sdk</artifactId>
+            <version>7.0.8</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.1.41</version>
+        </dependency>
 
-		<dependency>
-			<groupId>jstl</groupId>
-			<artifactId>jstl</artifactId>
-			<version>1.2</version>
-		</dependency>
-		<dependency>
-			<groupId>com.jfinal</groupId>
-			<artifactId>jfinal</artifactId>
-			<version>2.2</version>
-		</dependency>
-		<dependency>
-			<groupId>com.jfinal</groupId>
-			<artifactId>jfinal-weixin</artifactId>
-			<version>1.5</version>
-		</dependency>
-		<dependency>
-			<groupId>org.freemarker</groupId>
-			<artifactId>freemarker</artifactId>
-			<version>2.3.19</version>
-		</dependency>
+        <dependency>
+            <groupId>jstl</groupId>
+            <artifactId>jstl</artifactId>
+            <version>1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.jfinal</groupId>
+            <artifactId>jfinal</artifactId>
+            <version>2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.jfinal</groupId>
+            <artifactId>jfinal-weixin</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.19</version>
+        </dependency>
 
-		<dependency>
-			<groupId>commons-collections</groupId>
-			<artifactId>commons-collections</artifactId>
-			<version>3.2.1</version>
-		</dependency>
-		<dependency>
-			<groupId>org.mybatis</groupId>
-			<artifactId>mybatis</artifactId>
-			<version>3.2.1</version>
-		</dependency>
-		<dependency>
-			<!-- MySQL数据库驱动 -->
-			<groupId>mysql</groupId>
-			<artifactId>mysql-connector-java</artifactId>
-			<version>8.0.16</version>
-		</dependency>
-		<dependency>
-			<groupId>c3p0</groupId>
-			<artifactId>c3p0</artifactId>
-			<version>0.9.1.2</version>
-		</dependency>
-		<dependency>
-		    <groupId>com.alibaba</groupId>
-		    <artifactId>druid</artifactId>
-		    <version>1.1.20</version>
-		</dependency>
-		<dependency>
-			<groupId>commons-pool</groupId>
-			<artifactId>commons-pool</artifactId>
-			<version>20030825.183949</version>
-		</dependency>
-		<dependency>
-			<groupId>commons-fileupload</groupId>
-			<artifactId>commons-fileupload</artifactId>
-			<version>1.2.1</version>
-		</dependency>
-		<dependency>
-			<groupId>org.json</groupId>
-			<artifactId>json</artifactId>
-			<version>20080701</version>
-		</dependency>
-		<dependency>
-			<groupId>net.sf.ehcache</groupId>
-			<artifactId>ehcache</artifactId>
-			<version>2.8.1</version>
-		</dependency>
-		<!-- 这里是sl4j的包 -->
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-api</artifactId>
-			<version>1.7.13</version>
-		</dependency>
-		<dependency>
-			<groupId>ch.qos.logback</groupId>
-			<artifactId>logback-core</artifactId>
-			<version>1.1.3</version>
-		</dependency>
-		<dependency>
-			<groupId>ch.qos.logback</groupId>
-			<artifactId>logback-classic</artifactId>
-			<version>1.1.3</version>
-		</dependency>
-		<dependency>
-			<groupId>com.jfinal</groupId>
-			<artifactId>jfinal-ext2</artifactId>
-			<version>2.0.5</version>
-		</dependency>
-		<dependency>
-			<groupId>org.quartz-scheduler</groupId>
-			<artifactId>quartz</artifactId>
-			<version>2.0.2</version>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.poi</groupId>
-			<artifactId>poi-ooxml</artifactId>
-			<version>3.10-FINAL</version>
-		</dependency>
-		<dependency>
-			<groupId>commons-lang</groupId>
-			<artifactId>commons-lang</artifactId>
-			<version>2.6</version>
-		</dependency>
-		<dependency>
-		    <groupId>org.apache.ant</groupId>
-		    <artifactId>ant</artifactId>
-		    <version>1.8.2</version>
-		</dependency>
-					<dependency>
-    <groupId>com.google.zxing</groupId>
-    <artifactId>core</artifactId>
-    <version>3.1.0</version>
-</dependency>
-<dependency>
-    <groupId>com.google.zxing</groupId>
-    <artifactId>javase</artifactId>
-    <version>3.1.0</version>
-</dependency>
-<dependency>
-    <groupId>org.jsoup</groupId>
-    <artifactId>jsoup</artifactId>
-    <version>1.14.3</version>
-</dependency>
-	</dependencies>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis</artifactId>
+            <version>3.2.1</version>
+        </dependency>
+        <dependency>
+            <!-- MySQL数据库驱动 -->
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.16</version>
+        </dependency>
+        <dependency>
+            <groupId>c3p0</groupId>
+            <artifactId>c3p0</artifactId>
+            <version>0.9.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.1.20</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-pool</groupId>
+            <artifactId>commons-pool</artifactId>
+            <version>20030825.183949</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20080701</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <version>2.8.1</version>
+        </dependency>
+        <!-- 这里是sl4j的包 -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.13</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <version>1.1.3</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.1.3</version>
+        </dependency>
+        <dependency>
+            <groupId>com.jfinal</groupId>
+            <artifactId>jfinal-ext2</artifactId>
+            <version>2.0.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.quartz-scheduler</groupId>
+            <artifactId>quartz</artifactId>
+            <version>2.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>1.8.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+		<dependency>
+			<groupId>io.netty</groupId>
+			<artifactId>netty-all</artifactId>
+			<version>4.1.109.Final</version> <!-- 请使用最新稳定版本 -->
+		</dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.14.3</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.15.1</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.39</version>
+        </dependency>
+    </dependencies>
 </project>

+ 3 - 3
src/main/java/com/qlm/controller/core/SearchController.java

@@ -19,7 +19,7 @@ import com.qlm.controller.common.CommonController;
 import com.qlm.entity.ScanRecord;
 import com.qlm.interceptor.AuthInterceptor;
 import com.qlm.poi.ExcelHeader;
-import com.qlm.poi.ExcelUtil;
+//import com.qlm.poi.ExcelUtil;
 import com.qlm.tongji.service.ISearchService;
 import com.qlm.tongji.service.impl.SearchServiceImpl;
 import com.qlm.tools.DateUtils;
@@ -130,8 +130,8 @@ public class SearchController extends CommonController {
 			headers[i] = header;
 		}
 		String orderNo = WxUtil.getOrderNo();
-		File exportNormal = ExcelUtil.exportNormal(date+"扫码记录_"+orderNo,date, headers, list);
-		renderFile(exportNormal);
+		//File exportNormal = ExcelUtil.exportNormal(date+"扫码记录_"+orderNo,date, headers, list);
+		renderFile(new File(""));
 	}
 	
 	public void searchByNickname(){

+ 76 - 0
src/main/java/com/qlm/controller/jinzai/ProduceOrderController.java

@@ -6,10 +6,12 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import cn.hutool.core.date.DateUtil;
 import com.jfinal.aop.Clear;
 import com.jfinal.plugin.activerecord.Db;
 import com.jfinal.plugin.activerecord.Record;
 import com.qlm.annotation.RequestUrl;
+import com.qlm.common.ApiResponse;
 import com.qlm.controller.common.CommonController;
 import com.qlm.tools.WxUtil;
 
@@ -246,5 +248,79 @@ public class ProduceOrderController extends CommonController{
 		}
 		WxUtil.batchSaveIgnore("deliveryorders", list, "");
 	}
+
+
+	public void getCodeFlowSearchPage(){
+		renderJsp("/page/jinzai/flow_search.jsp");
+	}
+
+	/**
+	 * 盒码流向查询
+	 */
+	public void getCodeFlowSearch(){
+		ApiResponse result = ApiResponse.success();
+		String code = getPara("code");
+		if(WxUtil.isNull(code)){
+			result = ApiResponse.error("请输入盒码");
+			renderJson(result);
+			return;
+		}
+		//先查询小码信息
+		Record smallCodeInfo = Db.findFirst("select * from jinzai_upload_child where child_code = ?",code);
+		if(smallCodeInfo == null){
+			result = ApiResponse.error("盒码不存在");
+			renderJson(result);
+			return;
+		}
+		//查询大码信息
+		Record bigCodeInfo = Db.findFirst("select * from jinzai_upload_master where id = ?",smallCodeInfo.getStr("master_code"));
+		if(bigCodeInfo == null){
+			result = ApiResponse.error("箱码不存在");
+			renderJson(result);
+			return;
+		}
+		String sku = bigCodeInfo.getStr("sku");
+		//根据sku查询品相
+		Record quality = Db.findFirst("select * from t_jz_item where sku = ?",sku);
+		if(quality == null){
+			result = ApiResponse.error("品相不存在");
+			renderJson(result);
+			return;
+		}
+		//根据product_id 查询产品信息
+		Record product = Db.findFirst("select tj.*, tjp.type_name from t_jz_product  tj left join t_jz_product_type tjp on tj.product_type = tjp.id where tj.id = ?",WxUtil.getInt("product_id", quality));
+		if(product == null){
+			result = ApiResponse.error("产品不存在");
+			renderJson(result);
+			return;
+		}
+
+		// 查询出库信息
+		Record deliveryOrder = Db.findFirst("select * from deliveryorders where CaseCode = ?",smallCodeInfo.getStr("master_code"));
+		//组装返回信息
+		//产品信息对象、入库信息对象、出库信息对象
+		Record productInfo = new Record();
+		productInfo.set("productCode", product.getStr("product_code")).set("productName", product.getStr("product_name"))
+		.set("itemCode", quality.getStr("item_code")).set("itemName", quality.getStr("item_name"))
+		.set("category", product.getStr("type_name"));
+
+		Record inInfo = new Record();
+		inInfo.set("productionNo", bigCodeInfo.getStr("task_no")).set("boxCode", code)
+		.set("cartoonCode", bigCodeInfo.getStr("id")).set("palletCode", bigCodeInfo.getStr("duo_code"))
+		.set("inboundPerson", bigCodeInfo.getStr("device_no")).set("productionDate", DateUtil.format(bigCodeInfo.getDate("upload_time"), "yyyy-MM-dd HH:mm:ss"));
+
+
+		Record outInfo = new Record();
+		if(deliveryOrder != null){
+			outInfo.set("outboundNo", deliveryOrder.getStr("OrderNo")).set("warehouse", deliveryOrder.getStr("OutWarehouse"))
+			.set("outboundPerson", deliveryOrder.getStr("OperateUser")).set("dealerName", deliveryOrder.getStr("CustomerName"))
+			.set("salesArea", "").set("outboundTime", DateUtil.format(deliveryOrder.getDate("DeliveryTime"), "yyyy-MM-dd HH:mm:ss"));
+		}
+
+		Record endRecord = new Record();
+		endRecord.set("product", productInfo).set("inbound", inInfo).set("outbound", outInfo);
+		result.setData(endRecord);
+		renderJson(result);
+	}
 	
 }

+ 146 - 0
src/main/java/com/qlm/controller/organize/BrandController.java

@@ -0,0 +1,146 @@
+package com.qlm.controller.organize;
+
+import com.jfinal.kit.HttpKit;
+import com.qlm.annotation.RequestUrl;
+import com.qlm.common.ApiResponse;
+import com.qlm.controller.common.CommonUserController;
+import com.qlm.dto.BrandDto;
+import com.qlm.service.IBrandService;
+import com.qlm.service.impl.BrandServiceImpl;
+import com.alibaba.fastjson.JSON;
+import com.qlm.view.core.AdminView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author wuyingjianwu
+ * @date 2025/08/22
+ * 品牌信息管理
+ */
+@RequestUrl("/brand")
+public class BrandController extends CommonUserController {
+    private static final Logger logger = LoggerFactory.getLogger(BrandController.class);
+    private final IBrandService brandService = new BrandServiceImpl();
+
+    public void getBrandList() {
+        renderJsp("/page/jinzai/brand.jsp");
+    }
+
+    /**
+     * 保存品牌信息
+     */
+    @Override
+    public void save() {
+        AdminView loginUser = getLoginUser();
+        ApiResponse apiResponse;
+        try {
+            // 使用HttpKit.readData获取请求Body
+            String bodyData = HttpKit.readData(getRequest());
+            // 将JSON字符串转换为BrandDto对象
+            BrandDto brandDto = JSON.parseObject(bodyData, BrandDto.class);
+            brandDto.setOperator(loginUser.getUsername());
+
+            // 数据验证
+            if (brandDto.getBrandCode() == null || brandDto.getBrandCode().trim().isEmpty()) {
+                apiResponse = ApiResponse.error("品牌编号不能为空");
+                renderJson(apiResponse);
+                return;
+            }
+
+            if (brandDto.getBrandName() == null || brandDto.getBrandName().trim().isEmpty()) {
+                apiResponse = ApiResponse.error("品牌名称不能为空");
+                renderJson(apiResponse);
+                return;
+            }
+
+            // 调用Service层保存数据
+            apiResponse = brandService.saveBrand(brandDto);
+        } catch (Exception e) {
+            logger.error("保存品牌信息异常:", e);
+            apiResponse = ApiResponse.error("系统异常:" + e.getMessage());
+        }
+        renderJson(apiResponse);
+    }
+
+    /**
+     * 更新品牌信息
+     */
+    public void update() {
+        AdminView loginUser = getLoginUser();
+        ApiResponse apiResponse;
+        try {
+            String bodyData = HttpKit.readData(getRequest());
+            BrandDto brandDto = JSON.parseObject(bodyData, BrandDto.class);
+            brandDto.setOperator(loginUser.getUsername());
+
+            if (brandDto.getId() == null) {
+                apiResponse = ApiResponse.error("品牌ID不能为空");
+                renderJson(apiResponse);
+                return;
+            }
+
+            apiResponse = brandService.updateBrand(brandDto);
+        } catch (Exception e) {
+            logger.error("更新品牌信息异常:", e);
+            apiResponse = ApiResponse.error("系统异常:" + e.getMessage());
+        }
+        renderJson(apiResponse);
+    }
+
+    /**
+     * 删除品牌信息
+     */
+    public void delete() {
+        ApiResponse apiResponse;
+        try {
+            Long id = getParaToLong("id");
+            if (id == null) {
+                apiResponse = ApiResponse.error("品牌ID不能为空");
+                renderJson(apiResponse);
+                return;
+            }
+
+            apiResponse = brandService.deleteBrand(id);
+        } catch (Exception e) {
+            logger.error("删除品牌信息异常:", e);
+            apiResponse = ApiResponse.error("系统异常:" + e.getMessage());
+        }
+        renderJson(apiResponse);
+    }
+
+    /**
+     * 根据ID获取品牌信息
+     */
+    @Override
+    public void getById() {
+        ApiResponse apiResponse;
+        try {
+            Long id = getParaToLong("id");
+            if (id == null) {
+                apiResponse = ApiResponse.error("品牌ID不能为空");
+                renderJson(apiResponse);
+                return;
+            }
+
+            apiResponse = brandService.getBrandById(id);
+        } catch (Exception e) {
+            logger.error("获取品牌信息异常:", e);
+            apiResponse = ApiResponse.error("系统异常:" + e.getMessage());
+        }
+        renderJson(apiResponse);
+    }
+
+    /**
+     * 品牌列表查询
+     */
+    @Override
+    public void list() {
+        // 获取分页参数
+        int pageNumber = getParaToInt("pageNumber", 1);
+        int pageSize = getParaToInt("pageSize", 10);
+        // 获取查询条件
+        String keyword = getPara("keyword");
+        // 调用Service层查询列表
+        renderJson(brandService.listBrand(pageNumber, pageSize, keyword));
+    }
+}

+ 211 - 0
src/main/java/com/qlm/controller/organize/LineProductController.java

@@ -0,0 +1,211 @@
+package com.qlm.controller.organize;
+
+import com.jfinal.aop.Clear;
+import com.jfinal.kit.HttpKit;
+import com.jfinal.upload.UploadFile;
+import com.qlm.annotation.RequestUrl;
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.controller.common.CommonUserController;
+import com.qlm.dto.LineProductDto;
+import com.qlm.dto.LineProductImportDto;
+import com.qlm.service.ILineProductService;
+import com.qlm.service.impl.LineProductServiceImpl;
+import com.qlm.tools.EasyExcelUtil;
+import com.qlm.view.core.AdminView;
+import com.alibaba.fastjson.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.List;
+
+/**
+ * @author wuyingjianwu
+ * @date 2025/08/21
+ * 产线产品关联管理
+ */
+@RequestUrl("/lineProduct")
+public class LineProductController extends CommonUserController {
+
+    private static final Logger logger = LoggerFactory.getLogger(LineProductController.class);
+    private final ILineProductService lineProductService = new LineProductServiceImpl();
+
+    public void getLineProductList() {
+        renderJsp("/page/jinzai/line_product_relation.jsp");
+    }
+
+    /**
+     * 保存产线产品关联信息
+     */
+    @Override
+    public void save() {
+        try {
+            // 使用HttpKit.readData获取请求Body
+            String bodyData = HttpKit.readData(getRequest());
+            // 将JSON字符串转换为LineProductDto对象
+            LineProductDto lineProductDto = JSON.parseObject(bodyData, LineProductDto.class);
+
+            ApiResponse apiResponse = lineProductService.saveLineProduct(lineProductDto);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("保存产线产品关联信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 删除产线产品关联信息
+     */
+    public void delete() {
+        try {
+            Integer id = getParaToInt("id");
+            ApiResponse apiResponse = lineProductService.deleteLineProduct(id);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("删除产线产品关联信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据产线ID和产品ID获取关联信息
+     */
+    public void getById() {
+        try {
+            Integer id = getParaToInt("id");
+            ApiResponse apiResponse = lineProductService.getLineProductById(id);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取产线产品关联信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 产线产品关联列表查询
+     */
+    @Override
+    public void list() {
+        try {
+            // 获取分页参数
+            int pageNumber = getParaToInt("pageNumber", 1);
+            int pageSize = getParaToInt("pageSize", 10);
+
+            // 获取查询条件
+            Long factoryId = getParaToLong("factoryId");
+            Long workshopId = getParaToLong("workshopId");
+            Long lineId = getParaToLong("lineId");
+            Long productId = getParaToLong("productId");
+
+            // 调用Service层查询列表
+            PageResult<LineProductDto> pageResult = lineProductService.listLineProduct(pageNumber, pageSize, factoryId, workshopId, lineId, productId);
+            renderJson(pageResult);
+        } catch (Exception e) {
+            logger.error("查询产线产品关联列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 获取所有工厂列表(用于下拉选择)
+     */
+    public void getFactoryList() {
+        String keyword = getPara("keyword");
+        try {
+            ApiResponse apiResponse = lineProductService.getAllFactories(keyword);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取工厂列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据工厂ID获取车间列表(用于下拉选择)
+     */
+    public void getWorkshopList() {
+        Long factoryId = getParaToLong("factoryId");
+        try {
+            ApiResponse apiResponse = lineProductService.getWorkshopsByFactoryId(factoryId);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("根据工厂ID获取车间列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据车间ID获取产线列表(用于下拉选择)
+     */
+    public void getLineList() {
+        Long workshopId = getParaToLong("workshopId");
+        try {
+            ApiResponse apiResponse = lineProductService.getLinesByWorkshopId(workshopId);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("根据车间ID获取产线列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 获取产品列表(用于下拉选择)
+     */
+    public void getProductList() {
+        String keyword = getPara("keyword");
+        try {
+            ApiResponse apiResponse = lineProductService.getAllProducts(keyword);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取产品列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 修改产线产品关联信息
+     */
+    public void update() {
+        try {
+            // 使用HttpKit.readData获取请求Body
+            String bodyData = HttpKit.readData(getRequest());
+            // 将JSON字符串转换为LineProductDto对象
+            LineProductDto lineProductDto = JSON.parseObject(bodyData, LineProductDto.class);
+    
+            ApiResponse apiResponse = lineProductService.updateLineProduct(lineProductDto);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("修改产线产品关联信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 导入产线产品关联信息
+     */
+    public void importLineProduct() {
+        try {
+            // 获取当前登录用户
+            AdminView loginUser = getLoginUser();
+            // 获取上传的文件
+            UploadFile uploadFile = getFile("file");
+            if (uploadFile == null) {
+                renderJson(ApiResponse.error("请选择要导入的文件"));
+                return;
+            }
+            // 使用EasyExcelUtil解析文件
+            InputStream inputStream = Files.newInputStream(uploadFile.getFile().toPath());
+            List<LineProductImportDto> importList = EasyExcelUtil.getImportData(inputStream, LineProductImportDto.class);
+            inputStream.close();
+            // 调用Service层导入数据
+            ApiResponse apiResponse = lineProductService.importLineProduct(importList, loginUser.getUsername());
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("导入产线产品关联信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+}

+ 164 - 0
src/main/java/com/qlm/controller/organize/ProductionLineController.java

@@ -0,0 +1,164 @@
+package com.qlm.controller.organize;
+
+import com.jfinal.kit.HttpKit;
+import com.qlm.annotation.RequestUrl;
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.controller.common.CommonUserController;
+import com.qlm.dto.ProductionLineDto;
+import com.qlm.service.IProductionLineService;
+import com.qlm.service.impl.ProductionLineServiceImpl;
+import com.alibaba.fastjson.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author wuyingjianwu
+ * @date 2025/08/21
+ * 产线信息管理
+ */
+@RequestUrl("/productionLine")
+public class ProductionLineController extends CommonUserController {
+
+    private static final Logger logger = LoggerFactory.getLogger(ProductionLineController.class);
+    private final IProductionLineService productionLineService = new ProductionLineServiceImpl();
+
+
+    public void getProductionLineList() {
+        renderJsp("/page/jinzai/production_line.jsp");
+    }
+
+    /**
+     * 保存产线信息
+     */
+    @Override
+    public void save() {
+        try {
+            // 使用HttpKit.readData获取请求Body
+            String bodyData = HttpKit.readData(getRequest());
+            // 将JSON字符串转换为ProductionLineDto对象
+            ProductionLineDto productionLineDto = JSON.parseObject(bodyData, ProductionLineDto.class);
+
+            ApiResponse apiResponse = productionLineService.saveProductionLine(productionLineDto);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("保存产线信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 更新产线信息
+     */
+    public void update() {
+        try {
+            String bodyData = HttpKit.readData(getRequest());
+            ProductionLineDto productionLineDto = JSON.parseObject(bodyData, ProductionLineDto.class);
+
+            ApiResponse apiResponse = productionLineService.updateProductionLine(productionLineDto);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("更新产线信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 删除产线信息
+     */
+    public void delete() {
+        try {
+            Long id = getParaToLong("id");
+            ApiResponse apiResponse = productionLineService.deleteProductionLine(id);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("删除产线信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据ID获取产线信息
+     */
+    @Override
+    public void getById() {
+        try {
+            Long id = getParaToLong("id");
+            ApiResponse apiResponse = productionLineService.getProductionLineById(id);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取产线信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 切换产线状态
+     */
+    public void toggleStatus() {
+        try {
+            Long id = getParaToLong("id");
+            Integer status = getParaToInt("status");
+
+            ApiResponse apiResponse = productionLineService.toggleProductionLineStatus(id, status);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("切换产线状态异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 产线列表查询
+     */
+    @Override
+    public void list() {
+        try {
+            // 获取分页参数
+            int pageNumber = getParaToInt("pageNumber", 1);
+            int pageSize = getParaToInt("pageSize", 10);
+
+            // 获取查询条件
+            String lineName = getPara("lineName");
+            String lineCode = getPara("lineCode");
+            Long factoryId = getParaToLong("factoryId");
+            Long workshopId = getParaToLong("workshopId");
+
+            // 调用Service层查询列表
+            PageResult<ProductionLineDto> pageResult = productionLineService.listProductionLine(pageNumber, pageSize, lineName, lineCode, factoryId, workshopId);
+            renderJson(pageResult);
+        } catch (Exception e) {
+            logger.error("查询产线列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 获取所有工厂列表(用于下拉选择)
+     */
+    public void getFactoryList() {
+        String keyword = getPara("keyword");
+        try {
+            ApiResponse apiResponse = productionLineService.getAllFactories(keyword);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取工厂列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据工厂ID获取车间列表(用于下拉选择)
+     */
+    public void getWorkshopListByFactoryId() {
+        Long factoryId = getParaToLong("factoryId");
+        String keyword = getPara("keyword");
+        try {
+            ApiResponse apiResponse = productionLineService.getWorkshopsByFactoryId(factoryId, keyword);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取车间列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+}

+ 147 - 0
src/main/java/com/qlm/controller/organize/WorkshopController.java

@@ -0,0 +1,147 @@
+package com.qlm.controller.organize;
+
+import com.jfinal.kit.HttpKit;
+import com.qlm.annotation.RequestUrl;
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.controller.common.CommonUserController;
+import com.qlm.dto.WorkshopDto;
+import com.qlm.service.IWorkshopService;
+import com.qlm.service.impl.WorkshopServiceImpl;
+import com.alibaba.fastjson.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author wuyingjianwu
+ * @date 2025/08/20
+ * 车间信息管理
+ */
+@RequestUrl("/workshop")
+public class WorkshopController extends CommonUserController {
+
+    private static final Logger logger = LoggerFactory.getLogger(WorkshopController.class);
+    private final IWorkshopService workshopService = new WorkshopServiceImpl();
+
+
+    public void getWorkshopList() {
+        renderJsp("/page/jinzai/workshop.jsp");
+    }
+
+    /**
+     * 保存车间信息
+     */
+    @Override
+    public void save() {
+        try {
+            // 使用HttpKit.readData获取请求Body
+            String bodyData = HttpKit.readData(getRequest());
+            // 将JSON字符串转换为WorkshopDto对象
+            WorkshopDto workshopDto = JSON.parseObject(bodyData, WorkshopDto.class);
+
+            ApiResponse apiResponse = workshopService.saveWorkshop(workshopDto);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("保存车间信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 更新车间信息
+     */
+    public void update() {
+        try {
+            String bodyData = HttpKit.readData(getRequest());
+            WorkshopDto workshopDto = JSON.parseObject(bodyData, WorkshopDto.class);
+
+            ApiResponse apiResponse = workshopService.updateWorkshop(workshopDto);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("更新车间信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 删除车间信息
+     */
+    public void delete() {
+        try {
+            Long id = getParaToLong("id");
+            ApiResponse apiResponse = workshopService.deleteWorkshop(id);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("删除车间信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据ID获取车间信息
+     */
+    @Override
+    public void getById() {
+        try {
+            Long id = getParaToLong("id");
+            ApiResponse apiResponse = workshopService.getWorkshopById(id);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取车间信息异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 切换车间状态
+     */
+    public void toggleStatus() {
+        try {
+            Long id = getParaToLong("id");
+            Integer status = getParaToInt("status");
+
+            ApiResponse apiResponse = workshopService.toggleWorkshopStatus(id, status);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("切换车间状态异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 车间列表查询
+     */
+    @Override
+    public void list() {
+        try {
+            // 获取分页参数
+            int pageNumber = getParaToInt("pageNumber", 1);
+            int pageSize = getParaToInt("pageSize", 10);
+
+            // 获取查询条件
+            String workshopName = getPara("workshopName");
+            Long factoryId = getParaToLong("factoryId");
+
+            // 调用Service层查询列表
+            PageResult<WorkshopDto> pageResult = workshopService.listWorkshop(pageNumber, pageSize, workshopName, factoryId);
+            renderJson(pageResult);
+        } catch (Exception e) {
+            logger.error("查询车间列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+
+    /**
+     * 获取所有工厂列表(用于下拉选择)
+     */
+    public void getFactoryList() {
+        String keyword = getPara("keyword");
+        try {
+            ApiResponse apiResponse = workshopService.getAllFactories(keyword);
+            renderJson(apiResponse);
+        } catch (Exception e) {
+            logger.error("获取工厂列表异常:", e);
+            renderJson(ApiResponse.error("系统异常:" + e.getMessage()));
+        }
+    }
+}

+ 109 - 0
src/main/java/com/qlm/dto/BrandDto.java

@@ -0,0 +1,109 @@
+package com.qlm.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 品牌信息DTO
+ */
+public class BrandDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+    /**
+     * 品牌编号
+     */
+    private String brandCode;
+    /**
+     * 品牌名称
+     */
+    private String brandName;
+    /**
+     * 联系方式
+     */
+    private String contactPhone;
+    /**
+     * 状态 0停用 1启用
+     */
+    private Integer status;
+    /**
+     * 创建时间
+     */
+    private Date createdTime;
+    /**
+     * 修改时间
+     */
+    private Date updatedTime;
+    /**
+     * 操作人
+     */
+    private String operator;
+
+    // getter和setter方法
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getBrandCode() {
+        return brandCode;
+    }
+
+    public void setBrandCode(String brandCode) {
+        this.brandCode = brandCode;
+    }
+
+    public String getBrandName() {
+        return brandName;
+    }
+
+    public void setBrandName(String brandName) {
+        this.brandName = brandName;
+    }
+
+    public String getContactPhone() {
+        return contactPhone;
+    }
+
+    public void setContactPhone(String contactPhone) {
+        this.contactPhone = contactPhone;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public String getOperator() {
+        return operator;
+    }
+
+    public void setOperator(String operator) {
+        this.operator = operator;
+    }
+}

+ 164 - 0
src/main/java/com/qlm/dto/LineProductDto.java

@@ -0,0 +1,164 @@
+package com.qlm.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 产线产品关联表DTO
+ */
+public class LineProductDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    /**
+     * 工厂ID
+     */
+    private Long factoryId;
+
+    /**
+     * 工厂名称
+     */
+    private String factoryName;
+
+    /**
+     * 车间ID
+     */
+    private Long workshopId;
+
+    /**
+     * 车间名称
+     */
+    private String workshopName;
+
+    /**
+     * 产线ID
+     */
+    private Long lineId;
+
+    /**
+     * 产线名称
+     */
+    private String lineName;
+
+    /**
+     * 产品ID
+     */
+    private Long productId;
+
+    /**
+     * 产品名称
+     */
+    private String productName;
+
+    /**
+     * 创建时间
+     */
+    private Date createdTime;
+
+    /**
+     * 修改时间
+     */
+    private Date updatedTime;
+
+    /**
+     * 操作人
+     */
+    private String operator;
+
+    // getter和setter方法
+    public Long getFactoryId() {
+        return factoryId;
+    }
+
+    public void setFactoryId(Long factoryId) {
+        this.factoryId = factoryId;
+    }
+
+    public String getFactoryName() {
+        return factoryName;
+    }
+
+    public void setFactoryName(String factoryName) {
+        this.factoryName = factoryName;
+    }
+
+    public Long getWorkshopId() {
+        return workshopId;
+    }
+
+    public void setWorkshopId(Long workshopId) {
+        this.workshopId = workshopId;
+    }
+
+    public String getWorkshopName() {
+        return workshopName;
+    }
+
+    public void setWorkshopName(String workshopName) {
+        this.workshopName = workshopName;
+    }
+
+    public Long getLineId() {
+        return lineId;
+    }
+
+    public void setLineId(Long lineId) {
+        this.lineId = lineId;
+    }
+
+    public String getLineName() {
+        return lineName;
+    }
+
+    public void setLineName(String lineName) {
+        this.lineName = lineName;
+    }
+
+    public Long getProductId() {
+        return productId;
+    }
+
+    public void setProductId(Long productId) {
+        this.productId = productId;
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public void setProductName(String productName) {
+        this.productName = productName;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public String getOperator() {
+        return operator;
+    }
+
+    public void setOperator(String operator) {
+        this.operator = operator;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+}

+ 67 - 0
src/main/java/com/qlm/dto/LineProductImportDto.java

@@ -0,0 +1,67 @@
+package com.qlm.dto;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import java.io.Serializable;
+
+/**
+ * 产线产品关联导入DTO
+ */
+public class LineProductImportDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ExcelProperty("工厂编号")
+    private String factoryCode;
+
+    @ExcelProperty("车间编号")
+    private String workshopCode;
+
+    @ExcelProperty("产线编号")
+    private String lineCode;
+
+    @ExcelProperty("产品编号")
+    private String productCode;
+
+    // 用于存储导入错误信息
+    private String errorMsg;
+
+    // getter和setter方法
+    public String getFactoryCode() {
+        return factoryCode;
+    }
+
+    public void setFactoryCode(String factoryCode) {
+        this.factoryCode = factoryCode;
+    }
+
+    public String getWorkshopCode() {
+        return workshopCode;
+    }
+
+    public void setWorkshopCode(String workshopCode) {
+        this.workshopCode = workshopCode;
+    }
+
+    public String getLineCode() {
+        return lineCode;
+    }
+
+    public void setLineCode(String lineCode) {
+        this.lineCode = lineCode;
+    }
+
+    public String getProductCode() {
+        return productCode;
+    }
+
+    public void setProductCode(String productCode) {
+        this.productCode = productCode;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+}

+ 194 - 0
src/main/java/com/qlm/dto/ProductionLineDto.java

@@ -0,0 +1,194 @@
+package com.qlm.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 产线信息DTO
+ */
+public class ProductionLineDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 产线名称
+     */
+    private String lineName;
+
+    /**
+     * 产线编号
+     */
+    private String lineCode;
+
+    /**
+     * 所属工厂ID
+     */
+    private Long factoryId;
+
+    /**
+     * 所属工厂名称
+     */
+    private String factoryName;
+
+    /**
+     * 所属车间ID
+     */
+    private Long workshopId;
+
+    /**
+     * 所属车间名称
+     */
+    private String workshopName;
+
+    /**
+     * 联系人
+     */
+    private String contactName;
+
+    /**
+     * 联系方式
+     */
+    private String contactPhone;
+
+    /**
+     * 关联账户
+     */
+    private String accountId;
+
+    /**
+     * 状态(0 禁用 1 启用)
+     */
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    private Date createdTime;
+
+    /**
+     * 修改时间
+     */
+    private Date updatedTime;
+
+    /**
+     * 操作人
+     */
+    private String operator;
+
+    // getter和setter方法
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getLineName() {
+        return lineName;
+    }
+
+    public void setLineName(String lineName) {
+        this.lineName = lineName;
+    }
+
+    public String getLineCode() {
+        return lineCode;
+    }
+
+    public void setLineCode(String lineCode) {
+        this.lineCode = lineCode;
+    }
+
+    public Long getFactoryId() {
+        return factoryId;
+    }
+
+    public void setFactoryId(Long factoryId) {
+        this.factoryId = factoryId;
+    }
+
+    public String getFactoryName() {
+        return factoryName;
+    }
+
+    public void setFactoryName(String factoryName) {
+        this.factoryName = factoryName;
+    }
+
+    public Long getWorkshopId() {
+        return workshopId;
+    }
+
+    public void setWorkshopId(Long workshopId) {
+        this.workshopId = workshopId;
+    }
+
+    public String getWorkshopName() {
+        return workshopName;
+    }
+
+    public void setWorkshopName(String workshopName) {
+        this.workshopName = workshopName;
+    }
+
+    public String getContactName() {
+        return contactName;
+    }
+
+    public void setContactName(String contactName) {
+        this.contactName = contactName;
+    }
+
+    public String getContactPhone() {
+        return contactPhone;
+    }
+
+    public void setContactPhone(String contactPhone) {
+        this.contactPhone = contactPhone;
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(String accountId) {
+        this.accountId = accountId;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public String getOperator() {
+        return operator;
+    }
+
+    public void setOperator(String operator) {
+        this.operator = operator;
+    }
+}

+ 155 - 0
src/main/java/com/qlm/dto/WorkshopDto.java

@@ -0,0 +1,155 @@
+package com.qlm.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 车间信息DTO
+ */
+public class WorkshopDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 车间编号
+     */
+    private String workshopCode;
+
+    /**
+     * 车间名称
+     */
+    private String workshopName;
+
+    /**
+     * 所属工厂ID
+     */
+    private Long factoryId;
+
+    /**
+     * 所属工厂名称
+     */
+    private String factoryName;
+
+    /**
+     * 联系人
+     */
+    private String contactName;
+
+    /**
+     * 联系方式
+     */
+    private String contactPhone;
+
+    /**
+     * 状态(0 禁用 1 启用)
+     */
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    private Date createdTime;
+
+    /**
+     * 修改时间
+     */
+    private Date updatedTime;
+
+    /**
+     * 操作人
+     */
+    private String operator;
+
+    // getter和setter方法
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getWorkshopCode() {
+        return workshopCode;
+    }
+
+    public void setWorkshopCode(String workshopCode) {
+        this.workshopCode = workshopCode;
+    }
+
+    public String getWorkshopName() {
+        return workshopName;
+    }
+
+    public void setWorkshopName(String workshopName) {
+        this.workshopName = workshopName;
+    }
+
+    public Long getFactoryId() {
+        return factoryId;
+    }
+
+    public void setFactoryId(Long factoryId) {
+        this.factoryId = factoryId;
+    }
+
+    public String getFactoryName() {
+        return factoryName;
+    }
+
+    public void setFactoryName(String factoryName) {
+        this.factoryName = factoryName;
+    }
+
+    public String getContactName() {
+        return contactName;
+    }
+
+    public void setContactName(String contactName) {
+        this.contactName = contactName;
+    }
+
+    public String getContactPhone() {
+        return contactPhone;
+    }
+
+    public void setContactPhone(String contactPhone) {
+        this.contactPhone = contactPhone;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public String getOperator() {
+        return operator;
+    }
+
+    public void setOperator(String operator) {
+        this.operator = operator;
+    }
+}

+ 74 - 0
src/main/java/com/qlm/netty/NettyServer.java

@@ -0,0 +1,74 @@
+package com.qlm.netty;
+
+import com.qlm.netty.codec.CustomDecoder;
+import com.qlm.netty.codec.CustomEncoder;
+import com.qlm.netty.handler.NettyServerHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.timeout.IdleStateHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+public class NettyServer {
+    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
+
+    private int port;
+    private EventLoopGroup bossGroup;
+    private EventLoopGroup workerGroup;
+
+    public NettyServer(int port) {
+        this.port = port;
+    }
+
+    public void start() throws InterruptedException {
+        // 创建两个线程组
+        bossGroup = new NioEventLoopGroup(1);
+        workerGroup = new NioEventLoopGroup();
+
+        try {
+            ServerBootstrap bootstrap = new ServerBootstrap();
+            bootstrap.group(bossGroup, workerGroup)
+                    .channel(NioServerSocketChannel.class)
+                    .option(ChannelOption.SO_BACKLOG, 128)
+                    .childOption(ChannelOption.SO_KEEPALIVE, true)
+                    .childHandler(new ChannelInitializer<SocketChannel>() {
+                        @Override
+                        protected void initChannel(SocketChannel ch) throws Exception {
+                            // 添加空闲状态处理器(60秒读取超时)
+                            ch.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
+                            // 添加自定义编解码器
+                            ch.pipeline().addLast(new CustomDecoder());
+                            ch.pipeline().addLast(new CustomEncoder());
+                            // 添加业务处理器
+                            ch.pipeline().addLast(new NettyServerHandler());
+                        }
+                    });
+
+            logger.info("Netty服务端启动成功,监听端口: {}", port);
+            ChannelFuture future = bootstrap.bind(port).sync();
+            future.channel().closeFuture().sync();
+        } finally {
+            workerGroup.shutdownGracefully();
+            bossGroup.shutdownGracefully();
+            logger.info("Netty服务端已关闭");
+        }
+    }
+
+    public void stop() {
+        if (workerGroup != null) {
+            workerGroup.shutdownGracefully();
+        }
+        if (bossGroup != null) {
+            bossGroup.shutdownGracefully();
+        }
+        logger.info("Netty服务端已停止");
+    }
+}

+ 52 - 0
src/main/java/com/qlm/netty/codec/CustomDecoder.java

@@ -0,0 +1,52 @@
+package com.qlm.netty.codec;
+
+import com.qlm.netty.protocol.CustomProtocol;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+
+public class CustomDecoder extends LengthFieldBasedFrameDecoder {
+
+    // 长度字段的偏移量
+    private static final int LENGTH_FIELD_OFFSET = 21;  // deviceId(20字节) + type(1字节)
+    // 长度字段的长度
+    private static final int LENGTH_FIELD_LENGTH = 4;
+
+    public CustomDecoder() {
+        // maxFrameLength: 最大帧长度
+        // lengthFieldOffset: 长度字段的偏移量
+        // lengthFieldLength: 长度字段的长度
+        // lengthAdjustment: 长度调整值
+        // initialBytesToStrip: 跳过的初始字节数
+        super(Integer.MAX_VALUE, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, 0, LENGTH_FIELD_OFFSET + LENGTH_FIELD_LENGTH);
+    }
+
+    @Override
+    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
+        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
+        if (frame == null) {
+            return null;
+        }
+
+        try {
+            // 读取设备ID (20字节)
+            byte[] deviceIdBytes = new byte[20];
+            frame.readBytes(deviceIdBytes);
+            String deviceId = new String(deviceIdBytes).trim();
+
+            // 读取消息类型
+            byte type = frame.readByte();
+
+            // 读取消息长度
+            int length = frame.readInt();
+
+            // 读取消息内容
+            byte[] content = new byte[length];
+            frame.readBytes(content);
+
+            return new CustomProtocol(deviceId, type, content);
+        } finally {
+            frame.release();
+        }
+    }
+}

+ 33 - 0
src/main/java/com/qlm/netty/codec/CustomEncoder.java

@@ -0,0 +1,33 @@
+package com.qlm.netty.codec;
+
+import com.qlm.netty.protocol.CustomProtocol;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+public class CustomEncoder extends MessageToByteEncoder<CustomProtocol> {
+
+    @Override
+    protected void encode(ChannelHandlerContext ctx, CustomProtocol msg, ByteBuf out) throws Exception {
+        if (msg == null) {
+            throw new NullPointerException("CustomProtocol is null");
+        }
+
+        // 写入设备ID (固定20字节)
+        byte[] deviceIdBytes = new byte[20];
+        byte[] srcDeviceId = msg.getDeviceId().getBytes();
+        System.arraycopy(srcDeviceId, 0, deviceIdBytes, 0, Math.min(srcDeviceId.length, deviceIdBytes.length));
+        out.writeBytes(deviceIdBytes);
+
+        // 写入消息类型
+        out.writeByte(msg.getType());
+
+        // 写入消息长度
+        out.writeInt(msg.getLength());
+
+        // 写入消息内容
+        if (msg.getLength() > 0) {
+            out.writeBytes(msg.getContent());
+        }
+    }
+}

+ 93 - 0
src/main/java/com/qlm/netty/handler/NettyServerHandler.java

@@ -0,0 +1,93 @@
+package com.qlm.netty.handler;
+
+import com.alibaba.fastjson.JSON;
+import com.qlm.netty.model.IndustrialControlData;
+import com.qlm.netty.protocol.CustomProtocol;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.timeout.IdleStateEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NettyServerHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
+
+    // 心跳消息类型
+    private static final byte HEARTBEAT_TYPE = 1;
+    // 数据消息类型
+    private static final byte DATA_TYPE = 2;
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        logger.info("客户端连接成功: {}", ctx.channel().remoteAddress());
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        CustomProtocol protocol = (CustomProtocol) msg;
+        String deviceId = protocol.getDeviceId();
+
+        switch (protocol.getType()) {
+            case HEARTBEAT_TYPE:
+                logger.info("收到来自设备 {} 的心跳消息", deviceId);
+                // 回复心跳
+                ctx.writeAndFlush(new CustomProtocol(deviceId, HEARTBEAT_TYPE, new byte[0]));
+                break;
+
+            case DATA_TYPE:
+                // 解析数据
+                String dataStr = new String(protocol.getContent());
+                IndustrialControlData data = JSON.parseObject(dataStr, IndustrialControlData.class);
+                data.setDeviceId(deviceId);
+
+                logger.info("收到来自设备 {} 的数据: {}", deviceId, data);
+
+                // 这里可以添加数据处理逻辑,例如存储到数据库
+                // processIndustrialData(data);
+
+                // 回复确认消息
+                ctx.writeAndFlush(new CustomProtocol(deviceId, DATA_TYPE, "SUCCESS".getBytes()));
+                break;
+
+            default:
+                logger.warn("收到未知类型的消息: {}", protocol.getType());
+                break;
+        }
+    }
+
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            switch (event.state()) {
+                case READER_IDLE:
+                    logger.info("客户端 {} 读取超时,关闭连接", ctx.channel().remoteAddress());
+                    ctx.close();
+                    break;
+                case WRITER_IDLE:
+                    // 写超时,可发送心跳
+                    break;
+                case ALL_IDLE:
+                    // 所有超时
+                    break;
+                default:
+            }
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        logger.error("发生异常: {}", cause.getMessage(), cause);
+        ctx.close();
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        logger.info("客户端断开连接: {}", ctx.channel().remoteAddress());
+    }
+
+    // 数据处理方法,可根据实际需求实现
+    private void processIndustrialData(IndustrialControlData data) {
+        // 这里实现数据存储或其他业务逻辑
+    }
+}

+ 46 - 0
src/main/java/com/qlm/netty/model/IndustrialControlData.java

@@ -0,0 +1,46 @@
+package com.qlm.netty.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class IndustrialControlData implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String deviceId;        // 设备ID
+    private double cpuUsage;        // CPU使用率 (%)
+    private double memoryUsage;     // 内存使用率 (%)
+    private double diskFreeSpace;   // 磁盘剩余空间 (GB)
+    private Date timestamp;         // 数据采集时间
+
+    public IndustrialControlData() {}
+
+    public IndustrialControlData(String deviceId, double cpuUsage, double memoryUsage, double diskFreeSpace) {
+        this.deviceId = deviceId;
+        this.cpuUsage = cpuUsage;
+        this.memoryUsage = memoryUsage;
+        this.diskFreeSpace = diskFreeSpace;
+        this.timestamp = new Date();
+    }
+
+    public String getDeviceId() { return deviceId; }
+    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
+    public double getCpuUsage() { return cpuUsage; }
+    public void setCpuUsage(double cpuUsage) { this.cpuUsage = cpuUsage; }
+    public double getMemoryUsage() { return memoryUsage; }
+    public void setMemoryUsage(double memoryUsage) { this.memoryUsage = memoryUsage; }
+    public double getDiskFreeSpace() { return diskFreeSpace; }
+    public void setDiskFreeSpace(double diskFreeSpace) { this.diskFreeSpace = diskFreeSpace; }
+    public Date getTimestamp() { return timestamp; }
+    public void setTimestamp(Date timestamp) { this.timestamp = timestamp; }
+
+    @Override
+    public String toString() {
+        return "IndustrialControlData{" +
+                "deviceId='" + deviceId + '\'' +
+                ", cpuUsage=" + cpuUsage +
+                ", memoryUsage=" + memoryUsage +
+                ", diskFreeSpace=" + diskFreeSpace +
+                ", timestamp=" + timestamp +
+                '}';
+    }
+}

+ 35 - 0
src/main/java/com/qlm/netty/protocol/CustomProtocol.java

@@ -0,0 +1,35 @@
+package com.qlm.netty.protocol;
+
+import java.io.Serializable;
+
+public class CustomProtocol implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    // 设备ID
+    private String deviceId;
+    // 消息类型 (1:心跳 2:数据)
+    private byte type;
+    // 消息长度
+    private int length;
+    // 消息内容
+    private byte[] content;
+
+    // 构造函数、getter和setter
+    public CustomProtocol() {}
+
+    public CustomProtocol(String deviceId, byte type, byte[] content) {
+        this.deviceId = deviceId;
+        this.type = type;
+        this.content = content;
+        this.length = content.length;
+    }
+
+    public String getDeviceId() { return deviceId; }
+    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
+    public byte getType() { return type; }
+    public void setType(byte type) { this.type = type; }
+    public int getLength() { return length; }
+    public void setLength(int length) { this.length = length; }
+    public byte[] getContent() { return content; }
+    public void setContent(byte[] content) { this.content = content; }
+}

+ 24 - 0
src/main/java/com/qlm/oss/OssConfig.java

@@ -0,0 +1,24 @@
+package com.qlm.oss;
+
+import com.jfinal.kit.PropKit;
+
+/**
+ * @program: yunpingtai
+ * @ClassName: OssConfig
+ * @description: TODO
+ * @author: 吴英健
+ * @create: 2025-07-09 10:49
+ * @Version 1.0
+ **/
+public class OssConfig {
+    public static final String ENDPOINT = PropKit.get("oss.endpoint");
+    public static final String ACCESS_KEY_ID = PropKit.get("oss.accessKeyId");
+    public static final String ACCESS_KEY_SECRET = PropKit.get("oss.accessKeySecret");
+    public static final String BUCKET_NAME = PropKit.get("oss.bucketName");
+    public static final String DOMAIN = PropKit.get("oss.domain");
+    public static final String MKDIR = PropKit.get("oss.mkdir");
+    public static final long MAX_SIZE = PropKit.getLong("oss.maxSize", 10485760L);
+    public static final String[] ALLOWED_TYPES = PropKit.get("oss.allowedTypes").split(",");
+
+    private OssConfig() {}
+}

+ 180 - 0
src/main/java/com/qlm/oss/OssUtil.java

@@ -0,0 +1,180 @@
+package com.qlm.oss;
+
+import cn.hutool.core.date.DateUtil;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.PutObjectRequest;
+import com.jfinal.kit.LogKit;
+import com.jfinal.upload.UploadFile;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * @program: yunpingtai
+ * @ClassName: OssUtil
+ * @description: TODO
+ * @author: 吴英健
+ * @create: 2025-07-09 10:50
+ * @Version 1.0
+ **/
+public class OssUtil {
+    /**
+     * 上传文件到OSS
+     * @param uploadFile JFinal上传的文件对象
+     * @param folder OSS上的文件夹路径,如 "images/"
+     * @return 上传后的完整URL
+     * @throws RuntimeException 上传失败时抛出异常
+     */
+    public static String upload(UploadFile uploadFile) {
+        // 验证文件
+        validateFile(uploadFile);
+
+        File file = uploadFile.getFile();
+        String originalFilename = uploadFile.getOriginalFileName();
+        String fileExt = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
+
+        // 生成新的文件名
+        String newFileName = UUID.randomUUID().toString().replace("-", "") + fileExt;
+
+        // OSS文件路径
+        String ossPath = OssConfig.MKDIR + DateUtil.format(new Date(), "yyyyMMdd") + "/" + newFileName;
+        // 创建OSS客户端
+        OSS ossClient = new OSSClientBuilder().build(
+                OssConfig.ENDPOINT,
+                OssConfig.ACCESS_KEY_ID,
+                OssConfig.ACCESS_KEY_SECRET
+        );
+
+        try {
+            // 上传文件
+            PutObjectRequest putObjectRequest = new PutObjectRequest(
+                    OssConfig.BUCKET_NAME,
+                    ossPath,
+                    file
+            );
+            ossClient.putObject(putObjectRequest);
+
+            // 返回完整URL
+            return OssConfig.DOMAIN + "/" + ossPath;
+        } catch (Exception e) {
+            LogKit.error("OSS文件上传失败", e);
+            throw new RuntimeException("文件上传失败: " + e.getMessage());
+        } finally {
+            // 关闭OSS客户端
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+            // 删除临时文件
+            if (file != null && file.exists()) {
+                file.delete();
+            }
+        }
+    }
+
+    /**
+     * 通过InputStream上传文件到OSS
+     * @param inputStream 文件输入流
+     * @param filename 文件名
+     * @return 上传后的完整URL
+     * @throws RuntimeException 上传失败时抛出异常
+     */
+    public static String upload(InputStream inputStream, String filename) {
+        // 验证文件名
+        if (filename == null || filename.isEmpty()) {
+            throw new RuntimeException("文件名不能为空");
+        }
+
+        String fileExt = filename.substring(filename.lastIndexOf(".")).toLowerCase();
+
+        // 验证文件类型
+        boolean allowed = false;
+        for (String ext : OssConfig.ALLOWED_TYPES) {
+            if (ext.trim().equalsIgnoreCase(fileExt)) {
+                allowed = true;
+                break;
+            }
+        }
+        if (!allowed) {
+            throw new RuntimeException("不支持的文件类型,仅支持: " + String.join(", ", OssConfig.ALLOWED_TYPES));
+        }
+
+        // 生成新的文件名
+        String newFileName = UUID.randomUUID().toString().replace("-", "") + fileExt;
+
+        // OSS文件路径
+        String ossPath = OssConfig.MKDIR + DateUtil.format(new Date(), "yyyyMMdd") + "/" + newFileName;
+
+        // 创建OSS客户端
+        OSS ossClient = new OSSClientBuilder().build(
+                OssConfig.ENDPOINT,
+                OssConfig.ACCESS_KEY_ID,
+                OssConfig.ACCESS_KEY_SECRET
+        );
+
+        try {
+            // 通过InputStream上传文件
+            PutObjectRequest putObjectRequest = new PutObjectRequest(
+                    OssConfig.BUCKET_NAME,
+                    ossPath,
+                    inputStream
+            );
+            ossClient.putObject(putObjectRequest);
+
+            // 返回完整URL
+            return OssConfig.DOMAIN + "/" + ossPath;
+        } catch (Exception e) {
+            LogKit.error("OSS文件上传失败", e);
+            throw new RuntimeException("文件上传失败: " + e.getMessage());
+        } finally {
+            // 关闭OSS客户端
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+
+            // 关闭输入流
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (Exception e) {
+                    LogKit.error("关闭输入流失败", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * 验证上传文件
+     */
+    private static void validateFile(UploadFile uploadFile) {
+        if (uploadFile == null) {
+            throw new RuntimeException("请选择要上传的文件");
+        }
+
+        String originalFilename = uploadFile.getOriginalFileName();
+        if (originalFilename == null || originalFilename.isEmpty()) {
+            throw new RuntimeException("文件名不能为空");
+        }
+
+        // 检查文件扩展名
+        String fileExt = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
+        boolean allowed = false;
+        for (String ext : OssConfig.ALLOWED_TYPES) {
+            if (ext.trim().equalsIgnoreCase(fileExt)) {
+                allowed = true;
+                break;
+            }
+        }
+        if (!allowed) {
+            throw new RuntimeException("不支持的文件类型,仅支持: " + String.join(", ", OssConfig.ALLOWED_TYPES));
+        }
+
+        // 检查文件大小
+        File file = uploadFile.getFile();
+        if (file.length() > OssConfig.MAX_SIZE) {
+            throw new RuntimeException("文件大小不能超过 " + (OssConfig.MAX_SIZE / 1024 / 1024) + "MB");
+        }
+    }
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 631 - 631
src/main/java/com/qlm/poi/ExcelUtil.java


+ 69 - 69
src/main/java/com/qlm/poi/PoiExcelHelperImpl07.java

@@ -1,69 +1,69 @@
-package com.qlm.poi;
-import java.io.FileInputStream;  
-import java.util.ArrayList;  
-import java.util.Iterator;  
-  
-
-
-import org.apache.poi.xssf.usermodel.XSSFSheet;  
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;  
-  
-/** 
- * Excel 读取(2007+新格式) 
- * @author  chengesheng 
- * @date    2012-4-27 下午03:39:01 
- * @note    PoiExcel2k7Helper 
- */  
-public class PoiExcelHelperImpl07 extends PoiExcelHelper {  
-    /** 获取sheet列表 */  
-    public ArrayList<String> getSheetList(String filePath) {  
-        ArrayList<String> sheetList = new ArrayList<String>(0);  
-        try {  
-            XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(filePath));  
-            Iterator<XSSFSheet> iterator = wb.iterator();  
-            while (iterator.hasNext()) {  
-                sheetList.add(iterator.next().getSheetName());  
-            }  
-        } catch (Exception e) {  
-            e.printStackTrace();  
-        }  
-        return sheetList;  
-    }  
-  
-    /** 读取Excel文件内容 */  
-    public ArrayList<ArrayList<String>> readExcel(String filePath, int sheetIndex, String rows, String columns) {  
-        ArrayList<ArrayList<String>> dataList = new ArrayList<ArrayList<String>> ();  
-        try {  
-            XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(filePath));  
-//            XSSFSheet sheet = wb.getSheetAt(sheetIndex);  
-            XSSFSheet sheet = null;
-            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
-				sheet = wb.getSheetAt(i);
-				dataList = readExcel(sheet, rows, getColumnNumber(sheet, columns),dataList);  
-			}
-        } catch (Exception e) {  
-            e.printStackTrace();  
-        }  
-        return dataList;  
-    }  
-      
-    /** 读取Excel文件内容 */  
-    public ArrayList<ArrayList<String>> readExcel(String filePath, int sheetIndex, String rows, int[] cols) {  
-        ArrayList<ArrayList<String>> dataList = new ArrayList<ArrayList<String>> ();  
-        try {  
-            XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(filePath));  
-//            XSSFSheet sheet = wb.getSheetAt(sheetIndex);  
-            XSSFSheet sheet = null;
-            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
-				sheet = wb.getSheetAt(i);
-				dataList = readExcel(sheet, rows, cols,dataList);  
-			}
-//            dataList = readExcel(sheet, rows, cols,dataList);
-        } catch (Exception e) {  
-            e.printStackTrace();  
-        }  
-        return dataList;  
-    } 
-    
-   
-}  
+//package com.qlm.poi;
+//import java.io.FileInputStream;
+//import java.util.ArrayList;
+//import java.util.Iterator;
+//
+//
+//
+//import org.apache.poi.xssf.usermodel.XSSFSheet;
+//import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+//
+///**
+// * Excel 读取(2007+新格式)
+// * @author  chengesheng
+// * @date    2012-4-27 下午03:39:01
+// * @note    PoiExcel2k7Helper
+// */
+//public class PoiExcelHelperImpl07 extends PoiExcelHelper {
+//    /** 获取sheet列表 */
+//    public ArrayList<String> getSheetList(String filePath) {
+//        ArrayList<String> sheetList = new ArrayList<String>(0);
+//        try {
+//            XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(filePath));
+//            Iterator<XSSFSheet> iterator = wb.iterator();
+//            while (iterator.hasNext()) {
+//                sheetList.add(iterator.next().getSheetName());
+//            }
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//        return sheetList;
+//    }
+//
+//    /** 读取Excel文件内容 */
+//    public ArrayList<ArrayList<String>> readExcel(String filePath, int sheetIndex, String rows, String columns) {
+//        ArrayList<ArrayList<String>> dataList = new ArrayList<ArrayList<String>> ();
+//        try {
+//            XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(filePath));
+////            XSSFSheet sheet = wb.getSheetAt(sheetIndex);
+//            XSSFSheet sheet = null;
+//            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+//				sheet = wb.getSheetAt(i);
+//				dataList = readExcel(sheet, rows, getColumnNumber(sheet, columns),dataList);
+//			}
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//        return dataList;
+//    }
+//
+//    /** 读取Excel文件内容 */
+//    public ArrayList<ArrayList<String>> readExcel(String filePath, int sheetIndex, String rows, int[] cols) {
+//        ArrayList<ArrayList<String>> dataList = new ArrayList<ArrayList<String>> ();
+//        try {
+//            XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(filePath));
+////            XSSFSheet sheet = wb.getSheetAt(sheetIndex);
+//            XSSFSheet sheet = null;
+//            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+//				sheet = wb.getSheetAt(i);
+//				dataList = readExcel(sheet, rows, cols,dataList);
+//			}
+////            dataList = readExcel(sheet, rows, cols,dataList);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//        return dataList;
+//    }
+//
+//
+//}

+ 32 - 0
src/main/java/com/qlm/service/IBrandService.java

@@ -0,0 +1,32 @@
+package com.qlm.service;
+
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.BrandDto;
+
+public interface IBrandService {
+    /**
+     * 保存品牌信息
+     */
+    ApiResponse saveBrand(BrandDto brandDto);
+    
+    /**
+     * 更新品牌信息
+     */
+    ApiResponse updateBrand(BrandDto brandDto);
+    
+    /**
+     * 删除品牌信息
+     */
+    ApiResponse deleteBrand(Long id);
+    
+    /**
+     * 根据ID获取品牌信息
+     */
+    ApiResponse getBrandById(Long id);
+    
+    /**
+     * 品牌列表查询
+     */
+    PageResult<BrandDto> listBrand(int pageNumber, int pageSize, String keyword);
+}

+ 80 - 0
src/main/java/com/qlm/service/ILineProductService.java

@@ -0,0 +1,80 @@
+package com.qlm.service;
+
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.LineProductDto;
+import com.qlm.dto.LineProductImportDto;
+
+import java.util.List;
+
+public interface ILineProductService {
+    /**
+     * 保存产线产品关联信息
+     */
+    ApiResponse saveLineProduct(LineProductDto lineProductDto);
+
+    /**
+     * 删除产线产品关联信息
+     */
+    ApiResponse deleteLineProduct(Integer id);
+
+    /**
+     * 根据产线ID和产品ID获取关联信息
+     */
+    ApiResponse getLineProductById(Integer id);
+
+    /**
+     * 分页查询产线产品关联列表
+     */
+    PageResult<LineProductDto> listLineProduct(int pageNumber, int pageSize, Long factoryId, Long workshopId, Long lineId, Long productId);
+
+    /**
+     * 获取工厂列表(用于下拉选择)
+     */
+    ApiResponse getAllFactories(String keyword);
+
+    /**
+     * 根据工厂ID获取车间列表(用于下拉选择)
+     */
+    ApiResponse getWorkshopsByFactoryId(Long factoryId);
+
+    /**
+     * 根据车间ID获取产线列表(用于下拉选择)
+     */
+    ApiResponse getLinesByWorkshopId(Long workshopId);
+
+    /**
+     * 获取产品列表(用于下拉选择)
+     */
+    ApiResponse getAllProducts(String keyword);
+
+    /**
+     * 导入产线产品关联信息
+     */
+    ApiResponse importLineProduct(List<LineProductImportDto> importList, String operator);
+
+    /**
+     * 修改产线产品关联信息
+     */
+    ApiResponse updateLineProduct(LineProductDto lineProductDto);
+
+    /**
+     * 根据工厂编号获取工厂ID
+     */
+    Long getFactoryIdByCode(String factoryCode);
+
+    /**
+     * 根据车间编号获取车间ID
+     */
+    Long getWorkshopIdByCode(String workshopCode);
+
+    /**
+     * 根据产线编号获取产线ID
+     */
+    Long getLineIdByCode(String lineCode);
+
+    /**
+     * 根据产品编号获取产品ID
+     */
+    Long getProductIdByCode(String productCode);
+}

+ 47 - 0
src/main/java/com/qlm/service/IProductionLineService.java

@@ -0,0 +1,47 @@
+package com.qlm.service;
+
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.ProductionLineDto;
+
+public interface IProductionLineService {
+    /**
+     * 保存产线信息
+     */
+    ApiResponse saveProductionLine(ProductionLineDto productionLineDto);
+
+    /**
+     * 更新产线信息
+     */
+    ApiResponse updateProductionLine(ProductionLineDto productionLineDto);
+
+    /**
+     * 删除产线信息
+     */
+    ApiResponse deleteProductionLine(Long id);
+
+    /**
+     * 根据ID获取产线信息
+     */
+    ApiResponse getProductionLineById(Long id);
+
+    /**
+     * 切换产线状态
+     */
+    ApiResponse toggleProductionLineStatus(Long id, Integer status);
+
+    /**
+     * 分页查询产线列表
+     */
+    PageResult<ProductionLineDto> listProductionLine(int pageNumber, int pageSize, String lineName, String lineCode, Long factoryId, Long workshopId);
+
+    /**
+     * 获取所有工厂列表(用于下拉选择)
+     */
+    ApiResponse getAllFactories(String keyword);
+
+    /**
+     * 根据工厂ID获取车间列表(用于下拉选择)
+     */
+    ApiResponse getWorkshopsByFactoryId(Long factoryId, String keyword);
+}

+ 42 - 0
src/main/java/com/qlm/service/IWorkshopService.java

@@ -0,0 +1,42 @@
+package com.qlm.service;
+
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.WorkshopDto;
+
+public interface IWorkshopService {
+    /**
+     * 保存车间信息
+     */
+    ApiResponse saveWorkshop(WorkshopDto workshopDto);
+
+    /**
+     * 更新车间信息
+     */
+    ApiResponse updateWorkshop(WorkshopDto workshopDto);
+
+    /**
+     * 删除车间信息
+     */
+    ApiResponse deleteWorkshop(Long id);
+
+    /**
+     * 根据ID获取车间信息
+     */
+    ApiResponse getWorkshopById(Long id);
+
+    /**
+     * 切换车间状态
+     */
+    ApiResponse toggleWorkshopStatus(Long id, Integer status);
+
+    /**
+     * 分页查询车间列表
+     */
+    PageResult<WorkshopDto> listWorkshop(int pageNumber, int pageSize, String workshopName, Long factoryId);
+
+    /**
+     * 获取所有工厂列表(用于下拉选择)
+     */
+    ApiResponse getAllFactories(String keyword);
+}

+ 165 - 0
src/main/java/com/qlm/service/impl/BrandServiceImpl.java

@@ -0,0 +1,165 @@
+package com.qlm.service.impl;
+
+import com.jfinal.kit.StrKit;
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Record;
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.BrandDto;
+import com.qlm.service.IBrandService;
+import com.qlm.tools.WxUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BrandServiceImpl implements IBrandService {
+    private static final Logger logger = LoggerFactory.getLogger(BrandServiceImpl.class);
+
+    @Override
+    public ApiResponse saveBrand(BrandDto brandDto) {
+        try {
+            // 验证品牌编号是否已存在
+            Record brand = Db.findFirst("select * from t_brand where brand_code = ?", brandDto.getBrandCode());
+            if (brand != null) {
+                return ApiResponse.error("品牌编号已存在");
+            }
+
+            Record record = new Record();
+            record.set("brand_code", brandDto.getBrandCode());
+            record.set("brand_name", brandDto.getBrandName());
+            record.set("contact_phone", brandDto.getContactPhone());
+            record.set("status", brandDto.getStatus() != null ? brandDto.getStatus() : 1);
+            record.set("operator", brandDto.getOperator());
+
+            boolean save = Db.save("t_brand", record);
+            if (save) {
+                return ApiResponse.success("保存成功");
+            } else {
+                return ApiResponse.error("保存失败");
+            }
+        } catch (Exception e) {
+            logger.error("保存品牌信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse updateBrand(BrandDto brandDto) {
+        try {
+            // 验证品牌编号是否已存在(排除当前记录)
+            Record brand = Db.findFirst("select * from t_brand where brand_code = ? and id != ?", 
+                    brandDto.getBrandCode(), brandDto.getId());
+            if (brand != null) {
+                return ApiResponse.error("品牌编号已存在");
+            }
+
+            Record record = new Record();
+            record.set("id", brandDto.getId());
+            record.set("brand_code", brandDto.getBrandCode());
+            record.set("brand_name", brandDto.getBrandName());
+            record.set("contact_phone", brandDto.getContactPhone());
+            record.set("status", brandDto.getStatus());
+            record.set("operator", brandDto.getOperator());
+
+            boolean update = Db.update("t_brand", record);
+            if (update) {
+                return ApiResponse.success("更新成功");
+            } else {
+                return ApiResponse.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("更新品牌信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse deleteBrand(Long id) {
+        try {
+            boolean b = Db.deleteById("t_brand", id);
+            return b ? ApiResponse.success("删除成功") : ApiResponse.error("删除失败");
+        } catch (Exception e) {
+            logger.error("删除品牌信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getBrandById(Long id) {
+        try {
+            Record record = Db.findById("t_brand", id);
+            if (record == null) {
+                return ApiResponse.error("未找到该品牌信息");
+            }
+
+            BrandDto brandDto = convertRecordToDto(record);
+            return ApiResponse.success(brandDto);
+        } catch (Exception e) {
+            logger.error("获取品牌信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public PageResult<BrandDto> listBrand(int pageNumber, int pageSize, String keyword) {
+        try {
+            // 构建查询条件
+            StringBuilder sqlWhere = new StringBuilder(" from t_brand where 1=1 ");
+            List<Object> params = new ArrayList<>();
+
+            if (StrKit.notBlank(keyword)) {
+                sqlWhere.append(" and (brand_name like ? or brand_code like ?)");
+                params.add("%" + keyword + "%");
+                params.add("%" + keyword + "%");
+            }
+            sqlWhere.append(" order by updated_time desc ");
+
+            // 执行分页查询
+            Page<Record> recordPage = Db.paginate(pageNumber, pageSize,
+                    "select * ",
+                    sqlWhere.toString(),
+                    params.toArray());
+
+            if (recordPage == null) {
+                return new PageResult<>(0, pageNumber, pageSize, new ArrayList<>());
+            }
+
+            // 转换为Dto对象
+            List<BrandDto> brandDtoList = new ArrayList<>();
+            for (Record record : recordPage.getList()) {
+                brandDtoList.add(convertRecordToDto(record));
+            }
+
+            // 创建新的Page对象,包含Dto列表
+            return new PageResult<>(recordPage.getTotalRow(),
+                    recordPage.getPageNumber(),
+                    recordPage.getPageSize(),
+                    brandDtoList);
+
+        } catch (Exception e) {
+            logger.error("查询品牌列表异常:", e);
+            return new PageResult<>(0, pageNumber, pageSize, new ArrayList<>());
+        }
+    }
+
+    /**
+     * 将Record转换为BrandDto的工具方法
+     * @param record
+     * @return
+     */
+    private BrandDto convertRecordToDto(Record record) {
+        BrandDto dto = new BrandDto();
+        dto.setId(WxUtil.getInt("id",record).longValue());
+        dto.setBrandCode(record.getStr("brand_code"));
+        dto.setBrandName(record.getStr("brand_name"));
+        dto.setContactPhone(record.getStr("contact_phone"));
+        dto.setStatus(record.getInt("status"));
+        dto.setCreatedTime(record.getDate("created_time"));
+        dto.setUpdatedTime(record.getDate("updated_time"));
+        dto.setOperator(record.getStr("operator"));
+        return dto;
+    }
+}

+ 528 - 0
src/main/java/com/qlm/service/impl/LineProductServiceImpl.java

@@ -0,0 +1,528 @@
+package com.qlm.service.impl;
+
+import com.jfinal.kit.StrKit;
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Record;
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.LineProductDto;
+import com.qlm.dto.LineProductImportDto;
+import com.qlm.service.ILineProductService;
+import com.qlm.tools.EasyExcelUtil;
+import com.qlm.tools.WxUtil;
+import com.qlm.oss.OssUtil;
+import com.jfinal.upload.UploadFile;
+import com.alibaba.excel.EasyExcel;
+import java.io.File;
+import java.io.FileOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 产线产品关联服务实现类
+ */
+public class LineProductServiceImpl implements ILineProductService {
+
+    private static final Logger logger = LoggerFactory.getLogger(LineProductServiceImpl.class);
+
+    @Override
+    public ApiResponse saveLineProduct(LineProductDto lineProductDto) {
+        try {
+            // 数据验证
+            if (lineProductDto.getFactoryId() == null) {
+                return ApiResponse.error("工厂ID不能为空");
+            }
+
+            if (lineProductDto.getWorkshopId() == null) {
+                return ApiResponse.error("车间ID不能为空");
+            }
+
+            if (lineProductDto.getLineId() == null) {
+                return ApiResponse.error("产线ID不能为空");
+            }
+
+            if (lineProductDto.getProductId() == null) {
+                return ApiResponse.error("产品ID不能为空");
+            }
+
+            // 检查关联是否已存在
+            Record existRecord = Db.findFirst("select 1 from t_line_product where line_id = ? and product_id = ?",
+                    lineProductDto.getLineId(), lineProductDto.getProductId());
+            if (existRecord != null) {
+                return ApiResponse.error("该产线与产品的关联已存在");
+            }
+
+            // 获取工厂名称
+            Record factory = Db.findById("t_factory", lineProductDto.getFactoryId());
+            if (factory == null) {
+                return ApiResponse.error("未找到指定的工厂信息");
+            }
+
+            // 获取车间名称
+            Record workshop = Db.findById("t_workshop", lineProductDto.getWorkshopId());
+            if (workshop == null) {
+                return ApiResponse.error("未找到指定的车间信息");
+            }
+
+            // 获取产线名称
+            Record line = Db.findById("t_production_line", lineProductDto.getLineId());
+            if (line == null) {
+                return ApiResponse.error("未找到指定的产线信息");
+            }
+
+            // 获取产品名称
+            Record product = Db.findById("t_jz_product", lineProductDto.getProductId());
+            if (product == null) {
+                return ApiResponse.error("未找到指定的产品信息");
+            }
+
+            // 保存数据
+            Record record = new Record();
+            record.set("factory_id", lineProductDto.getFactoryId());
+            record.set("factory_name", factory.getStr("factory_name"));
+            record.set("workshop_id", lineProductDto.getWorkshopId());
+            record.set("workshop_name", workshop.getStr("workshop_name"));
+            record.set("line_id", lineProductDto.getLineId());
+            record.set("line_name", line.getStr("line_name"));
+            record.set("product_id", lineProductDto.getProductId());
+            record.set("product_name", product.getStr("product_name"));
+            record.set("operator", lineProductDto.getOperator());
+
+            boolean result = Db.save("t_line_product", record);
+            if (result) {
+                return ApiResponse.success("保存成功");
+            } else {
+                return ApiResponse.error("保存失败");
+            }
+        } catch (Exception e) {
+            logger.error("保存产线产品关联信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse deleteLineProduct(Integer id) {
+        try {
+            // 删除数据
+            int result = Db.update("delete from t_line_product where id = ?", id);
+            if (result > 0) {
+                return ApiResponse.success("删除成功");
+            } else {
+                return ApiResponse.error("删除失败或未找到该关联记录");
+            }
+        } catch (Exception e) {
+            logger.error("删除产线产品关联信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getLineProductById(Integer id) {
+        try {
+            String sql = "select * from t_line_product where id = ?";
+            Record record = Db.findFirst(sql, id);
+            if (record == null) {
+                return ApiResponse.error("未找到该产线产品关联信息");
+            }
+
+            LineProductDto lineProductDto = convertRecordToDto(record);
+            return ApiResponse.success(lineProductDto);
+        } catch (Exception e) {
+            logger.error("获取产线产品关联信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public PageResult<LineProductDto> listLineProduct(int pageNumber, int pageSize, Long factoryId, Long workshopId, Long lineId, Long productId) {
+        try {
+            // 构建查询条件
+            StringBuilder sqlWhere = new StringBuilder(" from t_line_product where 1=1 ");
+            List<Object> params = new ArrayList<>();
+
+            if (factoryId != null) {
+                sqlWhere.append(" and factory_id = ? ");
+                params.add(factoryId);
+            }
+
+            if (workshopId != null) {
+                sqlWhere.append(" and workshop_id = ? ");
+                params.add(workshopId);
+            }
+
+            if (lineId != null) {
+                sqlWhere.append(" and line_id = ? ");
+                params.add(lineId);
+            }
+
+            if (productId != null) {
+                sqlWhere.append(" and product_id = ? ");
+                params.add(productId);
+            }
+
+            sqlWhere.append(" order by created_time desc ");
+
+            // 执行分页查询
+            Page<Record> recordPage = Db.paginate(pageNumber, pageSize,
+                    "select * ",
+                    sqlWhere.toString(),
+                    params.toArray());
+
+            if (recordPage == null) {
+                return new PageResult<>(0, pageNumber, pageSize, new ArrayList<>());
+            }
+
+            // 转换为Dto对象
+            List<LineProductDto> lineProductDtoList = new ArrayList<>();
+            for (Record record : recordPage.getList()) {
+                lineProductDtoList.add(convertRecordToDto(record));
+            }
+
+            // 创建新的Page对象,包含Dto列表
+            return new PageResult<>(recordPage.getTotalRow(),
+                    recordPage.getPageNumber(),
+                    recordPage.getPageSize(),
+                    lineProductDtoList);
+
+        } catch (Exception e) {
+            logger.error("查询产线产品关联列表异常:", e);
+            return new PageResult<>(0, pageNumber, pageSize, new ArrayList<>());
+        }
+    }
+
+    @Override
+    public ApiResponse getAllFactories(String keyword) {
+        try {
+            StringBuilder sql = new StringBuilder("select id, factory_name from t_factory where status = 1 ");
+            List<Object> params = new ArrayList<>();
+
+            if (StrKit.notBlank(keyword)) {
+                sql.append(" and factory_name like ? ");
+                params.add("%" + keyword + "%");
+            }
+
+            sql.append(" order by factory_name asc ");
+            List<Record> records = Db.find(sql.toString(), params.toArray());
+            return ApiResponse.success(records);
+        } catch (Exception e) {
+            logger.error("获取工厂列表异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getWorkshopsByFactoryId(Long factoryId) {
+        try {
+            StringBuilder sql = new StringBuilder("select id, workshop_name from t_workshop where status = 1");
+            List<Object> params = new ArrayList<>();
+            if (factoryId != null) {
+                sql.append(" and factory_id = ? ");
+                params.add(factoryId);
+            }
+            List<Record> records = Db.find(sql.toString(), params.toArray());
+            return ApiResponse.success(records);
+        } catch (Exception e) {
+            logger.error("根据工厂ID获取车间列表异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getLinesByWorkshopId(Long workshopId) {
+        try {
+            StringBuilder sql = new StringBuilder("select id, line_name as lineName from t_production_line where status = 1");
+            List<Object>params = new ArrayList<>();
+            if(workshopId != null){
+                sql.append(" and workshop_id = ? ");
+                params.add(workshopId);
+            }
+            List<Record> records = Db.find(sql.toString(), params.toArray());
+            return ApiResponse.success(records);
+        } catch (Exception e) {
+            logger.error("根据车间ID获取产线列表异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getAllProducts(String keyword) {
+        try {
+            StringBuilder sql = new StringBuilder("select id, product_name from t_jz_product where status = 1 ");
+            List<Object> params = new ArrayList<>();
+
+            if (StrKit.notBlank(keyword)) {
+                sql.append(" and product_name like ? ");
+                params.add("%" + keyword + "%");
+            }
+
+            sql.append(" order by product_name asc ");
+            List<Record> records = Db.find(sql.toString(), params.toArray());
+            return ApiResponse.success(records);
+        } catch (Exception e) {
+            logger.error("获取产品列表异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 将Record转换为LineProductDto的工具方法
+     * @param record
+     * @return
+     */
+    private LineProductDto convertRecordToDto(Record record) {
+        LineProductDto dto = new LineProductDto();
+        dto.setId(WxUtil.getInt("id",record).longValue());
+        dto.setFactoryId(WxUtil.getInt("factory_id", record).longValue());
+        dto.setFactoryName(record.getStr("factory_name"));
+        dto.setWorkshopId(WxUtil.getInt("workshop_id", record).longValue());
+        dto.setWorkshopName(record.getStr("workshop_name"));
+        dto.setLineId(WxUtil.getInt("line_id", record).longValue());
+        dto.setLineName(record.getStr("line_name"));
+        dto.setProductId(WxUtil.getInt("product_id", record).longValue());
+        dto.setProductName(record.getStr("product_name"));
+        dto.setCreatedTime(record.getDate("created_time"));
+        dto.setUpdatedTime(record.getDate("updated_time"));
+        dto.setOperator(record.getStr("operator"));
+        return dto;
+    }
+
+    @Override
+    public ApiResponse updateLineProduct(LineProductDto lineProductDto) {
+        try {
+            // 数据验证
+            if (lineProductDto.getId() == null) {
+                return ApiResponse.error("ID不能为空");
+            }
+
+            if (lineProductDto.getFactoryId() == null) {
+                return ApiResponse.error("工厂ID不能为空");
+            }
+
+            if (lineProductDto.getWorkshopId() == null) {
+                return ApiResponse.error("车间ID不能为空");
+            }
+
+            if (lineProductDto.getLineId() == null) {
+                return ApiResponse.error("产线ID不能为空");
+            }
+
+            if (lineProductDto.getProductId() == null) {
+                return ApiResponse.error("产品ID不能为空");
+            }
+
+            // 检查记录是否存在
+            Record existRecord = Db.findById("t_line_product", lineProductDto.getId());
+            if (existRecord == null) {
+                return ApiResponse.error("未找到该产线产品关联记录");
+            }
+
+            // 检查关联是否已存在(排除当前记录)
+            Record duplicateRecord = Db.findFirst(
+                "select 1 from t_line_product where line_id = ? and product_id = ? and id != ?",
+                lineProductDto.getLineId(), lineProductDto.getProductId(), lineProductDto.getId());
+            if (duplicateRecord != null) {
+                return ApiResponse.error("该产线与产品的关联已存在");
+            }
+
+            // 获取工厂名称
+            Record factory = Db.findById("t_factory", lineProductDto.getFactoryId());
+            if (factory == null) {
+                return ApiResponse.error("未找到指定的工厂信息");
+            }
+
+            // 获取车间名称
+            Record workshop = Db.findById("t_workshop", lineProductDto.getWorkshopId());
+            if (workshop == null) {
+                return ApiResponse.error("未找到指定的车间信息");
+            }
+
+            // 获取产线名称
+            Record line = Db.findById("t_production_line", lineProductDto.getLineId());
+            if (line == null) {
+                return ApiResponse.error("未找到指定的产线信息");
+            }
+
+            // 获取产品名称
+            Record product = Db.findById("t_jz_product", lineProductDto.getProductId());
+            if (product == null) {
+                return ApiResponse.error("未找到指定的产品信息");
+            }
+
+            // 更新数据
+            Record record = new Record();
+            record.set("id", lineProductDto.getId());
+            record.set("factory_id", lineProductDto.getFactoryId());
+            record.set("factory_name", factory.getStr("factory_name"));
+            record.set("workshop_id", lineProductDto.getWorkshopId());
+            record.set("workshop_name", workshop.getStr("workshop_name"));
+            record.set("line_id", lineProductDto.getLineId());
+            record.set("line_name", line.getStr("line_name"));
+            record.set("product_id", lineProductDto.getProductId());
+            record.set("product_name", product.getStr("product_name"));
+            record.set("operator", lineProductDto.getOperator());
+            record.set("updated_time", new java.util.Date());
+
+            boolean result = Db.update("t_line_product", record);
+            if (result) {
+                return ApiResponse.success("更新成功");
+            } else {
+                return ApiResponse.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("修改产线产品关联信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse importLineProduct(List<LineProductImportDto> importList, String operator) {
+        List<LineProductImportDto> errorList = new ArrayList<>();
+        int successCount = 0;
+        ApiResponse response = ApiResponse.success();
+        Record record = new Record();
+        record.set("successCount", successCount);
+        record.set("errorCount", 0);
+        for (LineProductImportDto importDto : importList) {
+            try {
+                // 验证必填字段
+                if (StrKit.isBlank(importDto.getFactoryCode())) {
+                    importDto.setErrorMsg("工厂编号不能为空");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                if (StrKit.isBlank(importDto.getWorkshopCode())) {
+                    importDto.setErrorMsg("车间编号不能为空");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                if (StrKit.isBlank(importDto.getLineCode())) {
+                    importDto.setErrorMsg("产线编号不能为空");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                if (StrKit.isBlank(importDto.getProductCode())) {
+                    importDto.setErrorMsg("产品编号不能为空");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                // 获取各ID
+                Long factoryId = getFactoryIdByCode(importDto.getFactoryCode());
+                if (factoryId == null) {
+                    importDto.setErrorMsg("未找到工厂编号对应的工厂");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                Long workshopId = getWorkshopIdByCode(importDto.getWorkshopCode());
+                if (workshopId == null) {
+                    importDto.setErrorMsg("未找到车间编号对应的车间");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                Long lineId = getLineIdByCode(importDto.getLineCode());
+                if (lineId == null) {
+                    importDto.setErrorMsg("未找到产线编号对应的产线");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                Long productId = getProductIdByCode(importDto.getProductCode());
+                if (productId == null) {
+                    importDto.setErrorMsg("未找到产品编号对应的产品");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                // 检查关联是否已存在
+                Record existRecord = Db.findFirst("select 1 from t_line_product where line_id = ? and product_id = ?",
+                        lineId, productId);
+                if (existRecord != null) {
+                    importDto.setErrorMsg("该产线与产品的关联已存在");
+                    errorList.add(importDto);
+                    continue;
+                }
+
+                // 获取名称信息
+                Record factory = Db.findById("t_factory", factoryId);
+                Record workshop = Db.findById("t_workshop", workshopId);
+                Record line = Db.findById("t_production_line", lineId);
+                Record product = Db.findById("t_jz_product", productId);
+
+                // 保存数据
+                Record addrRecord = new Record();
+                addrRecord.set("factory_id", factoryId);
+                addrRecord.set("factory_name", factory.getStr("factory_name"));
+                addrRecord.set("workshop_id", workshopId);
+                addrRecord.set("workshop_name", workshop.getStr("workshop_name"));
+                addrRecord.set("line_id", lineId);
+                addrRecord.set("line_name", line.getStr("line_name"));
+                addrRecord.set("product_id", productId);
+                addrRecord.set("product_name", product.getStr("product_name"));
+                addrRecord.set("operator", operator);
+
+                boolean result = Db.save("t_line_product", addrRecord);
+                if (result) {
+                    successCount++;
+                } else {
+                    importDto.setErrorMsg("保存失败");
+                    errorList.add(importDto);
+                }
+            } catch (Exception e) {
+                logger.error("导入产线产品关联信息异常:", e);
+                importDto.setErrorMsg("系统异常:" + e.getMessage());
+                errorList.add(importDto);
+            }
+        }
+        record.set("successCount", successCount);
+        if (!errorList.isEmpty()) {
+            record.set("errorCount", errorList.size());
+            try {
+                String errorFileName = "line_product_import_error_" + System.currentTimeMillis() + ".xlsx";
+                InputStream errorInputStream = EasyExcelUtil.export(errorList, "导入错误数据", LineProductImportDto.class);
+                // 上传到OSS
+                String ossUrl = OssUtil.upload(errorInputStream, errorFileName);
+                // 添加到响应
+                record.set("errorFileUrl", ossUrl);
+            } catch (Exception e) {
+                logger.error("导出错误数据并上传到OSS异常:", e);
+                record.set("errorMsg", "导出错误数据并上传到OSS异常:" + e.getMessage());
+            }
+        }
+        response.setData(record);
+        return response;
+    }
+
+    @Override
+    public Long getFactoryIdByCode(String factoryCode) {
+        Record record = Db.findFirst("select id from t_factory where factory_code = ?", factoryCode);
+        return record != null ? WxUtil.getInt("id", record).longValue() : null;
+    }
+
+    @Override
+    public Long getWorkshopIdByCode(String workshopCode) {
+        Record record = Db.findFirst("select id from t_workshop where workshop_code = ?", workshopCode);
+        return record != null ? WxUtil.getInt("id", record).longValue() : null;
+    }
+
+    @Override
+    public Long getLineIdByCode(String lineCode) {
+        Record record = Db.findFirst("select id from t_production_line where line_code = ?", lineCode);
+        return record != null ? WxUtil.getInt("id", record).longValue() : null;
+    }
+
+    @Override
+    public Long getProductIdByCode(String productCode) {
+        Record record = Db.findFirst("select id from t_jz_product where product_no = ?", productCode);
+        return record != null ? WxUtil.getInt("id", record).longValue() : null;
+    }
+}

+ 333 - 0
src/main/java/com/qlm/service/impl/ProductionLineServiceImpl.java

@@ -0,0 +1,333 @@
+package com.qlm.service.impl;
+
+import com.jfinal.kit.StrKit;
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Record;
+import com.qlm.common.ApiResponse;
+import com.qlm.common.PageResult;
+import com.qlm.dto.ProductionLineDto;
+import com.qlm.service.IProductionLineService;
+import com.qlm.tools.WxUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 产线服务实现类
+ */
+public class ProductionLineServiceImpl implements IProductionLineService {
+
+    private static final Logger logger = LoggerFactory.getLogger(ProductionLineServiceImpl.class);
+
+    @Override
+    public ApiResponse saveProductionLine(ProductionLineDto productionLineDto) {
+        try {
+            // 数据验证
+            if (StrKit.isBlank(productionLineDto.getLineCode())) {
+                return ApiResponse.error("产线编号不能为空");
+            }
+
+            if (StrKit.isBlank(productionLineDto.getLineName())) {
+                return ApiResponse.error("产线名称不能为空");
+            }
+
+            if (productionLineDto.getFactoryId() == null) {
+                return ApiResponse.error("所属工厂不能为空");
+            }
+
+            if (productionLineDto.getWorkshopId() == null) {
+                return ApiResponse.error("所属车间不能为空");
+            }
+
+            // 检查产线编号是否已存在
+            Record existRecord = Db.findFirst("select id from t_production_line where line_code = ?", productionLineDto.getLineCode());
+            if (existRecord != null) {
+                return ApiResponse.error("产线编号已存在");
+            }
+
+            // 保存数据
+            Record record = new Record();
+            record.set("line_code", productionLineDto.getLineCode());
+            record.set("line_name", productionLineDto.getLineName());
+            record.set("factory_id", productionLineDto.getFactoryId());
+            record.set("workshop_id", productionLineDto.getWorkshopId());
+            record.set("contact_name", productionLineDto.getContactName());
+            record.set("contact_phone", productionLineDto.getContactPhone());
+            record.set("account_id", productionLineDto.getAccountId());
+            record.set("status", productionLineDto.getStatus() != null ? productionLineDto.getStatus() : 1);
+            record.set("operator", productionLineDto.getOperator());
+
+            boolean result = Db.save("t_production_line", record);
+            if (result) {
+                return ApiResponse.success("保存成功");
+            } else {
+                return ApiResponse.error("保存失败");
+            }
+        } catch (Exception e) {
+            logger.error("保存产线信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse updateProductionLine(ProductionLineDto productionLineDto) {
+        try {
+            // 数据验证
+            if (productionLineDto.getId() == null) {
+                return ApiResponse.error("产线ID不能为空");
+            }
+
+            if (StrKit.isBlank(productionLineDto.getLineCode())) {
+                return ApiResponse.error("产线编号不能为空");
+            }
+
+            if (StrKit.isBlank(productionLineDto.getLineName())) {
+                return ApiResponse.error("产线名称不能为空");
+            }
+
+            if (productionLineDto.getFactoryId() == null) {
+                return ApiResponse.error("所属工厂不能为空");
+            }
+
+            if (productionLineDto.getWorkshopId() == null) {
+                return ApiResponse.error("所属车间不能为空");
+            }
+
+            // 检查产线编号是否已存在(排除当前记录)
+            Record existRecord = Db.findFirst("select id from t_production_line where line_code = ? and id != ?", 
+                    productionLineDto.getLineCode(), productionLineDto.getId());
+            if (existRecord != null) {
+                return ApiResponse.error("产线编号已存在");
+            }
+
+            // 更新数据
+            Record record = new Record();
+            record.set("id", productionLineDto.getId());
+            record.set("line_code", productionLineDto.getLineCode());
+            record.set("line_name", productionLineDto.getLineName());
+            record.set("factory_id", productionLineDto.getFactoryId());
+            record.set("workshop_id", productionLineDto.getWorkshopId());
+            record.set("contact_name", productionLineDto.getContactName());
+            record.set("contact_phone", productionLineDto.getContactPhone());
+            record.set("account_id", productionLineDto.getAccountId());
+            record.set("status", productionLineDto.getStatus());
+            record.set("operator", productionLineDto.getOperator());
+
+            boolean result = Db.update("t_production_line", record);
+            if (result) {
+                return ApiResponse.success("更新成功");
+            } else {
+                return ApiResponse.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("更新产线信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse deleteProductionLine(Long id) {
+        try {
+            if (id == null) {
+                return ApiResponse.error("产线ID不能为空");
+            }
+
+            // 检查是否存在关联数据(如果有)
+            // TODO: 如果有其他表关联到产线表,需要先检查并处理
+
+            boolean result = Db.deleteById("t_production_line", id);
+            if (result) {
+                return ApiResponse.success("删除成功");
+            } else {
+                return ApiResponse.error("删除失败");
+            }
+        } catch (Exception e) {
+            logger.error("删除产线信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getProductionLineById(Long id) {
+        try {
+            if (id == null) {
+                return ApiResponse.error("产线ID不能为空");
+            }
+
+            // 关联查询工厂和车间名称
+            String sql = "select pl.*, f.factory_name, w.workshop_name from t_production_line pl " +
+                         "left join t_factory f on pl.factory_id = f.id " +
+                         "left join t_workshop w on pl.workshop_id = w.id " +
+                         "where pl.id = ?";
+            Record record = Db.findFirst(sql, id);
+            if (record == null) {
+                return ApiResponse.error("未找到该产线信息");
+            }
+
+            ProductionLineDto productionLineDto = convertRecordToDto(record);
+            return ApiResponse.success(productionLineDto);
+        } catch (Exception e) {
+            logger.error("获取产线信息异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse toggleProductionLineStatus(Long id, Integer status) {
+        try {
+            if (id == null) {
+                return ApiResponse.error("产线ID不能为空");
+            }
+
+            if (status == null || (status != 0 && status != 1)) {
+                return ApiResponse.error("状态值无效,只能是0或1");
+            }
+
+            Record record = new Record();
+            record.set("id", id);
+            record.set("status", status);
+            record.set("updated_time", WxUtil.getNowTime());
+
+            boolean result = Db.update("t_production_line", record);
+            if (result) {
+                return ApiResponse.success("状态更新成功");
+            } else {
+                return ApiResponse.error("状态更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("切换产线状态异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public PageResult<ProductionLineDto> listProductionLine(int pageNumber, int pageSize, String lineName, String lineCode, Long factoryId, Long workshopId) {
+        try {
+            // 构建查询条件
+            StringBuilder sqlWhere = new StringBuilder(" from t_production_line pl " +
+                                             "left join t_factory f on pl.factory_id = f.id " +
+                                             "left join t_workshop w on pl.workshop_id = w.id " +
+                                             "where 1=1 ");
+            List<Object> params = new ArrayList<>();
+
+            if (StrKit.notBlank(lineName)) {
+                sqlWhere.append(" and pl.line_name like ? ");
+                params.add("%" + lineName + "%");
+            }
+
+            if (StrKit.notBlank(lineCode)) {
+                sqlWhere.append(" and pl.line_code like ? ");
+                params.add("%" + lineCode + "%");
+            }
+
+            if (factoryId != null) {
+                sqlWhere.append(" and pl.factory_id = ? ");
+                params.add(factoryId);
+            }
+
+            if (workshopId != null) {
+                sqlWhere.append(" and pl.workshop_id = ? ");
+                params.add(workshopId);
+            }
+
+            sqlWhere.append(" order by pl.updated_time desc ");
+
+            // 执行分页查询
+            Page<Record> recordPage = Db.paginate(pageNumber, pageSize,
+                    "select pl.*, f.factory_name, w.workshop_name ",
+                    sqlWhere.toString(),
+                    params.toArray());
+
+            if (recordPage == null) {
+                return new PageResult<>(0, pageNumber, pageSize, new ArrayList<>());
+            }
+
+            // 转换为Dto对象
+            List<ProductionLineDto> productionLineDtoList = new ArrayList<>();
+            for (Record record : recordPage.getList()) {
+                productionLineDtoList.add(convertRecordToDto(record));
+            }
+
+            // 创建新的Page对象,包含Dto列表
+            return new PageResult<>(recordPage.getTotalRow(),
+                    recordPage.getPageNumber(),
+                    recordPage.getPageSize(),
+                    productionLineDtoList);
+
+        } catch (Exception e) {
+            logger.error("查询产线列表异常:", e);
+            return new PageResult<>(0, pageNumber, pageSize, new ArrayList<>());
+        }
+    }
+
+    @Override
+    public ApiResponse getAllFactories(String keyword) {
+        try {
+            StringBuilder sql = new StringBuilder("select id, factory_name from t_factory where status = 1 ");
+            List<Object> params = new ArrayList<>();
+
+            if (StrKit.notBlank(keyword)) {
+                sql.append(" and factory_name like ? ");
+                params.add("%" + keyword + "%");
+            }
+
+            sql.append(" order by factory_name asc ");
+
+            List<Record> records = Db.find(sql.toString(), params.toArray());
+            return ApiResponse.success(records);
+        } catch (Exception e) {
+            logger.error("获取工厂列表异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ApiResponse getWorkshopsByFactoryId(Long factoryId, String keyword) {
+        try {
+            StringBuilder sql = new StringBuilder("select id, workshop_name from t_workshop where status = 1 ");
+            List<Object> params = new ArrayList<>();
+            if (factoryId != null) {
+                sql.append(" and factory_id = ? ");
+                params.add(factoryId);
+            }
+            if (StrKit.notBlank(keyword)) {
+                sql.append(" and workshop_name like ? ");
+                params.add("%" + keyword + "%");
+            }
+            sql.append(" order by workshop_name asc ");
+
+            List<Record> records = Db.find(sql.toString(), params.toArray());
+            return ApiResponse.success(records);
+        } catch (Exception e) {
+            logger.error("获取车间列表异常:", e);
+            return ApiResponse.error("系统异常:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 将Record转换为ProductionLineDto的工具方法
+     * @param record
+     * @return
+     */
+    private ProductionLineDto convertRecordToDto(Record record) {
+        ProductionLineDto dto = new ProductionLineDto();
+        dto.setId(WxUtil.getInt("id",record).longValue());
+        dto.setLineCode(record.getStr("line_code"));
+        dto.setLineName(record.getStr("line_name"));
+        dto.setFactoryId(WxUtil.getInt("factory_id", record).longValue());
+        dto.setFactoryName(record.getStr("factory_name"));
+        dto.setWorkshopId(WxUtil.getInt("workshop_id", record).longValue());
+        dto.setWorkshopName(record.getStr("workshop_name"));
+        dto.setContactName(record.getStr("contact_name"));
+        dto.setContactPhone(record.getStr("contact_phone"));
+        dto.setAccountId(WxUtil.getStr("account_id", record));
+        dto.setStatus(record.getInt("status"));
+        dto.setCreatedTime(record.getDate("created_time"));
+        dto.setUpdatedTime(record.getDate("updated_time"));
+        dto.setOperator(record.getStr("operator"));
+        return dto;
+    }
+}

+ 14 - 8
src/main/java/com/qlm/service/impl/WorkshopServiceImpl.java

@@ -103,6 +103,7 @@ public class WorkshopServiceImpl implements IWorkshopService {
             record.set("factory_id", workshopDto.getFactoryId());
             record.set("contact_name", workshopDto.getContactName());
             record.set("contact_phone", workshopDto.getContactPhone());
+            record.set("status", workshopDto.getStatus());
 
             boolean result = Db.update("t_workshop", record);
             if (result) {
@@ -226,17 +227,22 @@ public class WorkshopServiceImpl implements IWorkshopService {
     }
 
     @Override
-    public ApiResponse getAllFactories() {
+    public ApiResponse getAllFactories(String keyword) {
         try {
-            // 查询所有启用的工厂
-            String sql = "select id, factory_name from t_factory where status = 1 order by factory_name asc";
-            List<Record> records = Db.find(sql);
+            StringBuilder sb = new StringBuilder();
+            sb.append("select id, factory_name from t_factory where status = 1 ");
+            List<Object> params = new ArrayList<>();
+            if(StrKit.notBlank(keyword)){
+                sb.append("and factory_name like ? ");
+                params.add("%" + keyword + "%");
+            }
+            List<Record> records = Db.find(sb.toString(),params.toArray());
 
             List<Record> resultList = new ArrayList<>();
             for (Record record : records) {
                 Record item = new Record();
-                item.set("value", record.getLong("id"));
-                item.set("label", record.getStr("factory_name"));
+                item.set("id", WxUtil.getInt("id", record));
+                item.set("factory_name", record.getStr("factory_name"));
                 resultList.add(item);
             }
 
@@ -252,10 +258,10 @@ public class WorkshopServiceImpl implements IWorkshopService {
      */
     private WorkshopDto convertRecordToDto(Record record) {
         WorkshopDto dto = new WorkshopDto();
-        dto.setId(record.getLong("id"));
+        dto.setId(WxUtil.getInt("id", record).longValue());
         dto.setWorkshopCode(record.getStr("workshop_code"));
         dto.setWorkshopName(record.getStr("workshop_name"));
-        dto.setFactoryId(record.getLong("factory_id"));
+        dto.setFactoryId(WxUtil.getInt("factory_id", record).longValue());
         dto.setFactoryName(record.getStr("factory_name"));
         dto.setContactName(record.getStr("contact_name"));
         dto.setContactPhone(record.getStr("contact_phone"));

+ 78 - 0
src/main/java/com/qlm/tools/EasyExcelUtil.java

@@ -0,0 +1,78 @@
+package com.qlm.tools;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @program: yunpingtai
+ * @ClassName: EasyExcelUtil
+ * @description: TODO
+ * @author: 吴英健
+ * @create: 2025-08-22 14:34
+ * @Version 1.0
+ **/
+public class EasyExcelUtil {
+    /**
+     * 获取导入数据
+     *
+     * @param inputStream Excel文件流
+     * @param clazz       导入的实体类
+     */
+    public static <T> List<T> getImportData(InputStream inputStream, Class<T> clazz) {
+        List<T> result = new ArrayList<>(16);
+        EasyExcel.read(inputStream, clazz, new AnalysisEventListener<T>() {
+            private boolean stop = false;
+
+            @Override
+            public boolean hasNext(AnalysisContext context) {
+                return !stop;
+            }
+
+            @Override
+            public void invoke(T data, AnalysisContext context) {
+                // 当满足某个条件时,停止读取
+                if (conditionMet(data)) {
+                    stop = true;
+                } else {
+                    result.add(data);
+                }
+            }
+
+            @Override
+            public void doAfterAllAnalysed(AnalysisContext context) {
+            }
+
+            private boolean conditionMet(T data) {
+                return JSONObject.toJSONString(data).contains("导入说明");
+            }
+
+        }).ignoreEmptyRow(true).sheet().doRead();
+        return result;
+    }
+
+    /**
+     * 导出Excel文件
+     * @param exportData 导出的数据
+     * @param sheetName sheet名称
+     * @param clazz 导出的实体类
+     * @return excel文件流
+     */
+    public static <T> InputStream export(List<T> exportData,String sheetName,Class<T> clazz) throws Exception {
+        sheetName = StringUtils.isBlank(sheetName) ? "sheet1" : sheetName;
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+            EasyExcel.write(bos, clazz).sheet(sheetName).doWrite(exportData);
+            return new ByteArrayInputStream(bos.toByteArray());
+        } catch (Exception e) {
+            throw e;
+        }
+    }
+}

+ 15 - 5
src/main/resources/config.properties

@@ -1,12 +1,12 @@
 #mysql 
-#jdbcUrl = jdbc:mysql://58.20.133.135:6177/jinzai?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
-#user=jinzai
-#password =rTdBsjA636XkarRa
-
-jdbcUrl = jdbc:mysql://127.0.0.1:6177/jinzai?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
+jdbcUrl = jdbc:mysql://58.20.133.135:6177/jinzai?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
 user=jinzai
 password =rTdBsjA636XkarRa
 
+#jdbcUrl = jdbc:mysql://127.0.0.1:6177/jinzai?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
+#user=jinzai
+#password =rTdBsjA636XkarRa
+
 #qrcode
 dbname=jinzai
 authoUrl=https://jzfch.jinzaifood.com.cn:6178
@@ -20,3 +20,13 @@ appid=wxf09c24f8384518fd
 appsecret=ad94208a7df3f0bc3aeec2100a546555
 
 
+oss.endpoint=oss-cn-hangzhou.aliyuncs.com
+oss.accessKeyId=LTAI5tPkvJTrf8DxQq6YSKYf
+oss.accessKeySecret=U5RNVF8PYP136QqxjSuqEing8fdTO0
+oss.bucketName=hyscancode
+oss.domain=https://hyscancode.oss-cn-hangzhou.aliyuncs.com
+oss.mkdir=miniscancode/
+# 10MB
+oss.maxSize=10485760
+oss.allowedTypes=.jpg,.jpeg,.png,.gif,.xlsx,.xls
+

+ 3 - 3
src/main/resources/job.properties

@@ -1,14 +1,14 @@
 a.job=com.qlm.job.MyJob
 a.cron=0 */5 * * * ?
-a.enable=true
+a.enable=false
 
 
 b.job=com.qlm.job.UploadJob
 b.cron=0 */5 * * * ?
-b.enable=true
+b.enable=false
 
 
 c.job=com.qlm.job.SyncJob
 c.cron=0 0 1 * * ?
-c.enable=true
+c.enable=false
 

+ 479 - 0
src/main/webapp/page/jinzai/brand.jsp

@@ -0,0 +1,479 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+         pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>品牌列表</title>
+    <meta name="keywords" content="品牌管理">
+    <meta name="description" content="品牌信息管理页面">
+    <!-- 引入Bootstrap CSS -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <link href="${ctx}/css/bootstrap-select.min.css" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+
+        .form-group {
+            margin-bottom: 1.5rem;
+        }
+
+        .toggle-switch {
+            position: relative;
+            display: inline-block;
+            width: 60px;
+            height: 34px;
+        }
+
+        .toggle-switch input {
+            opacity: 0;
+            width: 0;
+            height: 0;
+        }
+
+        .slider {
+            position: absolute;
+            cursor: pointer;
+            top: 0;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            background-color: #ccc;
+            transition: .4s;
+            border-radius: 34px;
+        }
+
+        .slider:before {
+            position: absolute;
+            content: "";
+            height: 26px;
+            width: 26px;
+            left: 4px;
+            bottom: 4px;
+            background-color: white;
+            transition: .4s;
+            border-radius: 50%;
+        }
+
+        input:checked + .slider {
+            background-color: #2196F3;
+        }
+
+        input:checked + .slider:before {
+            transform: translateX(26px);
+        }
+
+        .form-container {
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+            background-color: #fff;
+        }
+    </style>
+</head>
+
+<body class="gray-bg">
+<div class="wrapper wrapper-content animated fadeInRight">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="ibox">
+                <div class="ibox-title">
+                    <div class="row">
+                        <div class="col-sm-10">
+                            <h3>品牌列表</h3>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="ibox-content">
+                    <div class="row row-lg mb-4">
+                        <div class="col-lg-12">
+                            <div class="input-group">
+                                <input id="brandName" class="form-control" style="max-width: 300px;" placeholder="请输入品牌名称"/>
+                                <button type="button" id="searchBtn" class="btn btn-success ms-2" onclick="search();return false;">
+                                    <i class="fas fa-search"></i> 查询
+                                </button>
+                                <button type="button" id="resetBtn" class="btn btn-secondary ms-2" onclick="reset();return false;">
+                                    <i class="fas fa-sync-alt"></i> 重置
+                                </button>
+                                <button type="button" class="btn btn-primary ms-2" onclick="showAddModal();">
+                                    <i class="fas fa-plus"></i> 新增
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row row-lg mt-3">
+                        <div class="col-sm-12">
+                            <table id="table" data-toggle="table" data-mobile-responsive="true"></table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 新增/编辑品牌模态框 -->
+<div class="modal fade" id="brandModal" tabindex="-1" aria-labelledby="brandModalLabel" aria-hidden="true">
+    <div class="modal-dialog modal-lg">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="brandModalLabel">新增品牌</h5>
+                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+            </div>
+            <div class="modal-body">
+                <div class="form-container">
+                    <form id="brandForm">
+                        <input type="hidden" id="brandId" name="brandId">
+                        <div class="row mb-4">
+                            <div class="col">
+                                <label for="brandCode" class="required form-label">品牌编号</label>
+                                <input type="text" class="form-control" id="brandCode" name="brandCode" placeholder="请输入" required>
+                                <div class="form-text text-muted">品牌编号规则:不超过50个字符</div>
+                            </div>
+                            <div class="col">
+                                <label for="modalBrandName" class="required form-label">品牌名称</label>
+                                <input type="text" class="form-control" id="modalBrandName" name="modalBrandName" placeholder="请输入" required>
+                                <div class="form-text text-muted">名称:不超过50个字符</div>
+                            </div>
+                        </div>
+
+                        <div class="row mb-4">
+                            <div class="col">
+                                <label for="contactPhone" class="form-label">联系方式</label>
+                                <input type="text" class="form-control" id="contactPhone" name="contactPhone" placeholder="请输入">
+                            </div>
+                            <div class="col">
+                                <label for="status" class="required form-label">状态</label>
+                                <div class="d-flex align-items-center gap-2">
+                                    <label class="toggle-switch">
+                                        <input type="checkbox" id="status" name="status" checked>
+                                        <span class="slider"></span>
+                                    </label>
+                                    <span id="statusText">启用</span>
+                                    <div class="form-text text-muted">状态:启用、停用,默认启用</div>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-primary" onclick="saveBrand();">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 全局js -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<!-- 引入Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+<script src="${ctx}/js/bootstrap-select.min.js"></script>
+<!-- jQuery Validation plugin javascript-->
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table-mobile.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="${ctx}/js/common.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/Math.uuid.js"></script>
+<!-- 自定义js -->
+<script src="${ctx}/js/content.js?v=1.0.0"></script>
+</body>
+<script>
+    var table = null;
+    var brandModal = new bootstrap.Modal(document.getElementById('brandModal'));
+
+    $(document).ready(function () {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+        initValidation();
+
+        // 状态切换按钮
+        const statusToggle = document.getElementById('status');
+        const statusText = document.getElementById('statusText');
+
+        statusToggle.addEventListener('change', function () {
+            if (this.checked) {
+                statusText.textContent = '启用';
+            } else {
+                statusText.textContent = '停用';
+            }
+        });
+    });
+
+    function queryParams(param) {
+        let keyword = $.trim($("#brandName").val());
+        if (keyword) {
+            param['keyword'] = keyword;
+        }
+        return param;
+    }
+
+    function search() {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reset() {
+        $("#brandName").val('');
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function showAddModal() {
+        // 清空表单
+        $('#brandForm')[0].reset();
+        $('#brandId').val('');
+        $('#status').prop('checked', true);
+        $('#statusText').text('启用');
+        $('#brandModalLabel').text('新增品牌');
+        // 显示模态框
+        brandModal.show();
+    }
+
+    function showEditModal(id) {
+        // 清空表单
+        $('#brandForm')[0].reset();
+        // 加载品牌数据
+        loadBrandData(id);
+        $('#brandModalLabel').text('编辑品牌');
+        // 显示模态框
+        brandModal.show();
+    }
+
+    function loadBrandData(id) {
+        $.ajax({
+            url: '${ctx}/brand/getById?id=' + id,
+            type: 'GET',
+            dataType: 'json',
+            success: function (data) {
+                if (data.code === 0) {
+                    const brand = data.data;
+                    // 填充表单数据
+                    $('#brandId').val(brand.id);
+                    $('#brandCode').val(brand.brandCode);
+                    $('#modalBrandName').val(brand.brandName);
+                    $('#contactPhone').val(brand.contactPhone);
+
+                    // 设置状态开关
+                    if (brand.status === 1) {
+                        $('#status').prop('checked', true);
+                        $('#statusText').text('启用');
+                    } else {
+                        $('#status').prop('checked', false);
+                        $('#statusText').text('停用');
+                    }
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function () {
+                layer.msg('加载品牌数据失败', {icon: 5});
+            }
+        });
+    }
+
+    function saveBrand() {
+        if (!$('#brandForm').valid()) {
+            return;
+        }
+
+        // 获取表单数据
+        const id = $('#brandId').val();
+        const formData = {
+            id: id,
+            brandCode: $('#brandCode').val(),
+            brandName: $('#modalBrandName').val(),
+            contactPhone: $('#contactPhone').val(),
+            status: $('#status').is(':checked') ? 1 : 0
+        };
+
+        $.ajax({
+            url: id ? '${ctx}/brand/update' : '${ctx}/brand/save',
+            type: 'POST',
+            data: JSON.stringify(formData),
+            contentType: 'application/json',
+            success: function (data) {
+                if (data.code === 0) {
+                    layer.msg('保存成功', {icon: 6});
+                    // 关闭模态框
+                    brandModal.hide();
+                    // 刷新表格
+                    search();
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function () {
+                layer.msg('保存失败', {icon: 5});
+            }
+        });
+    }
+
+    function deleteBrand(id) {
+        layer.confirm('确定要删除该品牌吗?此操作不可撤销!', {
+            btn: ['确定', '取消']
+        }, function () {
+            $.ajax({
+                url: '${ctx}/brand/delete',
+                type: 'post',
+                data: {
+                    id: id
+                },
+                success: function (result) {
+                    if (result.code === 0) {
+                        layer.msg(result.msg, {icon: 1});
+                        search(); // 刷新表格
+                    } else {
+                        layer.msg(result.msg, {icon: 2});
+                    }
+                },
+                error: function () {
+                    layer.msg('删除失败', {icon: 2});
+                }
+            });
+        });
+    }
+
+    function initValidation() {
+        // 添加自定义验证方法
+        $.validator.addMethod('isPhone', function(value, element) {
+            const phoneRegex = /^1[3-9]\d{9}$/;
+            return this.optional(element) || phoneRegex.test(value);
+        }, '请输入有效的手机号码');
+
+        $('#brandForm').validate({
+            rules: {
+                brandCode: {
+                    required: true,
+                    maxlength: 20
+                },
+                brandName: {
+                    required: true,
+                    maxlength: 100
+                },
+                contactPhone: {
+                    isPhone: true
+                }
+            },
+            messages: {
+                brandCode: {
+                    required: '请输入品牌编号',
+                    maxlength: '品牌编号最多20个字符'
+                },
+                brandName: {
+                    required: '请输入品牌名称',
+                    maxlength: '品牌名称最多100个字符'
+                },
+                contactPhone: {
+                    isPhone: '请输入有效的联系电话'
+                }
+            }
+        });
+    }
+
+    function initTable() {
+        table = $('#table').bootstrapTable({
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',    //工具按钮用哪个容器
+            striped: true,      //是否显示行间隔色
+            cache: false,      //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
+            pagination: true,     //是否显示分页(*)
+            pageNumber: 1,      //初始化加载第一页,默认第一页
+            pageSize: 10,      //每页的记录行数(*)
+            pageList: [10, 25, 50, 100],  //可供选择的每页的行数(*)
+            url: '${ctx}/brand/list',//这个接口需要处理bootstrap table传递的固定参数
+            queryParamsType: '', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort
+            // 设置为 ''  在这种情况下传给服务器的参数为:pageSize,pageNumber
+
+            queryParams: queryParams,//前端调用服务时,会默认传递上边提到的参数,如果需要添加自定义参数,可以自定义一个函数返回请求参数
+            sidePagination: "server",   //分页方式:client客户端分页,server服务端分页(*)
+            strictSearch: false,
+            minimumCountColumns: 2,    //最少允许的列数
+            clickToSelect: true,    //是否启用点击选中行
+            searchOnEnterKey: true,
+            paginationDetailHAlign: 'right', // 将分页详细信息放在右边
+            idField: "id",
+            // 设置数据格式转换
+            responseHandler: function (res) {
+                // 这里假设接口返回的data就是我们需要的表格数据
+                return {
+                    total: res.total,  // 总记录数
+                    rows: res.records  // 数据列表
+                };
+            },
+            columns: [{
+                title: '序号',
+                align: 'center',
+                formatter: function (value, row, index) {
+                    // 使用this关键字访问表格实例
+                    var pageNumber = this.pageNumber || 1;
+                    var pageSize = this.pageSize || 10;
+                    return (pageNumber - 1) * pageSize + index + 1;
+                },
+                width: "5%"
+            }, {
+                field: 'brandCode',
+                title: '品牌编号',
+                align: 'center',
+                width: "20%"
+            }, {
+                field: 'brandName',
+                title: '品牌名称',
+                align: 'center',
+                width: "25%"
+            }, {
+                field: 'contactPhone',
+                title: '联系方式',
+                align: 'center',
+                width: "20%",
+                formatter: function (value) {
+                    return value || '-';  // 处理null值
+                }
+            }, {
+                field: 'status',
+                title: '状态',
+                align: 'center',
+                formatter: function (value) {
+                    if (value === 1) {
+                        return '<span class="label label-success">启用</span>';
+                    } else {
+                        return '<span class="label label-danger">停用</span>';
+                    }
+                },
+                width: "10%"
+            }, {
+                title: '操作',
+                align: 'center',
+                width: "20%",
+                formatter: function (value, row) {
+                    return '<button class="btn btn-primary btn-sm me-1" onclick="showEditModal(' + row.id + ')">编辑</button> ' +
+                        '<button class="btn btn-danger btn-sm" onclick="deleteBrand(' + row.id + ')">删除</button>';
+                }
+            }],
+            onLoadSuccess: function (data) {
+                console.log("数据加载成功", data);
+            },
+            onLoadError: function () {
+                layer.msg('数据加载失败', {icon: 2});
+            }
+        });
+    }
+</script>
+</html>

+ 1 - 1
src/main/webapp/page/jinzai/factory.jsp

@@ -45,7 +45,7 @@
                         <button type="button" id="searchBtn" class="btn btn-success" onclick="search();return false;">搜索</button>
                         <button type="button" id="resetBtn" class="btn btn-default" onclick="reset();return false;">重置</button>
                         <button type="button" class="btn btn-primary pull-right" onclick="addFactory();">
-                            <i class="glyphicon glyphicon-plus"></i> 添加工厂
+                            <i class="glyphicon glyphicon-plus"></i> 新增工厂
                         </button>
                     </div>
                 </div>

+ 297 - 0
src/main/webapp/page/jinzai/flow_search.jsp

@@ -0,0 +1,297 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+    pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>流向查询</title>
+    <meta name="keywords" content="流向查询">
+    <meta name="description" content="流向信息查询页面">
+    <link rel="shortcut icon" href="favicon.ico"> 
+    <link href="${ctx}/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
+    <link href="${ctx}/css/font-awesome.css?v=4.4.0" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+</head>
+
+<body class="gray-bg">
+    <div class="wrapper wrapper-content animated fadeInRight">
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="ibox float-e-margins">
+                    <div class="ibox-title">
+                        <div class="row">
+                           <div class="col-sm-12">
+                                <h3>流向查询</h3>
+                           </div>
+                        </div>
+                    </div>
+                    <div class="ibox-content">
+                        <form class="form-horizontal m-t" name="flowform" id="flowform">
+                            <div class="row">
+                                <div class="col-sm-2"></div>
+                                <div class="col-sm-8">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label"><span class="text-danger">*</span>数码:</label>
+                                        <div class="col-sm-4">
+                                            <input class="form-control" type="text" name="code" id="code" placeholder="请输入数码" maxlength="100" />
+                                        </div>
+                                        <div class="col-sm-4">
+                                            <button class="btn btn-primary" id="searchBtn" type="button" onclick="search();return false;">查询</button>
+                                            <button class="btn btn-white" id="resetBtn" type="button" onclick="reset();">重置</button>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-2"></div>
+                            </div>
+                        </form>
+                    </div>
+                      
+                    <div class="ibox-content" id="resultDiv" style="display:none">
+                        <div class="row">
+                            <div class="col-sm-12">
+                                <div class="alert alert-warning alert-dismissable">
+                                    <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
+                                    输入数码后显示数据
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-12">
+                                <h4>产品信息</h4>
+                                <table id="productTable" class="table table-striped table-bordered table-hover dataTables-example">
+                                    <thead>
+                                        <tr>
+                                            <th>产品编号</th>
+                                            <th>产品名称</th>
+                                            <th>品项编码</th>
+                                            <th>产品品项</th>
+                                            <th>产品分类</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody id="productTbody"></tbody>
+                                </table>
+                            </div>
+                        </div>
+                        <div class="row mt-4">
+                            <div class="col-sm-12">
+                                <h4>入库信息</h4>
+                                <table id="inboundTable" class="table table-striped table-bordered table-hover dataTables-example">
+                                    <thead>
+                                        <tr>
+                                            <th>生产单号</th>
+                                            <th>彩盒码</th>
+                                            <th>箱码</th>
+                                            <th>托码</th>
+                                            <th>入库人</th>
+                                            <th>生产日期</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody id="inboundTbody"></tbody>
+                                </table>
+                            </div>
+                        </div>
+                        <div class="row mt-4">
+                            <div class="col-sm-12">
+                                <h4>出库信息</h4>
+                                <table id="outboundTable" class="table table-striped table-bordered table-hover dataTables-example">
+                                    <thead>
+                                        <tr>
+                                            <th>出库单号</th>
+                                            <th>发货仓库</th>
+                                            <th>出库人</th>
+                                            <th>经销商名称</th>
+                                            <th>应销地区</th>
+                                            <th>出库时间</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody id="outboundTbody"></tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!-- 全局js -->
+    <script src="${ctx}/js/jquery.min.js?v=2.1.4"></script>
+    <script src="${ctx}/js/bootstrap.min.js?v=3.3.6"></script>
+    <!-- 自定义js -->
+    <script src="${ctx}/js/content.js?v=1.0.0"></script>
+    <!-- jQuery Validation plugin javascript-->
+    <script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+    <script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+    <script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+
+    <script type="text/javascript">
+    var ctx = "${ctx}";
+    
+    // 模拟数据
+    var mockData = {
+        product: {
+            productCode: "C20220629001",
+            productName: "12g深海小鱼",
+            itemCode: "C20220629001002",
+            itemName: "麻辣",
+            category: "-"
+        },
+        inbound: {
+            productionNo: "188237526440185315",
+            boxCode: "1cb40g2dfrfur",
+            cartoonCode: "000052140000438700068982",
+            palletCode: "000053150000437400013714",
+            inboundPerson: "zd003",
+            productionDate: "2025-01-23 18:30:11"
+        },
+        outbound: {
+            outboundNo: "SPJ20251700257",
+            warehouse: "平江仓",
+            outboundPerson: "fulesync",
+            dealerName: "山东青岛陈春园",
+            salesArea: "-",
+            outboundTime: "2025-01-29 11:47:58"
+        }
+    };
+    
+    $(function(){
+        $('#resetBtn').on('click', function() {
+            reset();
+        });
+        // 表单验证
+        $("#flowform").validate({
+            rules : {
+                code:{
+                    required : true
+                }
+            },
+            messages : {
+                code:{
+                    required : "请输入数码"
+                }
+            }
+        });
+    });
+    
+    function search(){
+        if(!$("#flowform").valid()){
+            return;
+        }
+
+        layer.load();
+        var code = $("#code").val().toLowerCase();
+
+        $.ajax({
+            url:"${ctx}/jinzaiOrder/getCodeFlowSearch",
+            type:"post",
+            data:{code:code},
+            success:function(res){
+                layer.closeAll();
+
+                if(res.code === 0){
+                    $("#resultDiv").show();
+                    // 填充产品信息
+                    fillProductInfo(res.data.product);
+                    // 填充入库信息
+                    fillInboundInfo(res.data.inbound);
+                    // 填充出库信息
+                    fillOutboundInfo(res.data.outbound);
+                } else {
+                    layer.msg(res.msg, {icon: 5});
+                    $("#resultDiv").hide();
+                }
+            },
+            error:function(){
+                layer.closeAll();
+                layer.msg("查询失败,请重试", {icon: 5});
+            }
+        });
+
+
+        // if(!$("#flowform").valid()){
+        //     return;
+        // }
+        //
+        // layer.load();
+        // var code = $("#code").val().toLowerCase();
+        //
+        // // 模拟延迟加载
+        // setTimeout(function(){
+        //     layer.closeAll();
+        //     $("#resultDiv").show();
+        //
+        //     // 使用模拟数据填充表格
+        //     fillProductInfo(mockData.product);
+        //     fillInboundInfo(mockData.inbound);
+        //     fillOutboundInfo(mockData.outbound);
+        // }, 800);
+    }
+
+    function reset() {
+        // 重置表单数据
+        $("#flowform")[0].reset();
+        // 重置jQuery Validate验证状态
+        $("#flowform").validate().resetForm();
+        // 隐藏结果区域
+        $("#resultDiv").hide();
+
+        // 可选:清空结果表格内容
+        $("#productTbody").empty();
+        $("#inboundTbody").empty();
+        $("#outboundTbody").empty();
+    }
+    
+    function fillProductInfo(product){
+        var tbody = $("#productTbody");
+        tbody.empty();
+        
+        if(product){
+            var row = $('<tr/>');
+            row.append($('<td/>').text(product.productCode || '-'));
+            row.append($('<td/>').text(product.productName || '-'));
+            row.append($('<td/>').text(product.itemCode || '-'));
+            row.append($('<td/>').text(product.itemName || '-'));
+            row.append($('<td/>').text(product.category || '-'));
+            tbody.append(row);
+        }
+    }
+    
+    function fillInboundInfo(inbound){
+        var tbody = $("#inboundTbody");
+        tbody.empty();
+        
+        if(inbound){
+            var row = $('<tr/>');
+            row.append($('<td/>').text(inbound.productionNo || '-'));
+            row.append($('<td/>').text(inbound.boxCode || '-'));
+            row.append($('<td/>').text(inbound.cartoonCode || '-'));
+            row.append($('<td/>').text(inbound.palletCode || '-'));
+            row.append($('<td/>').text(inbound.inboundPerson || '-'));
+            row.append($('<td/>').text(inbound.productionDate || '-'));
+            tbody.append(row);
+        }
+    }
+    
+    function fillOutboundInfo(outbound){
+        var tbody = $("#outboundTbody");
+        tbody.empty();
+        
+        if(outbound){
+            var row = $('<tr/>');
+            row.append($('<td/>').text(outbound.outboundNo || '-'));
+            row.append($('<td/>').text(outbound.warehouse || '-'));
+            row.append($('<td/>').text(outbound.outboundPerson || '-'));
+            row.append($('<td/>').text(outbound.dealerName || '-'));
+            row.append($('<td/>').text(outbound.salesArea || '-'));
+            row.append($('<td/>').text(outbound.outboundTime || '-'));
+            tbody.append(row);
+        }
+    }
+    </script>
+</body>
+</html>

+ 592 - 0
src/main/webapp/page/jinzai/line_product_relation.jsp

@@ -0,0 +1,592 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>产线产品关联关系列表</title>
+    <meta name="keywords" content="产线产品关联">
+    <meta name="description" content="产线与产品关联关系管理页面">
+    <link href="${ctx}/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
+    <link href="${ctx}/css/font-awesome.css?v=4.4.0" rel="stylesheet">
+    <link href="${ctx}/css/bootstrap-select.min.css" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+</head>
+
+<body class="gray-bg">
+<div class="wrapper wrapper-content animated fadeInRight">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="ibox">
+                <div class="ibox-title">
+                    <div class="row">
+                        <div class="col-sm-10">
+                            <h3>产线产品</h3>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="ibox-content">
+                    <div class="row row-lg" style="margin-left: 15px;">
+                        <form class="form-horizontal m-t">
+                            <div id="condition" class="form-group" style="margin-bottom: 0; display: flex; align-items: center;">
+                                <div class="col-sm-2" style="padding-right: 5px; padding-left: 0;">
+                                    <select id="factoryId" class="form-control selectpicker" data-live-search="true" title="请选择工厂">
+                                    </select>
+                                </div>
+                                <div class="col-sm-2" style="padding-right: 5px; padding-left: 5px;">
+                                    <select id="workshopId" class="form-control selectpicker" data-live-search="true" title="请选择车间">
+                                    </select>
+                                </div>
+                                <div class="col-sm-2" style="padding-right: 5px; padding-left: 5px;">
+                                    <select id="lineId" class="form-control selectpicker" data-live-search="true" title="请选择产线">
+                                    </select>
+                                </div>
+                                <div class="col-sm-2" style="padding-right: 15px; padding-left: 5px;">
+                                    <select id="productId" class="form-control selectpicker" data-live-search="true" title="请选择产品">
+                                    </select>
+                                </div>
+                                <div class="col-sm-4" style="padding-right: 0; padding-left: 0; display: flex; justify-content: flex-end; gap: 10px;">
+                                    <button type="button" id="searchBtn" class="btn btn-warning" style="margin-right: 66px;" onclick="search();">查询</button>
+                                    <button type="button" id="resetBtn" class="btn btn-default" onclick="reset();">重置</button>
+                                    <button type="button" class="btn btn-primary" onclick="importRelation();"><i class="glyphicon glyphicon-import"></i> 导入关联</button>
+                                    <button type="button" class="btn btn-success" onclick="addRelation();"><i class="glyphicon glyphicon-plus"></i> 新增关联</button>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="row row-lg">
+                        <div class="col-sm-12">
+                            <table id="table" data-toggle="table" data-mobile-responsive="true">
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 全局js -->
+<script src="${ctx}/js/jquery.min.js?v=2.1.4"></script>
+<script src="${ctx}/js/bootstrap.min.js?v=3.3.6"></script>
+<script src="${ctx}/js/bootstrap-select.min.js"></script>
+<!-- 自定义js -->
+<script src="${ctx}/js/content.js?v=1.0.0"></script>
+<!-- jQuery Validation plugin javascript-->
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table-mobile.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="${ctx}/js/common.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/Math.uuid.js"></script>
+</body>
+<script>
+    var table = null;
+    $(document).ready(function () {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+        $('.selectpicker').selectpicker({
+            liveSearch: true,
+            size: 5,
+            actionsBox: true,
+            selectedTextFormat: 'count > 2'
+        });
+        // 绑定重置按钮点击事件
+        $('#resetBtn').on('click', function() {
+            reset();
+        });
+        // 初始化工厂下拉框
+        initFactorySelect();
+        loadWorkshopsByFactoryId();
+        loadLinesByWorkshopId();
+        initProductSelect();
+    });
+
+    // 加载工厂数据
+    function initFactorySelect() {
+        $.ajax({
+            url: '${ctx}/lineProduct/getFactoryList',
+            type: 'POST',
+            dataType: 'json',
+            beforeSend: function () {
+                $('#factoryId').prop('disabled', true).selectpicker('refresh');
+            },
+            success: function (res) {
+                $('#factoryId').empty();
+                if (res.data && res.data.length) {
+                    res.data.forEach(item => {
+                        $('#factoryId').append('<option value="' + item.id + '">' + item.factory_name + '</option>');
+                    });
+                }
+                $('#factoryId').selectpicker('refresh');
+            },
+            error: function (xhr) {
+                $('#factoryId').empty().append('<option value="">加载失败</option>');
+                $('#factoryId').selectpicker('refresh');
+                layer.msg('获取工厂数据失败: ' + xhr.statusText);
+            },
+            complete: function () {
+                $('#factoryId').prop('disabled', false).selectpicker('refresh');
+            }
+        });
+    }
+
+    // 根据工厂ID加载车间数据
+    function loadWorkshopsByFactoryId(factoryId) {
+        $.ajax({
+            url: '${ctx}/lineProduct/getWorkshopList',
+            type: 'POST',
+            data: {factoryId: factoryId},
+            dataType: 'json',
+            beforeSend: function () {
+                $('#workshopId').prop('disabled', true).selectpicker('refresh');
+            },
+            success: function (res) {
+                $('#workshopId').empty();
+                if (res.data && res.data.length) {
+                    res.data.forEach(item => {
+                        $('#workshopId').append('<option value="' + item.id + '">' + item.workshop_name + '</option>');
+                    });
+                }
+                $('#workshopId').selectpicker('refresh');
+            },
+            error: function (xhr) {
+                $('#workshopId').empty().append('<option value="">加载失败</option>');
+                $('#workshopId').selectpicker('refresh');
+                layer.msg('获取车间数据失败: ' + xhr.statusText);
+            },
+            complete: function () {
+                $('#workshopId').prop('disabled', false).selectpicker('refresh');
+            }
+        });
+    }
+
+    // 根据车间ID加载产线数据
+    function loadLinesByWorkshopId(workshopId) {
+        $.ajax({
+            url: '${ctx}/lineProduct/getLineList',
+            type: 'POST',
+            data: {workshopId: workshopId},
+            dataType: 'json',
+            beforeSend: function () {
+                $('#lineId').prop('disabled', true).selectpicker('refresh');
+            },
+            success: function (res) {
+                $('#lineId').empty();
+                if (res.data && res.data.length) {
+                    res.data.forEach(item => {
+                        $('#lineId').append('<option value="' + item.id + '">' + item.lineName + '</option>');
+                    });
+                }
+                $('#lineId').selectpicker('refresh');
+            },
+            error: function (xhr) {
+                $('#lineId').empty().append('<option value="">加载失败</option>');
+                $('#lineId').selectpicker('refresh');
+                layer.msg('获取产线数据失败: ' + xhr.statusText);
+            },
+            complete: function () {
+                $('#lineId').prop('disabled', false).selectpicker('refresh');
+            }
+        });
+    }
+
+    // 加载产品数据
+    function initProductSelect() {
+        $.ajax({
+            url: '${ctx}/lineProduct/getProductList',
+            type: 'POST',
+            dataType: 'json',
+            beforeSend: function () {
+                $('#productId').prop('disabled', true).selectpicker('refresh');
+            },
+            success: function (res) {
+                $('#productId').empty();
+                if (res.data && res.data.length) {
+                    res.data.forEach(item => {
+                        $('#productId').append('<option value="' + item.id + '">' + item.product_name + '</option>');
+                    });
+                }
+                $('#productId').selectpicker('refresh');
+            },
+            error: function (xhr) {
+                $('#productId').empty().append('<option value="">加载失败</option>');
+                $('#productId').selectpicker('refresh');
+                layer.msg('获取产品数据失败: ' + xhr.statusText);
+            },
+            complete: function () {
+                $('#productId').prop('disabled', false).selectpicker('refresh');
+            }
+        });
+    }
+
+    // 监听工厂选择变化
+    $(document).on('change', '#factoryId', function() {
+        const factoryId = $(this).val();
+        if (factoryId) {
+            loadWorkshopsByFactoryId(factoryId);
+        } else {
+            $('#workshopId').empty().append('<option value="">请选择车间</option>');
+            $('#workshopId').selectpicker('refresh');
+            $('#lineId').empty().append('<option value="">请选择产线</option>');
+            $('#lineId').selectpicker('refresh');
+        }
+    });
+
+    // 监听车间选择变化
+    $(document).on('change', '#workshopId', function() {
+        const workshopId = $(this).val();
+        if (workshopId) {
+            loadLinesByWorkshopId(workshopId);
+        } else {
+            $('#lineId').empty().append('<option value="">请选择产线</option>');
+            $('#lineId').selectpicker('refresh');
+        }
+    });
+
+    function queryParams(param) {
+        let factoryId = $.trim($("#factoryId").val());
+        if (factoryId) {
+            param['factoryId'] = factoryId;
+        }
+        let workshopId = $.trim($("#workshopId").val());
+        if (workshopId) {
+            param['workshopId'] = workshopId;
+        }
+        let lineId = $.trim($("#lineId").val());
+        if (lineId) {
+            param['lineId'] = lineId;
+        }
+        let productId = $.trim($("#productId").val());
+        if (productId) {
+            param['productId'] = productId;
+        }
+        return param;
+    }
+
+    function search() {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reset() {
+        // 重置所有下拉框
+        $("#factoryId").val('');
+        $("#factoryId").selectpicker('val', '');
+        $("#factoryId").selectpicker('deselectAll');
+        $("#factoryId").selectpicker('refresh');
+
+        $("#workshopId").val('');
+        $("#workshopId").selectpicker('val', '');
+        $("#workshopId").selectpicker('deselectAll');
+        $("#workshopId").selectpicker('refresh');
+
+        $("#lineId").val('');
+        $("#lineId").selectpicker('val', '');
+        $("#lineId").selectpicker('deselectAll');
+        $("#lineId").selectpicker('refresh');
+
+        $("#productId").val('');
+        $("#productId").selectpicker('val', '');
+        $("#productId").selectpicker('deselectAll');
+        $("#productId").selectpicker('refresh');
+
+        // 重新搜索
+        search();
+    }
+
+    function addRelation() {
+        layer.open({
+            type: 2,
+            title: '新增产线产品关联',
+            area: ['800px', '500px'],
+            content: '${ctx}/page/jinzai/line_product_relation_add.jsp',
+            end: function () {
+                // 关闭弹窗后刷新表格
+                $('#table').bootstrapTable('refresh');
+            }
+        });
+    }
+
+    function editRelation(id) {
+        layer.open({
+            type: 2,
+            title: '编辑产线产品关联',
+            content: '${ctx}/page/jinzai/line_product_relation_add.jsp?id=' + id,
+            area: ['800px', '500px'],
+            maxmin: true,
+            end: function () {
+                // 关闭弹窗后刷新表格
+                $('#table').bootstrapTable('refresh');
+            }
+        });
+    }
+
+    function removeRelation(id) {
+        layer.confirm('确定要移除该产线产品关联吗?此操作不可撤销!', {
+            btn: ['确定', '取消']
+        }, function () {
+            $.ajax({
+                url: '${ctx}/lineProduct/delete',
+                type: 'post',
+                data: {
+                    id: id
+                },
+                success: function (result) {
+                    if (result.code === 0) {
+                        layer.msg(result.msg, {icon: 1});
+                        search(); // 刷新表格
+                    } else {
+                        layer.msg(result.msg, {icon: 2});
+                    }
+                },
+                error: function () {
+                    layer.msg('移除失败', {icon: 2});
+                }
+            });
+        });
+    }
+
+    function importRelation() {
+        const importHtml = `
+            <div style="padding: 20px;">
+                <div class="upload-area" style="border: 2px dashed #e0e0e0; padding: 30px; text-align: center; margin-bottom: 20px;">
+                    <div style="margin-bottom: 15px;">
+                        <i class="fa fa-cloud-upload" style="font-size: 50px; color: #ccc;"></i>
+                    </div>
+                    <p style="margin-bottom: 10px;">将文件拖到此处,或 <span style="color: #0066cc; cursor: pointer;" id="clickUpload">点击上传</span></p>
+                    <p style="font-size: 12px; color: #999;">仅允许导入xls、xlsx格式文件。<a href="https://hyscancode.oss-cn-hangzhou.aliyuncs.com/jinzaiimport/%E4%BA%A7%E7%BA%BF%E4%BA%A7%E5%93%81%E5%85%B3%E7%B3%BB%E5%AF%BC%E5%85%A5%E6%A8%A1%E7%89%88.xlsx" style="color: #0066cc;">下载模板</a></p>
+                    <div id="selectedFileName" style="margin-top: 15px; font-size: 12px; color: #333; display: none; justify-content: center; align-items: center;"></div>
+                    <input type="file" id="fileUpload" accept=".xlsx, .xls" style="display: none;">
+                </div>
+                <div style="text-align: center;">
+                    <button type="button" class="btn btn-warning" style="margin-right: 10px;" onclick="uploadFile()">确定</button>
+                    <button type="button" class="btn btn-default" onclick="layer.closeAll()">取消</button>
+                </div>
+            </div>
+        `;
+    
+        layer.open({
+            type: 1,
+            title: '导入',
+            area: ['500px', '350px'],
+            content: importHtml
+        });
+    
+        // 绑定点击上传事件
+        setTimeout(function() {
+            document.getElementById('clickUpload').addEventListener('click', function() {
+                document.getElementById('fileUpload').click();
+            });
+    
+            // 监听文件选择事件
+            document.getElementById('fileUpload').addEventListener('change', function() {
+                const fileName = this.files[0]?.name;
+                const fileNameElement = document.getElementById('selectedFileName');
+                if (fileName) {
+                    // 创建包含文件名和关闭按钮的HTML
+                    fileNameElement.innerHTML =
+                        '<span>已选择文件: ' + fileName + '</span>' +
+                        '<span style="margin-left: 8px; cursor: pointer; color: #ff4444;" id="clearFileSelection">×</span>';
+                    fileNameElement.style.display = 'flex';
+
+                    // 绑定清除按钮事件
+                    document.getElementById('clearFileSelection').addEventListener('click', function() {
+                        // 清除文件选择
+                        const fileInput = document.getElementById('fileUpload');
+                        fileInput.value = '';
+                        
+                        // 隐藏文件名显示
+                        fileNameElement.textContent = '';
+                        fileNameElement.style.display = 'none';
+                    });
+                } else {
+                    fileNameElement.textContent = '';
+                    fileNameElement.style.display = 'none';
+                }
+            });
+    
+            // 实现拖拽上传效果
+            const uploadArea = document.querySelector('.upload-area');
+            uploadArea.addEventListener('dragover', function(e) {
+                e.preventDefault();
+                this.style.borderColor = '#0066cc';
+                this.style.backgroundColor = '#f5f9ff';
+            });
+    
+            uploadArea.addEventListener('dragleave', function() {
+                this.style.borderColor = '#e0e0e0';
+                this.style.backgroundColor = 'transparent';
+            });
+    
+            uploadArea.addEventListener('drop', function(e) {
+                e.preventDefault();
+                this.style.borderColor = '#e0e0e0';
+                this.style.backgroundColor = 'transparent';
+    
+                if (e.dataTransfer.files.length) {
+                    document.getElementById('fileUpload').files = e.dataTransfer.files;
+                    // 触发change事件以更新文件名显示
+                    const event = new Event('change');
+                    document.getElementById('fileUpload').dispatchEvent(event);
+                }
+            });
+        }, 100);
+    }
+    
+    function uploadFile() {
+        const fileInput = document.getElementById('fileUpload');
+        if (!fileInput.files || fileInput.files.length === 0) {
+            layer.msg('请选择要上传的文件', {icon: 2});
+            return;
+        }
+    
+        const formData = new FormData();
+        formData.append('file', fileInput.files[0]);
+    
+        $.ajax({
+            url: '${ctx}/lineProduct/importLineProduct',
+            type: 'POST',
+            data: formData,
+            processData: false,
+            contentType: false,
+            success: function (result) {
+                if (result.code === 0) {
+                    let successCount = result.data.successCount;
+                    let errorCount = result.data.errorCount;
+                    let errorFileUrl = result.data.errorFileUrl;
+                    console.log(result.data);
+                    // 检查是否有错误数据
+                    if (errorCount > 0) {
+                        // 使用模板字符串创建错误信息HTML
+                        const errorHtml =
+                            '<div style="padding: 20px;">' +
+                            '<p style="text-align: center; margin-bottom: 15px;">导入结果:成功 ' + successCount + ' 条,失败 ' + errorCount + ' 条</p>' +
+                            '<div style="text-align: center;">' +
+                            '<a href="' + errorFileUrl + '" target="_blank" class="btn btn-danger">' +
+                            '<i class="fa fa-download"></i> 下载错误数据' +
+                            '</a>' +
+                            '</div>' +
+                            '</div>';
+    
+                        layer.open({
+                            type: 1,
+                            title: '导入结果',
+                            area: ['400px', '200px'],
+                            content: errorHtml,
+                            btn: ['确定'],
+                            yes: function(index) {
+                                layer.close(index);
+                            }
+                        });
+                    } else {
+                        layer.msg(result.msg, {icon: 1});
+                        layer.closeAll();
+                        search(); // 刷新表格
+                    }
+                } else {
+                    layer.msg(result.msg, {icon: 2});
+                }
+            },
+            error: function () {
+                layer.msg('上传失败', {icon: 2});
+            }
+        });
+    }
+
+    function initTable() {
+        table = $('#table').bootstrapTable({
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',    //工具按钮用哪个容器
+            striped: true,      //是否显示行间隔色
+            cache: false,      //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
+            pagination: true,     //是否显示分页(*)
+            pageNumber: 1,      //初始化加载第一页,默认第一页
+            pageSize: 10,      //每页的记录行数(*)
+            pageList: [10, 25, 50, 100],  //可供选择的每页的行数(*)
+            url: '${ctx}/lineProduct/list',//这个接口需要处理bootstrap table传递的固定参数
+            queryParamsType: '', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort
+            // 设置为 ''  在这种情况下传给服务器的参数为:pageSize,pageNumber
+
+            queryParams: queryParams,//前端调用服务时,会默认传递上边提到的参数,如果需要添加自定义参数,可以自定义一个函数返回请求参数
+            sidePagination: "server",   //分页方式:client客户端分页,server服务端分页(*)
+            strictSearch: false,
+            minimumCountColumns: 2,    //最少允许的列数
+            clickToSelect: true,    //是否启用点击选中行
+            searchOnEnterKey: true,
+            idField: "id",
+            // 设置数据格式转换
+            responseHandler: function (res) {
+                // 这里假设接口返回的data就是我们需要的表格数据
+                return {
+                    total: res.total,  // 总记录数
+                    rows: res.records  // 数据列表
+                };
+            },
+            columns: [{
+                title: '序号',
+                align: 'center',
+                formatter: function (value, row, index) {
+                    // 使用this关键字访问表格实例
+                    var pageNumber = this.pageNumber || 1;
+                    var pageSize = this.pageSize || 10;
+                    return (pageNumber - 1) * pageSize + index + 1;
+                },
+                width: "5%"
+            }, {
+                field: 'factoryName',
+                title: '工厂名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'workshopName',
+                title: '车间名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'lineName',
+                title: '产线名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'productName',
+                title: '产品名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                title: '操作',
+                align: 'center',
+                width: "10%",
+                formatter: function (value, row) {
+                    return '<button class="btn btn-primary btn-xs" onclick="editRelation(' + row.id + ')">编辑</button> ' +
+                        '<button class="btn btn-danger btn-xs" onclick="removeRelation(' + row.id + ')">移除</button>';
+                }
+            }],
+            onLoadSuccess: function (data) {
+                console.log("数据加载成功", data);
+            },
+            onLoadError: function () {
+                layer.msg('数据加载失败', {icon: 2});
+            }
+        });
+    }
+</script>
+<script type="text/javascript">
+
+    function isEmpty(obj) {
+        if (obj == undefined || obj == null || obj == '') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+</script>
+</html>

+ 316 - 0
src/main/webapp/page/jinzai/line_product_relation_add.jsp

@@ -0,0 +1,316 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>新增/编辑产线产品关联</title>
+    <!-- 引入Bootstrap CSS -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+
+        .form-group {
+            margin-bottom: 1.5rem; /* 增加字段间距 */
+        }
+
+        /* 自定义表单容器样式 */
+        .form-container {
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+            background-color: #fff;
+        }
+    </style>
+</head>
+<body style="background-color: #f5f7fa; padding: 20px;">
+<div class="form-container">
+    <form id="lineProductForm">
+        <div class="row mb-4">
+            <input type="hidden" id="relationId" name="relationId">
+            <div class="col">
+                <label for="factoryId" class="required">所属工厂</label>
+                <select class="form-select" id="factoryId" name="factoryId" required>
+                    <option value="">请选择工厂</option>
+                </select>
+            </div>
+            <div class="col">
+                <label for="workshopId" class="required">所属车间</label>
+                <select class="form-select" id="workshopId" name="workshopId" required>
+                    <option value="">请选择车间</option>
+                </select>
+            </div>
+        </div>
+
+        <div class="row mb-4">
+            <div class="col">
+                <label for="lineId" class="required">产线名称</label>
+                <select class="form-select" id="lineId" name="lineId" required>
+                    <option value="">请选择产线</option>
+                </select>
+            </div>
+            <div class="col">
+                <label for="productId" class="required">产品名称</label>
+                <select class="form-select" id="productId" name="productId" required>
+                    <option value="">请选择产品</option>
+                </select>
+            </div>
+        </div>
+
+        <div class="mt-4 d-flex justify-content-end gap-3">
+            <button type="button" class="btn btn-secondary" onclick="closeWindow()">取消</button>
+            <button type="submit" class="btn btn-primary">确定</button>
+        </div>
+    </form>
+</div>
+
+<!-- 引入jQuery -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<!-- 引入Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+
+<script>
+    // 在页面加载时检查是否有编辑ID
+    $(document).ready(function() {
+        initValidation();
+        loadFactories();
+        loadProducts();
+
+        // 获取URL中的ID参数
+        const urlParams = new URLSearchParams(window.location.search);
+        const relationId = urlParams.get('id');
+        // 如果存在ID,则加载关联数据
+        if (relationId) {
+            loadRelationData(relationId);
+        }
+    });
+
+    // 加载工厂数据
+    function loadFactories() {
+        $.ajax({
+            url: '${ctx}/lineProduct/getFactoryList',
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const factories = data.data;
+                    const factorySelect = $('#factoryId');
+                    factorySelect.empty().append('<option value="">请选择工厂</option>');
+                    $.each(factories, function(index, factory) {
+                        factorySelect.append('<option value="' + factory.id + '">' + factory.factory_name + '</option>');
+                    });
+                }
+            }
+        });
+    }
+
+    // 加载产品数据
+    function loadProducts() {
+        $.ajax({
+            url: '${ctx}/lineProduct/getProductList',
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const products = data.data;
+                    const productSelect = $('#productId');
+                    productSelect.empty().append('<option value="">请选择产品</option>');
+                    $.each(products, function(index, product) {
+                        productSelect.append('<option value="' + product.id + '">' + product.product_name + '</option>');
+                    });
+                }
+            }
+        });
+    }
+
+    // 根据工厂ID加载车间数据
+    function loadWorkshopsByFactoryId(factoryId) {
+        $.ajax({
+            url: '${ctx}/lineProduct/getWorkshopList',
+            type: 'GET',
+            data: {factoryId: factoryId},
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const workshops = data.data;
+                    const workshopSelect = $('#workshopId');
+                    workshopSelect.empty().append('<option value="">请选择车间</option>');
+                    $.each(workshops, function(index, workshop) {
+                        workshopSelect.append('<option value="' + workshop.id + '">' + workshop.workshop_name + '</option>');
+                    });
+                }
+            }
+        });
+    }
+
+    // 根据车间ID加载产线数据
+    function loadLinesByWorkshopId(workshopId) {
+        $.ajax({
+            url: '${ctx}/lineProduct/getLineList',
+            type: 'GET',
+            data: {workshopId: workshopId},
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const lines = data.data;
+                    const lineSelect = $('#lineId');
+                    lineSelect.empty().append('<option value="">请选择产线</option>');
+                    $.each(lines, function(index, line) {
+                        lineSelect.append('<option value="' + line.id + '">' + line.lineName + '</option>');
+                    });
+                }
+            }
+        });
+    }
+
+    // 监听工厂选择变化
+    $('#factoryId').change(function() {
+        const factoryId = $(this).val();
+        if (factoryId) {
+            loadWorkshopsByFactoryId(factoryId);
+        } else {
+            $('#workshopId').empty().append('<option value="">请选择车间</option>');
+            $('#lineId').empty().append('<option value="">请选择产线</option>');
+        }
+    });
+
+    // 监听车间选择变化
+    $('#workshopId').change(function() {
+        const workshopId = $(this).val();
+        if (workshopId) {
+            loadLinesByWorkshopId(workshopId);
+        } else {
+            $('#lineId').empty().append('<option value="">请选择产线</option>');
+        }
+    });
+
+    // 加载关联数据
+    function loadRelationData(id) {
+        $.ajax({
+            url: '${ctx}/lineProduct/getById?id=' + id,
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const relation = data.data;
+                    // 填充表单数据
+                    $('#relationId').val(relation.id);
+
+                    // 设置所属工厂、车间和产线
+                    setTimeout(function() {
+                        $('#factoryId').val(relation.factoryId);
+                        // 加载对应工厂的车间
+                        loadWorkshopsByFactoryId(relation.factoryId);
+                        // 设置车间
+                        setTimeout(function() {
+                            $('#workshopId').val(relation.workshopId);
+                            // 加载对应车间的产线
+                            loadLinesByWorkshopId(relation.workshopId);
+                            // 设置产线和产品
+                            setTimeout(function() {
+                                $('#lineId').val(relation.lineId);
+                                $('#productId').val(relation.productId);
+                            }, 300);
+                        }, 300);
+                    }, 300);
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function() {
+                layer.msg('加载关联数据失败', {icon: 5});
+            }
+        });
+    }
+
+    // 表单验证
+    function initValidation() {
+        $('#lineProductForm').validate({
+            rules: {
+                factoryId: {
+                    required: true
+                },
+                workshopId: {
+                    required: true
+                },
+                lineId: {
+                    required: true
+                },
+                productId: {
+                    required: true
+                }
+            },
+            messages: {
+                factoryId: {
+                    required: '请选择工厂'
+                },
+                workshopId: {
+                    required: '请选择车间'
+                },
+                lineId: {
+                    required: '请选择产线'
+                },
+                productId: {
+                    required: '请选择产品'
+                }
+            },
+            submitHandler: function(form) {
+                saveRelation();
+            }
+        });
+    }
+
+    // 保存关联信息
+    function saveRelation() {
+        // 获取表单数据
+        const id = $('#relationId').val();
+        const formData = {
+            id: id,
+            factoryId: $('#factoryId').val(),
+            workshopId: $('#workshopId').val(),
+            lineId: $('#lineId').val(),
+            productId: $('#productId').val(),
+        };
+
+        $.ajax({
+            url: id ? '${ctx}/lineProduct/update' : '${ctx}/lineProduct/save',
+            type: 'POST',
+            data: JSON.stringify(formData),
+            contentType: 'application/json',
+            success: function(data) {
+                if (data.code === 0) {
+                    layer.msg('保存成功', {icon: 6});
+                    // 正确关闭弹窗并刷新父页面
+                    var index = parent.layer.getFrameIndex(window.name);
+                    parent.layer.close(index);
+                    // 刷新父页面表格
+                    parent.search();
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function() {
+                layer.msg('保存失败', {icon: 5});
+            }
+        });
+    }
+
+    // 关闭窗口函数
+    function closeWindow() {
+        var index = parent.layer.getFrameIndex(window.name);
+        parent.layer.close(index);
+    }
+</script>
+</body>
+</html>

+ 322 - 0
src/main/webapp/page/jinzai/production_line.jsp

@@ -0,0 +1,322 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>产线列表</title>
+    <meta name="keywords" content="产线管理">
+    <meta name="description" content="产线信息管理页面">
+    <link href="${ctx}/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
+    <link href="${ctx}/css/font-awesome.css?v=4.4.0" rel="stylesheet">
+    <link href="${ctx}/css/bootstrap-select.min.css" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+</head>
+
+<body class="gray-bg">
+<div class="wrapper wrapper-content animated fadeInRight">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="ibox">
+                <div class="ibox-title">
+                    <div class="row">
+                        <div class="col-sm-10">
+                            <h3>产线列表</h3>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="ibox-content">
+                    <div class="row row-lg">
+                        <form class="form-horizontal m-t">
+                            <div id="condition" class="form-group" style="margin-bottom: 0;">
+                                <div class="col-sm-3">
+                                    <label class="col-sm-4 control-label" style="text-align: right;"><span
+                                            class="text-danger"></span>产线名称:</label>
+                                    <div class="col-sm-8">
+                                        <input id="lineName" class="form-control" placeholder="请输入产线名称"/>
+                                    </div>
+                                </div>
+                                <div class="col-sm-3">
+                                    <label class="col-sm-4 control-label" style="text-align: right;"><span
+                                            class="text-danger"></span>所属车间:</label>
+                                    <div class="col-sm-8">
+                                        <select id="workshopId" class="form-control selectpicker" data-live-search="true" title="请选择车间">
+                                        </select>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <button type="button" id="searchBtn" class="btn btn-success"
+                                            onclick="search()">搜索
+                                    </button>
+                                    <button type="button" id="resetBtn" class="btn btn-default">重置</button>
+                                    <button type="button" class="btn btn-primary pull-right" onclick="addProductionLine();">
+                                        <i class="glyphicon glyphicon-plus"></i> 新增
+                                    </button>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="row row-lg">
+                        <div class="col-sm-12">
+                            <table id="table" data-toggle="table" data-mobile-responsive="true">
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 全局js -->
+<script src="${ctx}/js/jquery.min.js?v=2.1.4"></script>
+<script src="${ctx}/js/bootstrap.min.js?v=3.3.6"></script>
+<script src="${ctx}/js/bootstrap-select.min.js"></script>
+<!-- 自定义js -->
+<script src="${ctx}/js/content.js?v=1.0.0"></script>
+<!-- jQuery Validation plugin javascript-->
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table-mobile.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="${ctx}/js/common.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/Math.uuid.js"></script>
+</body>
+<script>
+    var table = null;
+    $(document).ready(function () {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+        $('.selectpicker').selectpicker({
+            liveSearch: true,
+            size: 5,
+            actionsBox: true,
+            selectedTextFormat: 'count > 2'
+        });
+        // 绑定重置按钮点击事件
+        $('#resetBtn').on('click', function() {
+            reset();
+        });
+        // 初始化车间下拉框
+        initWorkshopSelect('');
+    });
+
+    // 加载车间数据
+    function initWorkshopSelect(keyword) {
+        $.ajax({
+            url: '${ctx}/productionLine/getWorkshopListByFactoryId',
+            type: 'POST',
+            data: {keyword: keyword || ''},
+            dataType: 'json',
+            beforeSend: function () {
+                $('#workshopId').prop('disabled', true).selectpicker('refresh');
+            },
+            success: function (res) {
+                $('#workshopId').empty();
+                if (res.data && res.data.length) {
+                    res.data.forEach(item => {
+                        $('#workshopId').append('<option value="' + item.id + '">' + item.workshop_name + '</option>');
+                    });
+                }
+                $('#workshopId').selectpicker('refresh');
+            },
+            error: function (xhr) {
+                $('#workshopId').empty().append('<option value="">加载失败</option>');
+                $('#workshopId').selectpicker('refresh');
+                layer.msg('获取车间数据失败: ' + xhr.statusText);
+            },
+            complete: function () {
+                $('#workshopId').prop('disabled', false).selectpicker('refresh');
+            }
+        });
+    }
+
+    function queryParams(param) {
+        let lineName = $.trim($("#lineName").val());
+        if (lineName) {
+            param['lineName'] = lineName;
+        }
+        let workshopId = $.trim($("#workshopId").val());
+        if (workshopId) {
+            param['workshopId'] = workshopId;
+        }
+        return param;
+    }
+
+    function search() {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reset() {
+        // 清空产线名称输入框
+        $("#lineName").val('');
+        // 完全重置车间下拉框 - 使用多种方法确保UI同步
+        $('#workshopId').val('');
+        $('#workshopId').selectpicker('val', '');
+        $('#workshopId').selectpicker('deselectAll');
+        $('#workshopId').selectpicker('refresh');
+        // 重新搜索(这会重新加载表格数据)
+        search();
+    }
+
+    function addProductionLine() {
+        layer.open({
+            type: 2,
+            title: '新增/编辑产线',
+            area: ['800px', '500px'],
+            content: '${ctx}/page/jinzai/production_line_add.jsp',
+            end: function () {
+                // 关闭弹窗后刷新表格
+                $('#table').bootstrapTable('refresh');
+            }
+        });
+    }
+
+    function editProductionLine(id) {
+        layer.open({
+            type: 2,
+            title: '编辑产线',
+            content: '${ctx}/page/jinzai/production_line_add.jsp?id=' + id,
+            area: ['800px', '500px'],
+            maxmin: true,
+            end: function () {
+                // 关闭弹窗后刷新表格
+                $('#table').bootstrapTable('refresh');
+            }
+        });
+    }
+
+    function deleteProductionLine(id) {
+        layer.confirm('确定要删除该产线吗?此操作不可撤销!', {
+            btn: ['确定', '取消']
+        }, function () {
+            $.ajax({
+                url: '${ctx}/productionLine/delete',
+                type: 'post',
+                data: {
+                    id: id
+                },
+                success: function (result) {
+                    if (result.code === 0) {
+                        layer.msg(result.msg, {icon: 1});
+                        search(); // 刷新表格
+                    } else {
+                        layer.msg(result.msg, {icon: 2});
+                    }
+                },
+                error: function () {
+                    layer.msg('删除失败', {icon: 2});
+                }
+            });
+        });
+    }
+
+    function initTable() {
+        table = $('#table').bootstrapTable({
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',    //工具按钮用哪个容器
+            striped: true,      //是否显示行间隔色
+            cache: false,      //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
+            pagination: true,     //是否显示分页(*)
+            pageNumber: 1,      //初始化加载第一页,默认第一页
+            pageSize: 10,      //每页的记录行数(*)
+            pageList: [10, 25, 50, 100],  //可供选择的每页的行数(*)
+            url: '${ctx}/productionLine/list',//这个接口需要处理bootstrap table传递的固定参数
+            queryParamsType: '', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort
+            // 设置为 ''  在这种情况下传给服务器的参数为:pageSize,pageNumber
+
+            queryParams: queryParams,//前端调用服务时,会默认传递上边提到的参数,如果需要添加自定义参数,可以自定义一个函数返回请求参数
+            sidePagination: "server",   //分页方式:client客户端分页,server服务端分页(*)
+            strictSearch: false,
+            minimumCountColumns: 2,    //最少允许的列数
+            clickToSelect: true,    //是否启用点击选中行
+            searchOnEnterKey: true,
+            idField: "id",
+            // 设置数据格式转换
+            responseHandler: function (res) {
+                // 这里假设接口返回的data就是我们需要的表格数据
+                return {
+                    total: res.total,  // 总记录数
+                    rows: res.records  // 数据列表
+                };
+            },
+            columns: [{
+                title: '序号',
+                align: 'center',
+                formatter: function (value, row, index) {
+                    // 使用this关键字访问表格实例
+                    var pageNumber = this.pageNumber || 1;
+                    var pageSize = this.pageSize || 10;
+                    return (pageNumber - 1) * pageSize + index + 1;
+                },
+                width: "5%"
+            }, {
+                field: 'lineCode',
+                title: '产线编号',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'lineName',
+                title: '产线名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'factoryName',
+                title: '所属工厂',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'workshopName',
+                title: '所属车间',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'status',
+                title: '状态',
+                align: 'center',
+                formatter: function (value) {
+                    if (value === 1) {
+                        return '<span class="label label-success">启用</span>';
+                    } else {
+                        return '<span class="label label-danger">停用</span>';
+                    }
+                },
+                width: "8%"
+            }, {
+                title: '操作',
+                align: 'center',
+                width: "12%",
+                formatter: function (value, row) {
+                    return '<button class="btn btn-primary btn-xs" onclick="editProductionLine(' + row.id + ')">编辑</button> ' +
+                        '<button class="btn btn-danger btn-xs" onclick="deleteProductionLine(' + row.id + ')">删除</button>';
+                }
+            }],
+            onLoadSuccess: function (data) {
+                console.log("数据加载成功", data);
+            },
+            onLoadError: function () {
+                layer.msg('数据加载失败', {icon: 2});
+            }
+        });
+    }
+</script>
+<script type="text/javascript">
+
+    function isEmpty(obj) {
+        if (obj == undefined || obj == null || obj == '') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+</script>
+</html>

+ 371 - 0
src/main/webapp/page/jinzai/production_line_add.jsp

@@ -0,0 +1,371 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>新增/编辑产线</title>
+    <!-- 引入Bootstrap CSS -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+
+        .form-group {
+            margin-bottom: 1.5rem; /* 增加字段间距 */
+        }
+
+        .toggle-switch {
+            position: relative;
+            display: inline-block;
+            width: 60px;
+            height: 34px;
+        }
+
+        .toggle-switch input {
+            opacity: 0;
+            width: 0;
+            height: 0;
+        }
+
+        .slider {
+            position: absolute;
+            cursor: pointer;
+            top: 0;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            background-color: #ccc;
+            transition: .4s;
+            border-radius: 34px;
+        }
+
+        .slider:before {
+            position: absolute;
+            content: "";
+            height: 26px;
+            width: 26px;
+            left: 4px;
+            bottom: 4px;
+            background-color: white;
+            transition: .4s;
+            border-radius: 50%;
+        }
+
+        input:checked + .slider {
+            background-color: #2196F3;
+        }
+
+        input:checked + .slider:before {
+            transform: translateX(26px);
+        }
+
+        /* 自定义表单容器样式 */
+        .form-container {
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+            background-color: #fff;
+        }
+    </style>
+</head>
+<body style="background-color: #f5f7fa; padding: 20px;">
+<div class="form-container">
+    <form id="productionLineForm">
+        <div class="row mb-4">
+            <input type="hidden" id="productionLineId" name="productionLineId">
+            <div class="col">
+                <label for="lineCode" class="required">产线编号</label>
+                <input type="text" class="form-control" id="lineCode" name="lineCode" placeholder="请输入" required>
+                <small class="form-text text-muted">产线编号规则:不超过50个字符</small>
+            </div>
+            <div class="col">
+                <label for="lineName" class="required">产线名称</label>
+                <input type="text" class="form-control" id="lineName" name="lineName" placeholder="请输入" required>
+                <small class="form-text text-muted">名称:不超过50个字符</small>
+            </div>
+        </div>
+
+        <div class="row mb-4">
+            <div class="col">
+                <label for="factoryId" class="required">所属工厂</label>
+                <select class="form-select" id="factoryId" name="factoryId" required>
+                    <option value="">请选择工厂</option>
+                </select>
+            </div>
+            <div class="col">
+                <label for="workshopId" class="required">所属车间</label>
+                <select class="form-select" id="workshopId" name="workshopId" required>
+                    <option value="">请选择车间</option>
+                </select>
+            </div>
+        </div>
+
+        <div class="row mb-4">
+            <div class="col">
+                <label for="contactName">联系人</label>
+                <input type="text" class="form-control" id="contactName" name="contactName" placeholder="请输入">
+            </div>
+            <div class="col">
+                <label for="contactPhone">联系方式</label>
+                <input type="text" class="form-control" id="contactPhone" name="contactPhone" placeholder="请输入">
+            </div>
+        </div>
+
+        <div class="row mb-4">
+            <div class="col">
+                <label for="accountId">关联账户</label>
+                <input type="text" class="form-control" id="accountId" name="accountId" placeholder="请输入">
+            </div>
+            <div class="col">
+                <label for="status" class="required">状态</label>
+                <div class="d-flex align-items-center gap-2">
+                    <label class="toggle-switch">
+                        <input type="checkbox" id="status" name="status" checked>
+                        <span class="slider"></span>
+                    </label>
+                    <span id="statusText">正常</span>
+                    <small class="form-text text-muted">状态:正常、禁用,默认正常</small>
+                </div>
+            </div>
+        </div>
+
+        <div class="mt-4 d-flex justify-content-end gap-3">
+            <!-- 修改取消按钮的onclick事件 -->
+            <button type="button" class="btn btn-secondary" onclick="closeWindow()">取消</button>
+            <button type="submit" class="btn btn-primary">确定</button>
+        </div>
+    </form>
+</div>
+
+<!-- 引入jQuery -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<!-- 引入Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+
+<script>
+    // 在页面加载时检查是否有编辑ID
+    $(document).ready(function() {
+
+
+        initValidation();
+        loadFactories();
+
+
+
+
+        // 获取URL中的ID参数
+        const urlParams = new URLSearchParams(window.location.search);
+        const productionLineId = urlParams.get('id');
+        // 如果存在ID,则加载产线数据
+        if (productionLineId) {
+            loadProductionLineData(productionLineId);
+        }
+    });
+    
+    // 加载工厂数据
+    function loadFactories() {
+        $.ajax({
+            url: '${ctx}/productionLine/getFactoryList',
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const factories = data.data;
+                    const factorySelect = $('#factoryId');
+                    factorySelect.empty().append('<option value="">请选择工厂</option>');
+                    $.each(factories, function(index, factory) {
+                        factorySelect.append('<option value="' + factory.id + '">' + factory.factory_name + '</option>');
+                    });
+                }
+            }
+        });
+    }
+    
+    // 根据工厂ID加载车间数据
+    function loadWorkshopsByFactoryId(factoryId) {
+        $.ajax({
+            url: '${ctx}/productionLine/getWorkshopListByFactoryId',
+            type: 'GET',
+            data: {factoryId: factoryId},
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const workshops = data.data;
+                    const workshopSelect = $('#workshopId');
+                    workshopSelect.empty().append('<option value="">请选择车间</option>');
+                    $.each(workshops, function(index, workshop) {
+                        workshopSelect.append('<option value="' + workshop.id + '">' + workshop.workshop_name + '</option>');
+                    });
+                }
+            }
+        });
+    }
+    
+    // 监听工厂选择变化
+    $('#factoryId').change(function() {
+        const factoryId = $(this).val();
+        if (factoryId) {
+            loadWorkshopsByFactoryId(factoryId);
+        } else {
+            $('#workshopId').empty().append('<option value="">请选择车间</option>');
+        }
+    });
+    
+    // 加载产线数据
+    function loadProductionLineData(id) {
+        $.ajax({
+            url: '${ctx}/productionLine/getById?id=' + id,
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const productionLine = data.data;
+                    // 填充表单数据
+                    $('#productionLineId').val(productionLine.id);
+                    $('#lineCode').val(productionLine.lineCode);
+                    $('#lineName').val(productionLine.lineName);
+                    $('#accountId').val(productionLine.accountId);
+                    $('#contactName').val(productionLine.contactName);
+                    $('#contactPhone').val(productionLine.contactPhone);
+
+                    // 设置状态开关
+                    if (productionLine.status === 1) {
+                        $('#status').prop('checked', true);
+                    } else {
+                        $('#status').prop('checked', false);
+                    }
+                    updateStatusText();
+
+                    // 设置所属工厂和车间
+                    setTimeout(function() {
+                        $('#factoryId').val(productionLine.factoryId);
+                        // 加载对应工厂的车间
+                        loadWorkshopsByFactoryId(productionLine.factoryId);
+                        // 设置车间
+                        setTimeout(function() {
+                            $('#workshopId').val(productionLine.workshopId);
+                        }, 300);
+                    }, 300);
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function() {
+                layer.msg('加载产线数据失败', {icon: 5});
+            }
+        });
+    }
+    
+    // 状态切换按钮
+    const statusToggle = document.getElementById('status');
+    const statusText = document.getElementById('statusText');
+    
+    statusToggle.addEventListener('change', updateStatusText);
+    
+    function updateStatusText() {
+        if (statusToggle.checked) {
+            statusText.textContent = '正常';
+        } else {
+            statusText.textContent = '禁用';
+        }
+    }
+    
+    // 表单验证
+    function initValidation() {
+        $('#productionLineForm').validate({
+            rules: {
+                lineCode: {
+                    required: true,
+                    maxlength: 20
+                },
+                lineName: {
+                    required: true,
+                    maxlength: 100
+                },
+                factoryId: {
+                    required: true
+                },
+                workshopId: {
+                    required: true
+                }
+            },
+            messages: {
+                lineCode: {
+                    required: '请输入产线编号',
+                    maxlength: '产线编号最多20个字符'
+                },
+                lineName: {
+                    required: '请输入产线名称',
+                    maxlength: '产线名称最多100个字符'
+                },
+                factoryId: {
+                    required: '请选择工厂'
+                },
+                workshopId: {
+                    required: '请选择车间'
+                }
+            },
+            submitHandler: function(form) {
+                saveProductionLine();
+            }
+        });
+    }
+    
+    // 保存产线信息
+    function saveProductionLine() {
+        // 获取表单数据
+        const id = $('#productionLineId').val();
+        const formData = {
+            id: id,
+            lineCode: $('#lineCode').val(),
+            lineName: $('#lineName').val(),
+            factoryId: $('#factoryId').val(),
+            workshopId: $('#workshopId').val(),
+            accountId: $('#accountId').val(),
+            contactName: $('#contactName').val(),
+            contactPhone: $('#contactPhone').val(),
+            status: $('#status').is(':checked') ? 1 : 0
+        };
+
+        $.ajax({
+            url: id ? '${ctx}/productionLine/update' : '${ctx}/productionLine/save',
+            type: 'POST',
+            data: JSON.stringify(formData),
+            contentType: 'application/json',
+            success: function(data) {
+                if (data.code === 0) {
+                    layer.msg('保存成功', {icon: 6});
+                    // 正确关闭弹窗并刷新父页面
+                    var index = parent.layer.getFrameIndex(window.name);
+                    parent.layer.close(index);
+                    // 刷新父页面表格
+                    parent.search();
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function() {
+                layer.msg('保存失败', {icon: 5});
+            }
+        });
+    }
+    
+    // 关闭窗口函数
+    function closeWindow() {
+        var index = parent.layer.getFrameIndex(window.name);
+        parent.layer.close(index);
+    }
+</script>
+</body>
+</html>

+ 341 - 0
src/main/webapp/page/jinzai/production_task_upload.jsp

@@ -0,0 +1,341 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+         pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>生产任务上传记录</title>
+    <meta name="keywords" content="生产任务,上传记录">
+    <meta name="description" content="生产任务上传记录管理页面">
+    <!-- 引入Bootstrap CSS -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <link href="${ctx}/css/bootstrap-select.min.css" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+
+        .form-group {
+            margin-bottom: 1.5rem;
+        }
+
+        .form-container {
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+            background-color: #fff;
+        }
+    </style>
+</head>
+
+<body class="gray-bg">
+<div class="wrapper wrapper-content animated fadeInRight">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="ibox">
+                <div class="ibox-title">
+                    <div class="row">
+                        <div class="col-sm-10">
+                            <h3>生产任务上传记录</h3>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="ibox-content">
+                    <div class="row row-lg mb-4">
+                        <div class="col-lg-12">
+                            <form id="searchForm" class="form-horizontal">
+                                <div class="row mb-3">
+                                    <div class="col-sm-3">
+                                        <label for="createTime" class="form-label">创建时间</label>
+                                        <input type="date" class="form-control" id="createTime" name="createTime">
+                                    </div>
+                                    <div class="col-sm-3">
+                                        <label for="factoryName" class="form-label">工厂名称</label>
+                                        <input type="text" class="form-control" id="factoryName" name="factoryName" placeholder="请输入工厂名称">
+                                    </div>
+                                    <div class="col-sm-3">
+                                        <label for="productName" class="form-label">产品名称</label>
+                                        <input type="text" class="form-control" id="productName" name="productName" placeholder="请输入产品名称">
+                                    </div>
+                                    <div class="col-sm-3">
+                                        <label for="wmsStatus" class="form-label">WMS状态</label>
+                                        <select class="form-select" id="wmsStatus" name="wmsStatus">
+                                            <option value="">全部</option>
+                                            <option value="1">成功</option>
+                                            <option value="2">失败</option>
+                                            <option value="3">重试中</option>
+                                        </select>
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="col-sm-12 text-right">
+                                        <button type="button" id="searchBtn" class="btn btn-success ms-2" onclick="search();return false;">
+                                            <i class="fas fa-search"></i> 查询
+                                        </button>
+                                        <button type="button" id="resetBtn" class="btn btn-secondary ms-2" onclick="reset();return false;">
+                                            <i class="fas fa-sync-alt"></i> 重置
+                                        </button>
+                                    </div>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                    <div class="row row-lg mt-3">
+                        <div class="col-sm-12">
+                            <table id="table" data-toggle="table" data-mobile-responsive="true"></table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 全局js -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<!-- 引入Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+<script src="${ctx}/js/bootstrap-select.min.js"></script>
+<!-- jQuery Validation plugin javascript-->
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table-mobile.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="${ctx}/js/common.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/Math.uuid.js"></script>
+<!-- 自定义js -->
+<script src="${ctx}/js/content.js?v=1.0.0"></script>
+</body>
+<script>
+    var table = null;
+
+    $(document).ready(function () {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    });
+
+    function queryParams(param) {
+        // 获取查询参数
+        let createTime = $.trim($("#createTime").val());
+        let factoryName = $.trim($("#factoryName").val());
+        let productName = $.trim($("#productName").val());
+        let wmsStatus = $.trim($("#wmsStatus").val());
+
+        if (createTime) {
+            param['createTime'] = createTime;
+        }
+        if (factoryName) {
+            param['factoryName'] = factoryName;
+        }
+        if (productName) {
+            param['productName'] = productName;
+        }
+        if (wmsStatus) {
+            param['wmsStatus'] = wmsStatus;
+        }
+
+        return param;
+    }
+
+    function search() {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reset() {
+        $("#searchForm")[0].reset();
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reupload(id) {
+        layer.confirm('确定要重传该记录吗?', {
+            btn: ['确定', '取消']
+        }, function () {
+            // 模拟重传操作
+            setTimeout(function() {
+                layer.msg('重传成功', {icon: 6});
+                // 刷新表格
+                search();
+            }, 1000);
+        });
+    }
+
+    function initTable() {
+        // 模拟数据
+        var mockData = generateMockData();
+
+        table = $('#table').bootstrapTable({
+            data: mockData,
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',    //工具按钮用哪个容器
+            striped: true,      //是否显示行间隔色
+            cache: false,      //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
+            pagination: true,     //是否显示分页(*)
+            pageNumber: 1,      //初始化加载第一页,默认第一页
+            pageSize: 10,      //每页的记录行数(*)
+            pageList: [10, 25, 50, 100],  //可供选择的每页的行数(*)
+            queryParamsType: '', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort
+            // 设置为 ''  在这种情况下传给服务器的参数为:pageSize,pageNumber
+            queryParams: queryParams,//前端调用服务时,会默认传递上边提到的参数,如果需要添加自定义参数,可以自定义一个函数返回请求参数
+            sidePagination: "client",   //客户端分页
+            strictSearch: false,
+            minimumCountColumns: 2,    //最少允许的列数
+            clickToSelect: true,    //是否启用点击选中行
+            searchOnEnterKey: true,
+            idField: "id",
+            paginationDetailHAlign: 'right', // 将分页详细信息放在右边
+            columns: [{
+                field: 'checkbox',
+                title: '<input type="checkbox" id="checkAll">',
+                checkbox: true,
+                align: 'center',
+                formatter: function (value, row, index) {
+                    return {
+                        checked: false
+                    };
+                },
+                width: "5%"
+            }, {
+                title: '序号',
+                align: 'center',
+                formatter: function (value, row, index) {
+                    // 使用this关键字访问表格实例
+                    var pageNumber = this.pageNumber || 1;
+                    var pageSize = this.pageSize || 10;
+                    return (pageNumber - 1) * pageSize + index + 1;
+                },
+                width: "5%"
+            }, {
+                field: 'factoryName',
+                title: '工厂名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'lineName',
+                title: '产线名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'batchNumber',
+                title: '批次号',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'transferredToDataCenter',
+                title: '已传到数据中台',
+                align: 'center',
+                formatter: function (value) {
+                    if (value === 1) {
+                        return '<span class="label label-success">是</span>';
+                    } else {
+                        return '<span class="label label-danger">否</span>';
+                    }
+                },
+                width: "10%"
+            }, {
+                field: 'createTime',
+                title: '创建时间',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'wmsStatus',
+                title: 'WMS状态',
+                align: 'center',
+                formatter: function (value) {
+                    if (value === 1) {
+                        return '<span class="badge bg-success">成功</span>';
+                    } else if (value === 2) {
+                        return '<span class="badge bg-danger">失败</span>';
+                    } else if (value === 3) {
+                        return '<span class="badge bg-warning">重试中</span>';
+                    } else {
+                        return '-';
+                    }
+                },
+                width: "10%"
+            }, {
+                field: 'transferredToPlatform',
+                title: '已传到平台',
+                align: 'center',
+                formatter: function (value) {
+                    if (value === 1) {
+                        return '<span class="label label-success">是</span>';
+                    } else {
+                        return '<span class="label label-danger">否</span>';
+                    }
+                },
+                width: "10%"
+            }, {
+                title: '操作',
+                align: 'center',
+                width: "10%",
+                formatter: function (value, row) {
+                    // 只有失败和重试中可以重传
+                    if (row.wmsStatus === 2 || row.wmsStatus === 3) {
+                        return '<button class="btn btn-primary btn-sm" onclick="reupload(' + row.id + ')">重传</button>';
+                    } else {
+                        return '-';
+                    }
+                }
+            }],
+            onLoadSuccess: function (data) {
+                console.log("数据加载成功", data);
+                // 全选框事件
+                $('#checkAll').on('change', function() {
+                    var checked = $(this).prop('checked');
+                    $('#table').bootstrapTable('checkAll');
+                    if (!checked) {
+                        $('#table').bootstrapTable('uncheckAll');
+                    }
+                });
+            },
+            onLoadError: function () {
+                layer.msg('数据加载失败', {icon: 2});
+            }
+        });
+    }
+
+    // 生成模拟数据
+    function generateMockData() {
+        var factories = ['金华工厂', '杭州工厂', '上海工厂', '苏州工厂', '广州工厂'];
+        var lines = ['A线', 'B线', 'C线', 'D线', 'E线'];
+        var products = ['食品A', '食品B', '食品C', '食品D', '食品E'];
+        var mockData = [];
+
+        for (var i = 1; i <= 50; i++) {
+            var wmsStatus = Math.floor(Math.random() * 3) + 1; // 1-3
+            var date = new Date();
+            date.setDate(date.getDate() - Math.floor(Math.random() * 30));
+            var formattedDate = date.toISOString().split('T')[0];
+
+            mockData.push({
+                id: i,
+                factoryName: factories[Math.floor(Math.random() * factories.length)],
+                lineName: '产线' + lines[Math.floor(Math.random() * lines.length)],
+                batchNumber: 'BAT' + Date.now().toString().slice(-6) + i,
+                transferredToDataCenter: Math.floor(Math.random() * 2), // 0或1
+                createTime: formattedDate,
+                wmsStatus: wmsStatus,
+                transferredToPlatform: Math.floor(Math.random() * 2) // 0或1
+            });
+        }
+
+        return mockData;
+    }
+</script>
+</html>

+ 302 - 0
src/main/webapp/page/jinzai/reprint_record.jsp

@@ -0,0 +1,302 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+         pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>补打信息记录</title>
+    <meta name="keywords" content="补打记录,信息管理">
+    <meta name="description" content="补打信息记录管理页面">
+    <!-- 引入Bootstrap CSS -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <link href="${ctx}/css/bootstrap-select.min.css" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+
+        .form-group {
+            margin-bottom: 1.5rem;
+        }
+
+        .form-container {
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+            background-color: #fff;
+        }
+    </style>
+</head>
+
+<body class="gray-bg">
+<div class="wrapper wrapper-content animated fadeInRight">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="ibox">
+                <div class="ibox-title">
+                    <div class="row">
+                        <div class="col-sm-10">
+                            <h3>补打信息记录</h3>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="ibox-content">
+                    <div class="row row-lg mb-4">
+                        <div class="col-lg-12">
+                            <form id="searchForm" class="form-horizontal">
+                                <div class="row mb-3">
+                                    <div class="col-sm-3">
+                                        <label for="reprintTime" class="form-label">补打时间</label>
+                                        <input type="date" class="form-control" id="reprintTime" name="reprintTime">
+                                    </div>
+                                    <div class="col-sm-3">
+                                        <label for="factoryName" class="form-label">工厂名称</label>
+                                        <input type="text" class="form-control" id="factoryName" name="factoryName" placeholder="请输入工厂名称">
+                                    </div>
+                                    <div class="col-sm-3">
+                                        <label for="productName" class="form-label">产品名称</label>
+                                        <input type="text" class="form-control" id="productName" name="productName" placeholder="请输入产品名称">
+                                    </div>
+                                    <div class="col-sm-3" style="display: flex; align-items: flex-end;">
+                                        <button type="button" id="searchBtn" class="btn btn-success ms-2" onclick="search();return false;">
+                                            <i class="fas fa-search"></i> 查询
+                                        </button>
+                                        <button type="button" id="resetBtn" class="btn btn-secondary ms-2" onclick="reset();return false;">
+                                            <i class="fas fa-sync-alt"></i> 重置
+                                        </button>
+                                    </div>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                    <div class="row row-lg mt-3">
+                        <div class="col-sm-12">
+                            <table id="table" data-toggle="table" data-mobile-responsive="true"></table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 全局js -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<!-- 引入Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+<script src="${ctx}/js/bootstrap-select.min.js"></script>
+<!-- jQuery Validation plugin javascript-->
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table-mobile.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="${ctx}/js/common.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/Math.uuid.js"></script>
+<!-- 自定义js -->
+<script src="${ctx}/js/content.js?v=1.0.0"></script>
+</body>
+<script>
+    var table = null;
+
+    $(document).ready(function () {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    });
+
+    function queryParams(param) {
+        // 获取查询参数
+        let reprintTime = $.trim($("#reprintTime").val());
+        let factoryName = $.trim($("#factoryName").val());
+        let productName = $.trim($("#productName").val());
+
+        if (reprintTime) {
+            param['reprintTime'] = reprintTime;
+        }
+        if (factoryName) {
+            param['factoryName'] = factoryName;
+        }
+        if (productName) {
+            param['productName'] = productName;
+        }
+
+        return param;
+    }
+
+    function search() {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reset() {
+        $("#searchForm")[0].reset();
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function initTable() {
+        // 模拟数据
+        var mockData = generateMockData();
+
+        table = $('#table').bootstrapTable({
+            data: mockData,
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',    //工具按钮用哪个容器
+            striped: true,      //是否显示行间隔色
+            cache: false,      //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
+            pagination: true,     //是否显示分页(*)
+            pageNumber: 1,      //初始化加载第一页,默认第一页
+            pageSize: 10,      //每页的记录行数(*)
+            pageList: [10, 25, 50, 100],  //可供选择的每页的行数(*)
+            queryParamsType: '', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort
+            // 设置为 ''  在这种情况下传给服务器的参数为:pageSize,pageNumber
+            queryParams: queryParams,//前端调用服务时,会默认传递上边提到的参数,如果需要添加自定义参数,可以自定义一个函数返回请求参数
+            sidePagination: "client",   //客户端分页
+            strictSearch: false,
+            minimumCountColumns: 2,    //最少允许的列数
+            clickToSelect: true,    //是否启用点击选中行
+            searchOnEnterKey: true,
+            idField: "id",
+            paginationDetailHAlign: 'right', // 将分页详细信息放在右边
+            columns: [{
+                title: '序号',
+                align: 'center',
+                formatter: function (value, row, index) {
+                    // 使用this关键字访问表格实例
+                    var pageNumber = this.pageNumber || 1;
+                    var pageSize = this.pageSize || 10;
+                    return (pageNumber - 1) * pageSize + index + 1;
+                },
+                width: "5%"
+            }, {
+                field: 'productionOrder',
+                title: '生产单号',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'factoryName',
+                title: '工厂名称',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'workshop',
+                title: '生产车间',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'lineName',
+                title: '产线',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'productName',
+                title: '产品名称',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'reprintCount',
+                title: '补打次数',
+                align: 'center',
+                width: "8%"
+            }, {
+                field: 'boxCode',
+                title: '箱码',
+                align: 'center',
+                width: "12%"
+            }, {
+                field: 'palletCode',
+                title: '托码',
+                align: 'center',
+                width: "12%"
+            }, {
+                field: 'reprintTime',
+                title: '补打时间',
+                align: 'center',
+                width: "13%"
+            }],
+            onLoadSuccess: function (data) {
+                console.log("数据加载成功", data);
+            },
+            onLoadError: function () {
+                layer.msg('数据加载失败', {icon: 2});
+            }
+        });
+
+        // 注释:当后端接口准备好后,可以替换为以下代码调用真实接口
+        /*
+        table = $('#table').bootstrapTable({
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',
+            striped: true,
+            cache: false,
+            pagination: true,
+            pageNumber: 1,
+            pageSize: 10,
+            pageList: [10, 25, 50, 100],
+            url: '${ctx}/reprint/list', // 后端接口URL
+            queryParamsType: '',
+            queryParams: queryParams,
+            sidePagination: "server",
+            strictSearch: false,
+            minimumCountColumns: 2,
+            clickToSelect: true,
+            searchOnEnterKey: true,
+            idField: "id",
+            responseHandler: function (res) {
+                return {
+                    total: res.total,
+                    rows: res.records
+                };
+            },
+            columns: [// 与上面相同的列定义...]
+        });
+        */
+    }
+
+    // 生成模拟数据
+    function generateMockData() {
+        var factories = ['金华工厂', '杭州工厂', '上海工厂', '苏州工厂', '广州工厂'];
+        var workshops = ['第一车间', '第二车间', '第三车间', '第四车间', '第五车间'];
+        var lines = ['A线', 'B线', 'C线', 'D线', 'E线'];
+        var products = ['食品A', '食品B', '食品C', '食品D', '食品E', '食品F', '食品G'];
+        var mockData = [];
+
+        for (var i = 1; i <= 50; i++) {
+            var date = new Date();
+            date.setDate(date.getDate() - Math.floor(Math.random() * 30));
+            var formattedDate = date.toISOString().split('T')[0];
+            var time = Math.floor(Math.random() * 24) + ':' + (Math.floor(Math.random() * 60) < 10 ? '0' : '') + Math.floor(Math.random() * 60);
+            var reprintTime = formattedDate + ' ' + time;
+
+            mockData.push({
+                id: i,
+                productionOrder: 'PO' + Date.now().toString().slice(-6) + i,
+                factoryName: factories[Math.floor(Math.random() * factories.length)],
+                workshop: workshops[Math.floor(Math.random() * workshops.length)],
+                lineName: lines[Math.floor(Math.random() * lines.length)],
+                productName: products[Math.floor(Math.random() * products.length)],
+                reprintCount: Math.floor(Math.random() * 5) + 1, // 1-5次
+                boxCode: 'BOX' + Math.floor(Math.random() * 1000000000),
+                palletCode: 'PAL' + Math.floor(Math.random() * 1000000000),
+                reprintTime: reprintTime
+            });
+        }
+
+        return mockData;
+    }
+</script>
+</html>

+ 335 - 0
src/main/webapp/page/jinzai/workshop.jsp

@@ -0,0 +1,335 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>车间列表</title>
+    <meta name="keywords" content="车间管理">
+    <meta name="description" content="车间信息管理页面">
+    <link href="${ctx}/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
+    <link href="${ctx}/css/font-awesome.css?v=4.4.0" rel="stylesheet">
+    <link href="${ctx}/css/bootstrap-select.min.css" rel="stylesheet">
+    <link href="${ctx}/css/plugins/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
+    <link href="${ctx}/css/animate.css" rel="stylesheet">
+    <link href="${ctx}/css/style.css?v=4.1.0" rel="stylesheet">
+</head>
+
+<body class="gray-bg">
+<div class="wrapper wrapper-content animated fadeInRight">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="ibox">
+                <div class="ibox-title">
+                    <div class="row">
+                        <div class="col-sm-10">
+                            <h3>车间列表</h3>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="ibox-content">
+                    <div class="row row-lg">
+                        <form class="form-horizontal m-t">
+                            <div id="condition" class="form-group" style="margin-bottom: 0;">
+                                <div class="col-sm-3">
+                                    <label class="col-sm-4 control-label" style="text-align: right;"><span
+                                            class="text-danger"></span>车间名称:</label>
+                                    <div class="col-sm-8">
+                                        <input id="workshopName" class="form-control" placeholder="请输入车间名称"/>
+                                    </div>
+                                </div>
+                                <div class="col-sm-3">
+                                    <label class="col-sm-4 control-label" style="text-align: right;"><span
+                                            class="text-danger"></span>所属工厂:</label>
+                                    <div class="col-sm-8">
+                                        <select id="factoryId" class="form-control selectpicker" data-live-search="true" title="请选择工厂">
+                                        </select>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <button type="button" id="searchBtn" class="btn btn-success"
+                                            onclick="search()">搜索
+                                    </button>
+                                    <button type="button" id="resetBtn" class="btn btn-default">重置</button>
+                                    <button type="button" class="btn btn-primary pull-right" onclick="addWorkshop();">
+                                        <i class="glyphicon glyphicon-plus"></i> 新增
+                                    </button>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="row row-lg">
+                        <div class="col-sm-12">
+                            <table id="table" data-toggle="table" data-mobile-responsive="true">
+                            </table>
+                        </div>
+                    </div>
+
+
+                </div>
+
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 全局js -->
+<script src="${ctx}/js/jquery.min.js?v=2.1.4"></script>
+<script src="${ctx}/js/bootstrap.min.js?v=3.3.6"></script>
+<script src="${ctx}/js/bootstrap-select.min.js"></script>
+<!-- 自定义js -->
+<script src="${ctx}/js/content.js?v=1.0.0"></script>
+<!-- jQuery Validation plugin javascript-->
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/bootstrap-table-mobile.min.js"></script>
+<script src="${ctx}/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="${ctx}/js/common.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/Math.uuid.js"></script>
+</body>
+<script>
+    var table = null;
+    $(document).ready(function () {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+        $('.selectpicker').selectpicker({
+            liveSearch: true,
+            size: 5,
+            actionsBox: true,
+            selectedTextFormat: 'count > 2'
+        });
+        // 初始化工厂下拉框
+        initFactorySelect('');
+        // 绑定重置按钮点击事件
+        $('#resetBtn').on('click', function() {
+            reset();
+        });
+    });
+
+    // 加载区域数据
+    function initFactorySelect(keyword) {
+        $.ajax({
+            url: '${ctx}/workshop/getFactoryList',
+            type: 'POST',
+            data: {keyword: keyword || ''},
+            dataType: 'json',
+            beforeSend: function () {
+                $('#factoryId').prop('disabled', true).selectpicker('refresh');
+            },
+            success: function (res) {
+                $('#factoryId').empty();
+                if (res.data && res.data.length) {
+                    res.data.forEach(item => {
+                        $('#factoryId').append('<option value="' + item.id + '">' + item.factory_name + '</option>');
+                    });
+                }
+                $('#factoryId').selectpicker('refresh');
+            },
+            error: function (xhr) {
+                $('#factoryId').empty().append('<option value="">加载失败</option>');
+                $('#factoryId').selectpicker('refresh');
+                layer.msg('获取工厂数据失败: ' + xhr.statusText);
+            },
+            complete: function () {
+                $('#factoryId').prop('disabled', false).selectpicker('refresh');
+            }
+        });
+    }
+
+    function queryParams(param) {
+        let workshopName = $.trim($("#workshopName").val());
+        if (workshopName) {
+            param['workshopName'] = workshopName;
+        }
+        let factoryId = $.trim($("#factoryId").val());
+        if (factoryId) {
+            param['factoryId'] = factoryId;
+        }
+        return param;
+    }
+
+    function search() {
+        table = $('#table').bootstrapTable("destroy");
+        initTable();
+    }
+
+    function reset() {
+        // 清空车间名称输入框
+        // 重新初始化表格
+        $("#workshopName").val('');
+        let $factoryId = $('#factoryId');
+        // 完全重置车间下拉框 - 使用多种方法确保UI同步
+        $factoryId.val('');
+        $factoryId.selectpicker('val', '');
+        $factoryId.selectpicker('deselectAll');
+        $factoryId.selectpicker('refresh');
+        // 重新搜索(这会重新加载表格数据)
+        search();
+    }
+
+    function addWorkshop() {
+        layer.open({
+            type: 2,
+            title: '新增/编辑车间',
+            area: ['800px', '500px'],
+            content: '${ctx}/page/jinzai/workshop_add.jsp',
+            end: function () {
+                // 关闭弹窗后刷新表格
+                $('#table').bootstrapTable('refresh');
+            }
+        });
+    }
+
+    function editWorkshop(id) {
+        layer.open({
+            type: 2,
+            title: '编辑车间',
+            content: '${ctx}/page/jinzai/workshop_add.jsp?id=' + id,
+            area: ['800px', '500px'],
+            maxmin: true,
+            end: function () {
+                // 关闭弹窗后刷新表格
+                $('#table').bootstrapTable('refresh');
+            }
+        });
+    }
+
+    function deleteWorkshop(id) {
+        layer.confirm('确定要删除该车间吗?此操作不可撤销!', {
+            btn: ['确定', '取消']
+        }, function () {
+            $.ajax({
+                url: '${ctx}/workshop/delete',
+                type: 'post',
+                data: {
+                    id: id
+                },
+                success: function (result) {
+                    if (result.code === 0) {
+                        layer.msg(result.msg, {icon: 1});
+                        search(); // 刷新表格
+                    } else {
+                        layer.msg(result.msg, {icon: 2});
+                    }
+                },
+                error: function () {
+                    layer.msg('删除失败', {icon: 2});
+                }
+            });
+        });
+    }
+
+    function initTable() {
+        table = $('#table').bootstrapTable({
+            method: 'get',
+            sortable: true,
+            toolbar: '#toolbar',    //工具按钮用哪个容器
+            striped: true,      //是否显示行间隔色
+            cache: false,      //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
+            pagination: true,     //是否显示分页(*)
+            pageNumber: 1,      //初始化加载第一页,默认第一页
+            pageSize: 10,      //每页的记录行数(*)
+            pageList: [10, 25, 50, 100],  //可供选择的每页的行数(*)
+            url: '${ctx}/workshop/list',//这个接口需要处理bootstrap table传递的固定参数
+            queryParamsType: '', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort
+            // 设置为 ''  在这种情况下传给服务器的参数为:pageSize,pageNumber
+
+            queryParams: queryParams,//前端调用服务时,会默认传递上边提到的参数,如果需要添加自定义参数,可以自定义一个函数返回请求参数
+            sidePagination: "server",   //分页方式:client客户端分页,server服务端分页(*)
+            strictSearch: false,
+            minimumCountColumns: 2,    //最少允许的列数
+            clickToSelect: true,    //是否启用点击选中行
+            searchOnEnterKey: true,
+            idField: "id",
+            // 设置数据格式转换
+            responseHandler: function (res) {
+                // 这里假设接口返回的data就是我们需要的表格数据
+                return {
+                    total: res.total,  // 总记录数
+                    rows: res.records  // 数据列表
+                };
+            },
+            columns: [{
+                title: '序号',
+                align: 'center',
+                formatter: function (value, row, index) {
+                    // 使用this关键字访问表格实例
+                    var pageNumber = this.pageNumber || 1;
+                    var pageSize = this.pageSize || 10;
+                    return (pageNumber - 1) * pageSize + index + 1;
+                },
+                width: "5%"
+            }, {
+                field: 'workshopCode',
+                title: '车间编号',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'workshopName',
+                title: '车间名称',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'factoryName',
+                title: '所属工厂',
+                align: 'center',
+                width: "15%"
+            }, {
+                field: 'contactName',
+                title: '联系人',
+                align: 'center',
+                width: "10%",
+                formatter: function (value) {
+                    return value || '-';  // 处理null值
+                }
+            }, {
+                field: 'contactPhone',
+                title: '联系方式',
+                align: 'center',
+                width: "10%"
+            }, {
+                field: 'status',
+                title: '状态',
+                align: 'center',
+                formatter: function (value) {
+                    if (value === 1) {
+                        return '<span class="label label-success">启用</span>';
+                    } else {
+                        return '<span class="label label-danger">停用</span>';
+                    }
+                },
+                width: "8%"
+            }, {
+                title: '操作',
+                align: 'center',
+                width: "12%",
+                formatter: function (value, row) {
+                    return '<button class="btn btn-primary btn-xs" onclick="editWorkshop(' + row.id + ')">编辑</button> ' +
+                        '<button class="btn btn-danger btn-xs" onclick="deleteWorkshop(' + row.id + ')">删除</button>';
+                }
+            }],
+            onLoadSuccess: function (data) {
+                console.log("数据加载成功", data);
+            },
+            onLoadError: function () {
+                layer.msg('数据加载失败', {icon: 2});
+            }
+        });
+    }
+</script>
+<script type="text/javascript">
+
+    function isEmpty(obj) {
+        if (obj == undefined || obj == null || obj == '') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+</script>
+</html>

+ 330 - 0
src/main/webapp/page/jinzai/workshop_add.jsp

@@ -0,0 +1,330 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<c:set value="<%=request.getContextPath()%>" var="ctx"></c:set>
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>新增/编辑车间</title>
+    <!-- 引入Bootstrap CSS -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+
+        .form-group {
+            margin-bottom: 1.5rem; /* 增加字段间距 */
+        }
+
+        .toggle-switch {
+            position: relative;
+            display: inline-block;
+            width: 60px;
+            height: 34px;
+        }
+
+        .toggle-switch input {
+            opacity: 0;
+            width: 0;
+            height: 0;
+        }
+
+        .slider {
+            position: absolute;
+            cursor: pointer;
+            top: 0;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            background-color: #ccc;
+            transition: .4s;
+            border-radius: 34px;
+        }
+
+        .slider:before {
+            position: absolute;
+            content: "";
+            height: 26px;
+            width: 26px;
+            left: 4px;
+            bottom: 4px;
+            background-color: white;
+            transition: .4s;
+            border-radius: 50%;
+        }
+
+        input:checked + .slider {
+            background-color: #2196F3;
+        }
+
+        input:checked + .slider:before {
+            transform: translateX(26px);
+        }
+
+        /* 自定义表单容器样式 */
+        .form-container {
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+            background-color: #fff;
+        }
+    </style>
+</head>
+<body style="background-color: #f5f7fa; padding: 20px;">
+<div class="form-container">
+    <form id="workshopForm">
+        <div class="row mb-4">
+            <input type="hidden" id="workshopId" name="workshopId">
+            <div class="col">
+                <label for="workshopCode" class="required">车间编号</label>
+                <input type="text" class="form-control" id="workshopCode" name="workshopCode" placeholder="请输入" required>
+                <small class="form-text text-muted">车间编号规则:不超过50个字符</small>
+            </div>
+            <div class="col">
+                <label for="workshopName" class="required">车间名称</label>
+                <input type="text" class="form-control" id="workshopName" name="workshopName" placeholder="请输入" required>
+                <small class="form-text text-muted">名称:不超过50个字符</small>
+            </div>
+        </div>
+
+        <div class="row mb-4">
+            <div class="col">
+                <label for="factoryId" class="required">所属工厂</label>
+                <select class="form-select" id="factoryId" name="factoryId" required>
+                    <option value="">请选择工厂</option>
+                </select>
+            </div>
+            <div class="col">
+                <label for="contactPerson" class="required">联系人</label>
+                <input type="text" class="form-control" id="contactPerson" name="contactPerson" placeholder="请输入" required>
+            </div>
+        </div>
+
+        <div class="row mb-4">
+            <div class="col">
+                <label for="contactPhone" class="required">联系方式</label>
+                <input type="text" class="form-control" id="contactPhone" name="contactPhone" placeholder="请输入" required>
+            </div>
+            <div class="col">
+                <label for="status" class="required">状态</label>
+                <div class="d-flex align-items-center gap-2">
+                    <label class="toggle-switch">
+                        <input type="checkbox" id="status" name="status" checked>
+                        <span class="slider"></span>
+                    </label>
+                    <span id="statusText">正常</span>
+                    <small class="form-text text-muted">状态:正常、禁用,默认正常</small>
+                </div>
+            </div>
+        </div>
+
+        <div class="mt-4 d-flex justify-content-end gap-3">
+            <!-- 修改取消按钮的onclick事件 -->
+            <button type="button" class="btn btn-secondary" onclick="closeWindow()">取消</button>
+            <button type="submit" class="btn btn-primary">确定</button>
+        </div>
+    </form>
+</div>
+
+<!-- 引入jQuery -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<!-- 引入Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+<script src="${ctx}/js/plugins/layer/layer.min.js"></script>
+<script src="${ctx}/js/plugins/validate/jquery.validate.min.js"></script>
+<script src="${ctx}/js/plugins/validate/messages_zh.min.js"></script>
+
+<script>
+    // 在页面加载时检查是否有编辑ID
+    $(document).ready(function() {
+        initValidation();
+        loadFactories();
+        
+        // 获取URL中的ID参数
+        const urlParams = new URLSearchParams(window.location.search);
+        const workshopId = urlParams.get('id');
+        // 如果存在ID,则加载车间数据
+        if (workshopId) {
+            loadWorkshopData(workshopId);
+        }
+    });
+    
+    // 添加自定义手机号验证方法
+    $.validator.addMethod("isPhone", function(value, element) {
+        var mobileReg = /^1[3-9]\d{9}$/;
+        return this.optional(element) || mobileReg.test(value);
+    }, "请输入正确的手机号码");
+    
+    // 加载工厂数据
+    function loadFactories() {
+        $.ajax({
+            url: '${ctx}/workshop/getFactoryList',
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const factories = data.data;
+                    const factorySelect = $('#factoryId');
+                    factorySelect.empty().append('<option value="">请选择工厂</option>');
+                    $.each(factories, function(index, factory) {
+                        factorySelect.append('<option value="' + factory.id + '">' + factory.factory_name + '</option>');
+                    });
+                }
+            }
+        });
+    }
+    
+    // 加载车间数据
+    function loadWorkshopData(id) {
+        $.ajax({
+            url: '${ctx}/workshop/getById?id=' + id,
+            type: 'GET',
+            dataType: 'json',
+            success: function(data) {
+                if (data.code === 0) {
+                    const workshop = data.data;
+                    // 填充表单数据
+                    $('#workshopId').val(workshop.id);
+                    $('#workshopCode').val(workshop.workshopCode);
+                    $('#workshopName').val(workshop.workshopName);
+                    $('#contactPerson').val(workshop.contactName);
+                    $('#contactPhone').val(workshop.contactPhone);
+
+                    // 设置状态开关
+                    if (workshop.status === 1) {
+                        $('#status').prop('checked', true);
+                    } else {
+                        $('#status').prop('checked', false);
+                    }
+                    updateStatusText();
+
+                    // 设置所属工厂
+                    setTimeout(function() {
+                        $('#factoryId').val(workshop.factoryId);
+                    }, 300);
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function() {
+                layer.msg('加载车间数据失败', {icon: 5});
+            }
+        });
+    }
+    
+    // 状态切换按钮
+    const statusToggle = document.getElementById('status');
+    const statusText = document.getElementById('statusText');
+    
+    statusToggle.addEventListener('change', updateStatusText);
+    
+    function updateStatusText() {
+        if (statusToggle.checked) {
+            statusText.textContent = '正常';
+        } else {
+            statusText.textContent = '禁用';
+        }
+    }
+    
+    // 表单验证
+    function initValidation() {
+        $('#workshopForm').validate({
+            rules: {
+                workshopCode: {
+                    required: true,
+                    maxlength: 20
+                },
+                workshopName: {
+                    required: true,
+                    maxlength: 100
+                },
+                factoryId: {
+                    required: true
+                },
+                contactPerson: {
+                    required: true,
+                    maxlength: 20
+                },
+                contactPhone: {
+                    required: true,
+                    isPhone: true
+                }
+            },
+            messages: {
+                workshopCode: {
+                    required: '请输入车间编号',
+                    maxlength: '车间编号最多20个字符'
+                },
+                workshopName: {
+                    required: '请输入车间名称',
+                    maxlength: '车间名称最多100个字符'
+                },
+                factoryId: {
+                    required: '请选择工厂'
+                },
+                contactPerson: {
+                    required: '请输入联系人',
+                    maxlength: '联系人最多20个字符'
+                },
+                contactPhone: {
+                    required: '请输入联系电话',
+                    isPhone: '请输入有效的联系电话'
+                }
+            },
+            submitHandler: function(form) {
+                saveWorkshop();
+            }
+        });
+    }
+    
+    // 保存车间信息
+    function saveWorkshop() {
+        // 获取表单数据
+        const id = $('#workshopId').val();
+        const formData = {
+            id: id,
+            workshopCode: $('#workshopCode').val(),
+            workshopName: $('#workshopName').val(),
+            factoryId: $('#factoryId').val(),
+            contactName: $('#contactPerson').val(),
+            contactPhone: $('#contactPhone').val(),
+            status: $('#status').is(':checked') ? 1 : 0
+        };
+
+        $.ajax({
+            url: id ? '${ctx}/workshop/update' : '${ctx}/workshop/save',
+            type: 'POST',
+            data: JSON.stringify(formData),
+            contentType: 'application/json',
+            success: function(data) {
+                if (data.code === 0) {
+                    layer.msg('保存成功', {icon: 6});
+                    // 正确关闭弹窗并刷新父页面
+                    var index = parent.layer.getFrameIndex(window.name);
+                    parent.layer.close(index);
+                    // 刷新父页面表格
+                    parent.search();
+                } else {
+                    layer.msg(data.msg, {icon: 5});
+                }
+            },
+            error: function() {
+                layer.msg('保存失败', {icon: 5});
+            }
+        });
+    }
+    
+    // 关闭窗口函数
+    function closeWindow() {
+        var index = parent.layer.getFrameIndex(window.name);
+        parent.layer.close(index);
+    }
+</script>
+</body>
+</html>

+ 253 - 0
src/main/webapp/page/product/edit.jsp

@@ -0,0 +1,253 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>新增/编辑产品</title>
+    <!-- 引入Bootstrap CSS -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <!-- 引入Font Awesome图标 -->
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <!-- 引入富文本编辑器CSS -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css">
+    <style>
+        .required::before {
+            content: '*';
+            color: red;
+            margin-right: 5px;
+        }
+        .form-group {
+            margin-bottom: 1rem;
+        }
+        .toggle-switch {
+            position: relative;
+            display: inline-block;
+            width: 60px;
+            height: 34px;
+        }
+        .toggle-switch input {
+            opacity: 0;
+            width: 0;
+            height: 0;
+        }
+        .slider {
+            position: absolute;
+            cursor: pointer;
+            top: 0;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            background-color: #ccc;
+            transition: .4s;
+            border-radius: 34px;
+        }
+        .slider:before {
+            position: absolute;
+            content: "";
+            height: 26px;
+            width: 26px;
+            left: 4px;
+            bottom: 4px;
+            background-color: white;
+            transition: .4s;
+            border-radius: 50%;
+        }
+        input:checked + .slider {
+            background-color: #2196F3;
+        }
+        input:checked + .slider:before {
+            transform: translateX(26px);
+        }
+    </style>
+</head>
+<body>
+    <div class="container mt-4">
+        <div class="card">
+            <div class="card-header bg-primary text-white">
+                <h4 class="mb-0">新增/编辑产品</h4>
+            </div>
+            <div class="card-body">
+                <form id="productForm" action="${pageContext.request.contextPath}/product/save" method="post">
+                    <div class="row">
+                        <div class="col-md-6">
+                            <div class="form-group">
+                                <label for="productCode" class="required">产品编号</label>
+                                <input type="text" class="form-control" id="productCode" name="productCode" placeholder="请输入" required>
+                                <small class="form-text text-muted">产品编号规则:不超过50个字符</small>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="productName" class="required">产品名称</label>
+                                <input type="text" class="form-control" id="productName" name="productName" placeholder="请输入" required>
+                                <small class="form-text text-muted">名称:不超过50个字符</small>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="specification">规格</label>
+                                <input type="text" class="form-control" id="specification" name="specification" placeholder="请输入">
+                            </div>
+
+                            <div class="form-group">
+                                <label for="brand" class="required">品牌</label>
+                                <select class="form-select" id="brand" name="brand" required>
+                                    <option value="">请选择品牌</option>
+                                    <!-- 这里可以通过JSTL标签从后端获取品牌列表 -->
+                                    <!-- <c:forEach items="${brands}" var="brand"> -->
+                                    <!--     <option value="${brand.id}">${brand.name}</option> -->
+                                    <!-- </c:forEach> -->
+                                </select>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="barcode">产品条形码</label>
+                                <input type="text" class="form-control" id="barcode" name="barcode" placeholder="请输入">
+                            </div>
+
+                            <div class="form-group">
+                                <label for="status" class="required">状态</label>
+                                <label class="toggle-switch">
+                                    <input type="checkbox" id="status" name="status" checked>
+                                    <span class="slider"></span>
+                                </label>
+                                <span id="statusText" class="ms-2">正常</span>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="boxesPerCarton" class="required">每垛箱数</label>
+                                <input type="number" class="form-control" id="boxesPerCarton" name="boxesPerCarton" placeholder="请输入" required>
+                            </div>
+                        </div>
+
+                        <div class="col-md-6">
+                            <div class="form-group">
+                                <label for="productCategory" class="required">产品类别</label>
+                                <select class="form-select" id="productCategory" name="productCategory" required>
+                                    <option value="">请选择产品类别</option>
+                                    <!-- 这里可以通过JSTL标签从后端获取产品类别列表 -->
+                                    <!-- <c:forEach items="${categories}" var="category"> -->
+                                    <!--     <option value="${category.id}">${category.name}</option> -->
+                                    <!-- </c:forEach> -->
+                                    <!-- 静态选项,实际应用中应从数据库加载 -->
+                                    <option value="1">小鱼干</option>
+                                    <option value="2">手撕肉干</option>
+                                    <option value="3">摸鱼</option>
+                                    <option value="4">手撕素肉</option>
+                                    <option value="5">烤素牛排</option>
+                                    <option value="6">鱿鱼</option>
+                                    <option value="7">蛋白棒</option>
+                                    <option value="8">鹌鹑蛋</option>
+                                    <option value="9">素毛肚</option>
+                                    <option value="10">豆皮</option>
+                                </select>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="unit">单位</label>
+                                <input type="text" class="form-control" id="unit" name="unit" placeholder="请输入">
+                            </div>
+
+                            <div class="form-group">
+                                <label for="packagingLevel" class="required">包装层级</label>
+                                <select class="form-select" id="packagingLevel" name="packagingLevel" required>
+                                    <option value="">请选择包装层级</option>
+                                    <!-- 静态选项,实际应用中应从数据库加载 -->
+                                    <option value="2">二级</option>
+                                    <option value="3">三级</option>
+                                </select>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="packagingRatio" class="required">包装比例</label>
+                                <div class="input-group">
+                                    <input type="text" class="form-control" value="1箱" disabled>
+                                    <input type="number" class="form-control" id="packagingRatio" name="packagingRatio" placeholder="请输入" required>
+                                    <input type="text" class="form-control" value="盒子" disabled>
+                                </div>
+                                <small class="form-text text-muted">包装比例换算:1箱=?盒</small>
+                            </div>
+
+                            <div class="form-group">
+                                <label for="boxesPerCase" class="required">每箱盒数</label>
+                                <input type="number" class="form-control" id="boxesPerCase" name="boxesPerCase" placeholder="请输入" required>
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group mt-4">
+                        <label for="details" class="required">详细信息</label>
+                        <textarea id="details" name="details" rows="10"></textarea>
+                    </div>
+
+                    <div class="mt-4 d-flex justify-content-end gap-3">
+                        <button type="button" class="btn btn-secondary" onclick="window.history.back()">取消</button>
+                        <button type="submit" class="btn btn-primary">确定</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+
+    <!-- 引入jQuery -->
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <!-- 引入Bootstrap JS -->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+    <!-- 引入富文本编辑器JS -->
+    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/lang/summernote-zh-CN.min.js"></script>
+
+    <script>
+        $(document).ready(function() {
+            // 初始化富文本编辑器
+            $('#details').summernote({
+                lang: 'zh-CN',
+                height: 300,
+                toolbar: [
+                    ['style', ['style']],
+                    ['font', ['bold', 'underline', 'clear']],
+                    ['color', ['color']],
+                    ['para', ['ul', 'ol', 'paragraph']],
+                    ['table', ['table']],
+                    ['insert', ['link', 'picture', 'video']],
+                    ['view', ['fullscreen', 'codeview', 'help']]
+                ]
+            });
+
+            // 状态切换按钮
+            const statusToggle = document.getElementById('status');
+            const statusText = document.getElementById('statusText');
+
+            statusToggle.addEventListener('change', function() {
+                if (this.checked) {
+                    statusText.textContent = '正常';
+                } else {
+                    statusText.textContent = '禁用';
+                }
+            });
+
+            // 表单验证
+            document.getElementById('productForm').addEventListener('submit', function(event) {
+                let isValid = true;
+
+                // 产品编号验证
+                const productCode = document.getElementById('productCode').value.trim();
+                if (!productCode || productCode.length > 50) {
+                    alert('产品编号不能为空且不能超过50个字符');
+                    isValid = false;
+                }
+
+                // 产品名称验证
+                const productName = document.getElementById('productName').value.trim();
+                if (!productName || productName.length > 50) {
+                    alert('产品名称不能为空且不能超过50个字符');
+                    isValid = false;
+                }
+
+                if (!isValid) {
+                    event.preventDefault();
+                }
+            });
+        });
+    </script>
+</body>
+</html>