根据文件写出对应的前端后端代码,并说明配置情况和依赖
时间: 2024-11-09 18:19:32 浏览: 16
### 项目概述
该项目旨在为一家健康管理公司开发一个报价系统。该系统可以根据用户选择的不同危害因素,自动生成包括岗前、在岗、离岗三种状态下的体检项目和总报价。项目涉及三个Excel附件:
1. **体检项目价目表**:包含体检项目及其价格。
2. **危害因素**:每种危害因素对应不同的检查项目。
3. **报价模板**:生成最终的报价单。
### 技术栈
- **前端**:HTML + JavaScript
- **后端**:Java + Apache POI
- **依赖管理**:Maven
### 目录结构
```
project-root/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/example/
│ │ │ ├── ExcelHazardGenerator.java
│ │ │ └── ExcelMerge.java
│ │ └── resources/
│ │ └── templates/
│ │ └── index.html
├── pom.xml
└── attachments/
├── file1.xlsx
├── file2.xlsx
└── file3.xlsx
```
### 后端代码
#### `ExcelHazardGenerator.java`
```java
package org.example;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
public class ExcelHazardGenerator {
public static void main(String[] args) throws IOException {
// 定义 Excel 文件路径
String file1Path = "attachments/file1.xlsx";
String file2Path = "attachments/file2.xlsx";
String file3Path = "attachments/file3.xlsx";
// 读取第一个 Excel 文件(项目和单价)
Workbook workbook1 = new XSSFWorkbook(new FileInputStream(file1Path));
Sheet sheet1 = workbook1.getSheetAt(0);
// 读取第二个 Excel 文件(危害因素名称)
Workbook workbook2 = new XSSFWorkbook(new FileInputStream(file2Path));
Sheet sheet2 = workbook2.getSheetAt(0);
// 读取第三个 Excel 文件(报价模板)
Workbook workbook3 = new XSSFWorkbook(new FileInputStream(file3Path));
Sheet sheet3 = workbook3.getSheetAt(0);
// 提取表格 1 中的项目名称和单价
List<String> projectNames = new ArrayList<>();
List<Double> prices = new ArrayList<>();
for (int i = 2; i <= sheet1.getLastRowNum(); i++) {
Row row = sheet1.getRow(i);
if (row != null) {
Cell projectCell = row.getCell(0);
Cell priceCell = row.getCell(2);
if (projectCell != null && priceCell != null && priceCell.getCellType() == CellType.NUMERIC) {
projectNames.add(projectCell.getStringCellValue().trim());
prices.add(priceCell.getNumericCellValue());
}
}
}
// 提取表格 2 中的危害因素名称
List<String> hazardNames = new ArrayList<>();
for (int i = 1; i <= sheet2.getLastRowNum(); i++) {
Row row = sheet2.getRow(i);
if (row != null) {
Cell hazardCell = row.getCell(2);
if (hazardCell != null && hazardCell.getCellType() == CellType.STRING) {
hazardNames.add(hazardCell.getStringCellValue().trim());
}
}
}
// 随机选择 10 到 20 个危害因素
Random random = new Random();
int numHazards = 10 + random.nextInt(11);
Set<Integer> chosenIndexes = new HashSet<>();
while (chosenIndexes.size() < numHazards) {
chosenIndexes.add(random.nextInt(hazardNames.size()));
}
// 从表格 3 的第二行开始写入数据,并计算总价
double totalPrice = 0.0;
int rowIndex = 1;
for (int index : chosenIndexes) {
Row newRow = sheet3.createRow(rowIndex++);
String hazardName = hazardNames.get(index);
newRow.createCell(0).setCellValue(hazardName); // 危害因素名称
newRow.createCell(1).setCellValue("体检项目"); // 项目名称
newRow.createCell(2).setCellValue("单价"); // 单价
newRow.createCell(3).setCellValue("数量"); // 数量
newRow.createCell(4).setCellValue("小计"); // 小计
newRow.createCell(5).setCellValue("备注"); // 备注
totalPrice += 0.0; // 这里需要根据实际项目计算总价
}
// 将总价写入到最后一行的第二列
Row totalRow = sheet3.createRow(rowIndex);
totalRow.createCell(1).setCellValue("岗前总价");
totalRow.createCell(2).setCellValue(totalPrice);
totalRow.createCell(3).setCellValue("在岗总价");
totalRow.createCell(4).setCellValue(totalPrice);
totalRow.createCell(5).setCellValue("离岗总价");
totalRow.createCell(6).setCellValue(totalPrice);
// 保存修改后的 Excel 表格 3
try (FileOutputStream fileOut = new FileOutputStream(file3Path)) {
workbook3.write(fileOut);
}
System.out.println("生成报表成功!");
workbook1.close();
workbook2.close();
workbook3.close();
}
}
```
#### `ExcelMerge.java`
```java
package org.example;
import org.apache.commons.text.similarity.LevenshteinDistance;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
public class ExcelMerge {
private static final int SIMILARITY_THRESHOLD = 3;
private static final Pattern NUMERIC_PATTERN = Pattern.compile("\\d+");
public static void main(String[] args) throws IOException {
// 定义 Excel 文件路径
String file1Path = "attachments/file1.xlsx";
String file2Path = "attachments/file2.xlsx";
// 读取第一个 Excel 文件
Workbook workbook1 = new XSSFWorkbook(new FileInputStream(file1Path));
Sheet sheet1 = workbook1.getSheetAt(0);
// 读取第二个 Excel 文件
Workbook workbook2 = new XSSFWorkbook(new FileInputStream(file2Path));
Sheet sheet2 = workbook2.getSheetAt(0);
// 提取第一个表格的所有项目名称,并存入一个 Set 集合(避免重复)
Set<String> projectSet1 = new HashSet<>();
for (Row row : sheet1) {
Cell cell = row.getCell(0);
if (cell != null) {
projectSet1.add(cell.getStringCellValue().trim());
}
}
// 创建 Levenshtein 算法实例
LevenshteinDistance levenshtein = new LevenshteinDistance();
// 用来存储未找到匹配项目的唯一列表
Set<String> uniqueMissingProjects = new HashSet<>();
// 遍历第二个表格的每一行,查找第 7 列中的项目
for (Row row : sheet2) {
Cell cell = row.getCell(6);
if (cell != null) {
String projectNames = cell.getStringCellValue().trim();
String[] projectArray = projectNames.split(", ");
for (String projectName2 : projectArray) {
projectName2 = projectName2.trim();
if (isNumeric(projectName2)) {
continue;
}
boolean isFound = false;
for (String projectName1 : projectSet1) {
int distance = levenshtein.apply(projectName2, projectName1);
if (distance <= SIMILARITY_THRESHOLD) {
System.out.println("Found similar project: " + projectName2 + " -> " + projectName1);
isFound = true;
break;
}
}
if (!isFound) {
uniqueMissingProjects.add(projectName2);
}
}
}
}
// 将 uniqueMissingProjects 中的唯一项目添加到 sheet1 中
int lastRowNum = sheet1.getLastRowNum();
Random random = new Random();
for (String missingProject : uniqueMissingProjects) {
Row newRow = sheet1.createRow(++lastRowNum);
newRow.createCell(0).setCellValue(missingProject);
int randomPrice = random.nextInt(100) + 1;
newRow.createCell(2).setCellValue(randomPrice);
}
// 保存修改后的第一个文件
try (FileOutputStream fileOut = new FileOutputStream(file1Path)) {
workbook1.write(fileOut);
}
System.out.println("完成!");
workbook1.close();
workbook2.close();
}
// 检查字符串是否为纯数字
private static boolean isNumeric(String str) {
return NUMERIC_PATTERN.matcher(str).matches();
}
}
```
### 前端代码
#### `index.html`
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>体检报价系统</title>
<style>
body { font-family: Arial, sans-serif; }
input[type="text"] { width: 100%; padding: 10px; margin: 10px 0; }
button { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>体检报价系统</h1>
<form id="hazardForm">
<label for="hazards">请输入危害因素(用逗号分隔):</label>
<input type="text" id="hazards" name="hazards" required>
<button type="submit">生成报价</button>
</form>
<table id="quoteTable" style="display:none;">
<thead>
<tr>
<th>危害因素</th>
<th>体检项目</th>
<th>单价</th>
<th>数量</th>
<th>小计</th>
<th>备注</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div id="totalPrices" style="margin-top: 20px;"></div>
<script>
document.getElementById('hazardForm').addEventListener('submit', function(event) {
event.preventDefault();
const hazardsInput = document.getElementById('hazards').value;
const hazards = hazardsInput.split(',').map(h => h.trim());
fetch('/generate-quote', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ hazards })
})
.then(response => response.json())
.then(data => {
displayQuote(data);
})
.catch(error => console.error('Error:', error));
});
function displayQuote(data) {
const quoteTableBody = document.querySelector('#quoteTable tbody');
quoteTableBody.innerHTML = '';
data.items.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.hazard}</td>
<td>${item.project}</td>
<td>${item.price}</td>
<td>${item.quantity}</td>
<td>${item.subtotal}</td>
<td>${item.remarks}</td>
`;
quoteTableBody.appendChild(row);
});
document.getElementById('quoteTable').style.display = 'table';
const totalPricesDiv = document.getElementById('totalPrices');
totalPricesDiv.innerHTML = `
<p>岗前总价: ${data.totalPre}</p>
<p>在岗总价: ${data.totalDuring}</p>
<p>离岗总价: ${data.totalPost}</p>
`;
}
</script>
</body>
</html>
```
### Maven 配置 (`pom.xml`)
```xml
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>price-statement</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Apache POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<!-- Commons Text -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.9</version>
</dependency>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.5</version>
</plugin>
</plugins>
</build>
</project>
```
### 后端 API 接口
#### `QuoteController.java`
```java
package org.example;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class QuoteController {
@PostMapping("/generate-quote")
public Map<String, Object> generateQuote(@RequestBody Map<String, List<String>> request) throws IOException {
List<String> hazards = request.get("hazards");
// 调用 ExcelHazardGenerator 和 ExcelMerge 方法生成报价
// 返回生成的报价数据
return generateQuoteData(hazards);
}
private Map<String, Object> generateQuoteData(List<String> hazards) {
// 这里调用 ExcelHazardGenerator 和 ExcelMerge 的方法生成报价数据
// 示例返回数据
Map<String, Object> result = new HashMap<>();
result.put("items", List.of(
Map.of("hazard", "碳酸钡", "project", "内科常规(职检)", "price", 100.0, "quantity", 1, "subtotal", 100.0, "remarks", ""),
Map.of("hazard", "N-甲基二乙醇胺", "project", "血常规六分类(职检)", "price", 150.0, "quantity",
阅读全文