Java低代码案例

wen java案例 2

本文目录导读:

Java低代码案例

  1. 项目结构
  2. pom.xml
  3. 主启动类
  4. 实体类
  5. Repository
  6. Service层
  7. Controller
  8. 前端页面 (static/index.html)

我将为您提供一个完整的Java低代码平台案例,包含核心功能实现,这个案例将演示如何构建一个简单的低代码应用,让用户通过配置而非编码来创建业务应用。

项目结构

low-code-platform/
├── pom.xml
├── src/main/java/com/example/lowcode/
│   ├── LowCodeApplication.java
│   ├── controller/
│   │   ├── FormController.java
│   │   └── PageController.java
│   ├── service/
│   │   ├── FormService.java
│   │   └── DataService.java
│   ├── entity/
│   │   ├── FormConfig.java
│   │   └── DataRecord.java
│   ├── repository/
│   │   ├── FormConfigRepository.java
│   │   └── DataRecordRepository.java
│   └── config/
│       └── DynamicBeanConfig.java
└── src/main/resources/
    ├── application.yml
    └── static/
        └── index.html

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>com.example</groupId>
    <artifactId>low-code-platform</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <!-- Spring Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Data JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- H2 Database -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- JSON Processing -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

主启动类

package com.example.lowcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LowCodeApplication {
    public static void main(String[] args) {
        SpringApplication.run(LowCodeApplication.class, args);
    }
}

实体类

FormConfig.java

package com.example.lowcode.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Map;
@Data
@Entity
@Table(name = "form_configs")
public class FormConfig {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String formName;
    private String description;
    @Lob
    @Column(columnDefinition = "TEXT")
    private String formSchema; // JSON格式的表单配置
    private String status; // DRAFT, PUBLISHED, ARCHIVED
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = createdAt;
        if (status == null) {
            status = "DRAFT";
        }
    }
    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
}

DataRecord.java

package com.example.lowcode.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Map;
@Data
@Entity
@Table(name = "data_records")
public class DataRecord {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long formConfigId;
    @Lob
    @Column(columnDefinition = "TEXT")
    private String dataJson; // 实际数据
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = createdAt;
    }
    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
}

Repository

FormConfigRepository.java

package com.example.lowcode.repository;
import com.example.lowcode.entity.FormConfig;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface FormConfigRepository extends JpaRepository<FormConfig, Long> {
    List<FormConfig> findByStatus(String status);
    FormConfig findByFormName(String formName);
}

DataRecordRepository.java

package com.example.lowcode.repository;
import com.example.lowcode.entity.DataRecord;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface DataRecordRepository extends JpaRepository<DataRecord, Long> {
    List<DataRecord> findByFormConfigId(Long formConfigId);
    void deleteByFormConfigId(Long formConfigId);
}

Service层

FormService.java

package com.example.lowcode.service;
import com.example.lowcode.entity.FormConfig;
import com.example.lowcode.repository.FormConfigRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class FormService {
    @Autowired
    private FormConfigRepository formConfigRepository;
    @Autowired
    private ObjectMapper objectMapper;
    public FormConfig createForm(String formName, String description, String schemaJson) {
        FormConfig formConfig = new FormConfig();
        formConfig.setFormName(formName);
        formConfig.setDescription(description);
        formConfig.setFormSchema(schemaJson);
        formConfig.setStatus("PUBLISHED");
        // 验证schema格式
        try {
            objectMapper.readTree(schemaJson);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid schema JSON: " + e.getMessage());
        }
        return formConfigRepository.save(formConfig);
    }
    public FormConfig updateForm(Long id, String formName, String description, String schemaJson) {
        Optional<FormConfig> optional = formConfigRepository.findById(id);
        if (optional.isPresent()) {
            FormConfig formConfig = optional.get();
            if (formName != null) {
                formConfig.setFormName(formName);
            }
            if (description != null) {
                formConfig.setDescription(description);
            }
            if (schemaJson != null) {
                // 验证JSON格式
                try {
                    objectMapper.readTree(schemaJson);
                } catch (Exception e) {
                    throw new IllegalArgumentException("Invalid schema JSON: " + e.getMessage());
                }
                formConfig.setFormSchema(schemaJson);
            }
            return formConfigRepository.save(formConfig);
        }
        throw new RuntimeException("Form not found with id: " + id);
    }
    public List<FormConfig> getAllForms() {
        return formConfigRepository.findAll();
    }
    public FormConfig getForm(Long id) {
        return formConfigRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("Form not found with id: " + id));
    }
    @Transactional
    public void deleteForm(Long id) {
        formConfigRepository.deleteById(id);
    }
    public ObjectNode getFormSchemaWithSampleData(Long formId) {
        FormConfig formConfig = getForm(formId);
        ObjectNode result = objectMapper.createObjectNode();
        try {
            JsonNode schema = objectMapper.readTree(formConfig.getFormSchema());
            result.set("schema", schema);
            // 生成示例数据
            ObjectNode sampleData = generateSampleData((ObjectNode) schema);
            result.set("sampleData", sampleData);
        } catch (Exception e) {
            throw new RuntimeException("Error processing form schema: " + e.getMessage());
        }
        return result;
    }
    private ObjectNode generateSampleData(ObjectNode schema) {
        ObjectNode sample = objectMapper.createObjectNode();
        JsonNode fields = schema.get("fields");
        if (fields != null && fields.isArray()) {
            for (JsonNode field : fields) {
                String name = field.get("name").asText();
                String type = field.get("type").asText();
                switch (type) {
                    case "text":
                        sample.put(name, "Sample text");
                        break;
                    case "number":
                        sample.put(name, 123);
                        break;
                    case "date":
                        sample.put(name, "2024-01-15");
                        break;
                    case "select":
                        sample.put(name, "option1");
                        break;
                    case "checkbox":
                        sample.put(name, true);
                        break;
                    default:
                        sample.put(name, "");
                }
            }
        }
        return sample;
    }
}

DataService.java

package com.example.lowcode.service;
import com.example.lowcode.entity.DataRecord;
import com.example.lowcode.entity.FormConfig;
import com.example.lowcode.repository.DataRecordRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class DataService {
    @Autowired
    private DataRecordRepository dataRecordRepository;
    @Autowired
    private FormConfigRepository formConfigRepository;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private FormService formService;
    public DataRecord saveData(Long formConfigId, String dataJson) {
        // 验证表单存在
        FormConfig formConfig = formService.getForm(formConfigId);
        // 验证数据符合schema
        validateData(formConfig, dataJson);
        DataRecord dataRecord = new DataRecord();
        dataRecord.setFormConfigId(formConfigId);
        dataRecord.setDataJson(dataJson);
        return dataRecordRepository.save(dataRecord);
    }
    private void validateData(FormConfig formConfig, String dataJson) {
        try {
            JsonNode data = objectMapper.readTree(dataJson);
            JsonNode schema = objectMapper.readTree(formConfig.getFormSchema());
            JsonNode fields = schema.get("fields");
            if (fields != null && fields.isArray()) {
                for (JsonNode field : fields) {
                    String name = field.get("name").asText();
                    String type = field.get("type").asText();
                    Boolean required = field.has("required") ? field.get("required").asBoolean() : false;
                    JsonNode value = data.get(name);
                    // 必填检查
                    if (required && (value == null || value.isNull())) {
                        throw new IllegalArgumentException("Field '" + name + "' is required");
                    }
                    // 类型检查
                    if (value != null && !value.isNull()) {
                        switch (type) {
                            case "number":
                                if (!value.isNumber()) {
                                    throw new IllegalArgumentException("Field '" + name + "' must be a number");
                                }
                                break;
                            case "date":
                                // 简单验证格式
                                break;
                            case "select":
                                // 验证选项
                                break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            if (e instanceof IllegalStateException) {
                throw new IllegalArgumentException(e.getMessage());
            }
            throw new IllegalArgumentException("Invalid data: " + e.getMessage());
        }
    }
    public List<DataRecord> getDataByFormId(Long formConfigId) {
        return dataRecordRepository.findByFormConfigId(formConfigId);
    }
    public void deleteData(Long dataId) {
        dataRecordRepository.deleteById(dataId);
    }
    public ObjectNode getDataForDisplay(Long formConfigId) {
        FormConfig formConfig = formService.getForm(formConfigId);
        List<DataRecord> records = getDataByFormId(formConfigId);
        ObjectNode result = objectMapper.createObjectNode();
        // 获取字段定义
        try {
            JsonNode schema = objectMapper.readTree(formConfig.getFormSchema());
            result.set("fields", schema.get("fields"));
            // 转换数据为List of Maps
            List<ObjectNode> displayData = new ArrayList<>();
            for (DataRecord record : records) {
                JsonNode data = objectMapper.readTree(record.getDataJson());
                ObjectNode displayRecord = objectMapper.createObjectNode();
                displayRecord.put("id", record.getId());
                displayRecord.set("data", data);
                displayData.add(displayRecord);
            }
            result.set("data", objectMapper.valueToTree(displayData));
        } catch (Exception e) {
            throw new RuntimeException("Error preparing display data: " + e.getMessage());
        }
        return result;
    }
}

Controller

FormController.java

package com.example.lowcode.controller;
import com.example.lowcode.entity.FormConfig;
import com.example.lowcode.service.FormService;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/forms")
@CrossOrigin(origins = "*")
public class FormController {
    @Autowired
    private FormService formService;
    @PostMapping
    public ResponseEntity<FormConfig> createForm(@RequestBody ObjectNode request) {
        String formName = request.get("formName").asText();
        String description = request.has("description") ? request.get("description").asText() : "";
        String schemaJson = request.get("schema").toString();
        FormConfig form = formService.createForm(formName, description, schemaJson);
        return ResponseEntity.ok(form);
    }
    @GetMapping
    public ResponseEntity<List<FormConfig>> getAllForms() {
        return ResponseEntity.ok(formService.getAllForms());
    }
    @GetMapping("/{id}")
    public ResponseEntity<FormConfig> getForm(@PathVariable Long id) {
        return ResponseEntity.ok(formService.getForm(id));
    }
    @PutMapping("/{id}")
    public ResponseEntity<FormConfig> updateForm(@PathVariable Long id, @RequestBody ObjectNode request) {
        String formName = request.has("formName") ? request.get("formName").asText() : null;
        String description = request.has("description") ? request.get("description").asText() : null;
        String schemaJson = request.has("schema") ? request.get("schema").toString() : null;
        FormConfig form = formService.updateForm(id, formName, description, schemaJson);
        return ResponseEntity.ok(form);
    }
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteForm(@PathVariable Long id) {
        formService.deleteForm(id);
        return ResponseEntity.ok().build();
    }
    @GetMapping("/{id}/schema-with-sample")
    public ResponseEntity<ObjectNode> getFormWithSampleData(@PathVariable Long id) {
        return ResponseEntity.ok(formService.getFormSchemaWithSampleData(id));
    }
}

PageController.java

package com.example.lowcode.controller;
import com.example.lowcode.entity.DataRecord;
import com.example.lowcode.service.DataService;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/data")
@CrossOrigin(origins = "*")
public class PageController {
    @Autowired
    private DataService dataService;
    @PostMapping("/{formConfigId}")
    public ResponseEntity<DataRecord> saveData(@PathVariable Long formConfigId, 
                                               @RequestBody ObjectNode request) {
        String dataJson = request.get("data").toString();
        DataRecord savedRecord = dataService.saveData(formConfigId, dataJson);
        return ResponseEntity.ok(savedRecord);
    }
    @GetMapping("/{formConfigId}")
    public ResponseEntity<ObjectNode> getDataForDisplay(@PathVariable Long formConfigId) {
        return ResponseEntity.ok(dataService.getDataForDisplay(formConfigId));
    }
    @DeleteMapping("/record/{dataId}")
    public ResponseEntity<?> deleteData(@PathVariable Long dataId) {
        dataService.deleteData(dataId);
        return ResponseEntity.ok().build();
    }
}

前端页面 (static/index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">低代码平台</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            padding: 30px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
        }
        h1 {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
            font-size: 2.5em;
        }
        .tabs {
            display: flex;
            margin-bottom: 30px;
            border-bottom: 2px solid #eee;
        }
        .tab {
            padding: 10px 30px;
            cursor: pointer;
            border: none;
            background: none;
            font-size: 16px;
            color: #666;
            transition: all 0.3s;
        }
        .tab.active {
            color: #667eea;
            border-bottom: 2px solid #667eea;
            font-weight: bold;
        }
        .tab-form {
            display: none;
        }
        .tab-form.active {
            display: block;
        }
        .form-section {
            margin-bottom: 20px;
        }
        .form-section h2 {
            color: #333;
            margin-bottom: 15px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #555;
        }
        input, textarea, select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
        }
        textarea {
            min-height: 150px;
            resize: vertical;
        }
        button {
            padding: 10px 30px;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            transition: all 0.3s;
            margin-right: 10px;
        }
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
        }
        .btn-secondary {
            background: #f0f0f0;
            color: #333;
        }
        .btn-secondary:hover {
            background: #e0e0e0;
        }
        .btn-danger {
            background: #dc3545;
            color: white;
        }
        .btn-danger:hover {
            background: #c82333;
        }
        .form-card {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 15px;
        }
        .form-card h3 {
            margin-bottom: 10px;
            color: #333;
        }
        .form-card p {
            color: #666;
            margin-bottom: 10px;
        }
        .form-card .date {
            font-size: 12px;
            color: #999;
        }
        .actions {
            margin-top: 15px;
        }
        .dynamic-form {
            margin: 20px 0;
        }
        .dynamic-form .form-group {
            background: #fff;
            padding: 15px;
            border-radius: 8px;
            border: 1px solid #eee;
        }
        .data-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        .data-table th, .data-table td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }
        .data-table th {
            background: #f8f9fa;
            font-weight: bold;
        }
        .data-table tr:hover {
            background: #f5f5f5;
        }
        .json-view {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: monospace;
        }
        .blueprint {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        .blueprint th, .blueprint td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }
        .blueprint th {
            background: #667eea;
            color: white;
        }
        .blueprint tr:nth-child(even) {
            background: #f2f2f2;
        }
        .blueprint tr:hover {
            background: #ddd;
        }
        .status-badge {
            padding: 5px 10px;
            border-radius: 5px;
            font-size: 12px;
            font-weight: bold;
        }
        .status-published {
            background: #d4edda;
            color: #155724;
        }
        .status-draft {
            background: #fff3cd;
            color: #856404;
        }
        .preview-modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            overflow: auto;
        }
        .preview-content {
            background: white;
            margin: 10% auto;
            padding: 30px;
            width: 80%;
            max-width: 700px;
            border-radius: 10px;
        }
        .close {
            color: #aaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }
        .close:hover {
            color: black;
        }
        .sample-data {
            background: #f0f0f0;
            padding: 15px;
            border-radius: 5px;
            margin-top: 15px;
        }
        .sample-data code {
            white-space: pre-wrap;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🚀 低代码平台</h1>
        <!-- Tabs -->
        <div class="tabs">
            <button class="tab active" onclick="switchTab('form-builder')">📝 表单构建器</button>
            <button class="tab" onclick="switchTab('data-management')">🗃️ 数据管理</button>
            <button class="tab" onclick="switchTab('blueprint')">📋 数据蓝图</button>
        </div>
        <!-- 表单构建器 -->
        <div class="tab-form active" id="form-builder">
            <div class="form-section">
                <h2>创建新表单</h2>
                <div class="form-group">
                    <label>表单名称</label>
                    <input type="text" id="form-name" placeholder="请输入表单名称">
                </div>
                <div class="form-group">
                    <label>表单描述</label>
                    <input type="text" id="form-description" placeholder="请输入表单描述">
                </div>
                <div class="form-group">
                    <label>表单Schema (JSON格式)</label>
                    <textarea id="schema-input" placeholder='{
  "fields": [
    {
      "name": "username",
      "label": "用户名",
      "type": "text",
      "required": true
    },
    {
      "name": "age",
      "label": "年龄",
      "type": "number"
    }
  ]
}'></textarea>
                </div>
                <button class="btn-primary" onclick="createForm()">创建表单</button>
                <button class="btn-secondary" onclick="previewSample()">预览示例</button>
            </div>
            <div class="form-section">
                <h2>已创建的表单</h2>
                <div id="form-list"></div>
            </div>
        </div>
        <!-- 数据管理 -->
        <div class="tab-form" id="data-management">
            <div class="form-section">
                <div class="form-group">
                    <label>选择表单</label>
                    <select id="data-form-select" onchange="loadDataForm()">
                        <option value="">请选择要管理数据的表单</option>
                    </select>
                </div>
                <div id="data-entry" style="display: none;">
                    <h2>添加新数据</h2>
                    <form id="data-form"></form>
                    <button class="btn-primary" onclick="submitData()">提交数据</button>
                    <h2>已有数据</h2>
                    <div id="data-records"></div>
                </div>
            </div>
        </div>
        <!-- 数据蓝图 -->
        <div class="tab-form" id="blueprint">
            <h2>数据蓝图</h2>
            <div id="blueprint-content">
                <table class="blueprint">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>表单名称</th>
                            <th>描述</th>
                            <th>状态</th>
                            <th>更新时间</th>
                        </tr>
                    </thead>
                    <tbody id="blueprint-body">
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!-- 预览弹窗 -->
    <div id="preview-modal" class="preview-modal">
        <div class="preview-content">
            <span class="close" onclick="closePreview()">&times;</span>
            <h2>表单预览</h2>
            <div id="preview-content"></div>
        </div>
    </div>
    <script>
        const API_BASE = '/api';
        // 切换标签页
        function switchTab(tabId) {
            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
            document.querySelectorAll('.tab-form').forEach(t => t.classList.remove('active'));
            const clickedTab = document.querySelector(`[onclick="switchTab('${tabId}')"]`);
            clickedTab.classList.add('active');
            document.getElementById(tabId).classList.add('active');
            // 根据标签页加载数据
            if (tabId === 'form-builder') {
                loadForms();
            } else if (tabId === 'data-management') {
                loadFormSelector();
            } else if (tabId === 'blueprint') {
                loadBlueprint();
            }
        }
        // 加载表单列表
        async function loadForms() {
            try {
                const response = await fetch(`${API_BASE}/forms`);
                const forms = await response.json();
                const formList = document.getElementById('form-list');
                formList.innerHTML = '';
                forms.forEach(form => {
                    const card = document.createElement('div');
                    card.className = 'form-card';
                    card.innerHTML = `
                        <h3>${form.formName || '未命名表单'}</h3>
                        <p>${form.description || '无描述'}</p>
                        <p class="date">更新于: ${new Date(form.updatedAt).toLocaleString()}</p>
                        <div class="actions">
                            <span class="status-badge status-${form.status.toLowerCase()}">${form.status}</span>
                            <button class="btn-secondary" onclick="viewSchema(${form.id})">查看Schema</button>
                            <button class="btn-danger" onclick="deleteForm(${form.id})">删除</button>
                        </div>
                    `;
                    formList.appendChild(card);
                });
            } catch (error) {
                console.error('加载表单失败:', error);
            }
        }
        // 创建表单
        async function createForm() {
            const formName = document.getElementById('form-name').value;
            const description = document.getElementById('form-description').value;
            const schemaText = document.getElementById('schema-input').value;
            if (!formName || !schemaText) {
                alert('请填写表单名称和Schema');
                return;
            }
            try {
                const schema = JSON.parse(schemaText);
                const response = await fetch(`${API_BASE}/forms`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        formName: formName,
                        description: description,
                        schema: schema
                    })
                });
                if (response.ok) {
                    alert('表单创建成功!');
                    document.getElementById('form-name').value = '';
                    document.getElementById('form-description').value = '';
                    document.getElementById('schema-input').value = '';
                    loadForms();
                } else {
                    const error = await response.json();
                    alert('创建失败: ' + (error.message || '未知错误'));
                }
            } catch (e) {
                alert('Schema JSON格式错误: ' + e.message);
            }
        }
        // 查看Schema
        async function viewSchema(id) {
            try {
                const response = await fetch(`${API_BASE}/forms/${id}/schema-with-sample`);
                const data = await response.json();
                const modal = document.getElementById('preview-modal');
                const content = document.getElementById('preview-content');
                content.innerHTML = `
                    <h3>Schema 预览</h3>
                    <div class="json-view">${JSON.stringify(data, null, 2)}</div>
                    <h3>示例数据</h3>
                    <div class="sample-data">
                        <code>${JSON.stringify(data.sampleData, null, 2)}</code>
                    </div>
                `;
                modal.style.display = 'block';
            } catch (error) {
                console.error('加载Schema失败:', error);
            }
        }
        // 关闭预览
        function closePreview() {
            document.getElementById('preview-modal').style.display = 'none';
        }
        // 删除表单
        async function deleteForm(id) {
            if (confirm('确定要删除这个表单吗?')) {
                try {
                    await fetch(`${API_BASE}/forms/${id}`, {method: 'DELETE'});
                    loadForms();
                } catch (error) {
                    console.error('删除失败:', error);
                }
            }
        }
        // 加载表单选择器
        async function loadFormSelector() {
            try {
                const response = await fetch(`${API_BASE}/forms`);
                const forms = await response.json();
                const select = document.getElementById('data-form-select');
                select.innerHTML = '<option value="">请选择要管理数据的表单</option>';
                forms.forEach(form => {
                    const option = document.createElement('option');
                    option.value = form.id;
                    option.textContent = form.formName;
                    select.appendChild(option);
                });
                document.getElementById('data-entry').style.display = 'none';
            } catch (error) {
                console.error('加载表单选择器失败:', error);
            }
        }
        // 加载数据管理表单
        async function loadDataForm() {
            const select = document.getElementById('data-form-select');
            const formId = select.value;
            if (!formId) {
                document.getElementById('data-entry').style.display = 'none';
                return;
            }
            document.getElementById('data-entry').style.display = 'block';
            try {
                // 获取表单Schema
                const schemaResponse = await fetch(`${API_BASE}/forms/${formId}/schema-with-sample`);
                const schemaData = await schemaResponse.json();
                // 构建动态表单
                const form = document.getElementById('data-form');
                form.innerHTML = '';
                schemaData.schema.fields.forEach(field => {
                    const formGroup = document.createElement('div');
                    formGroup.className = 'form-group';
                    const label = document.createElement('label');
                    label.textContent = field.label || field.name;
                    label.htmlFor = field.name;
                    formGroup.appendChild(label);
                    let input;
                    if (field.type === 'select') {
                        input = document.createElement('select');
                        input.id = field.name;
                        input.name = field.name;
                        if (field.options) {
                            field.options.forEach(option => {
                                const optionElement = document.createElement('option');
                                optionElement.value = option.value || option;
                                optionElement.textContent = option.label || option;
                                input.appendChild(optionElement);
                            });
                        }
                    } else if (field.type === 'checkbox') {
                        input = document.createElement('input');
                        input.type = 'checkbox';
                        input.id = field.name;
                        input.name = field.name;
                    } else if (field.type === 'number') {
                        input = document.createElement('input');
                        input.type = 'number';
                        input.id = field.name;
                        input.name = field.name;
                    } else if (field.type === 'date') {
                        input = document.createElement('input');
                        input.type = 'date';
                        input.id = field.name;
                        input.name = field.name;
                    } else {
                        input = document.createElement('input');
                        input.type = 'text';
                        input.id = field.name;
                        input.name = field.name;
                    }
                    input.className = 'dynamic-input';
                    if (field.required) input.required = true;
                    formGroup.appendChild(input);
                    form.appendChild(formGroup);
                });
                // 加载已有数据
                loadDataRecords(formId);
            } catch (error) {
                console.error('加载表单数据失败:', error);
            }
        }
        // 提交数据
        async function submitData() {
            const select = document.getElementById('data-form-select');
            const formId = select.value;
            const form = document.getElementById('data-form');
            const data = {};
            form.querySelectorAll('.dynamic-input').forEach(input => {
                if (input.type === 'checkbox') {
                    data[input.name] = input.checked;
                } else {
                    data[input.name] = input.value;
                }
            });
            try {
                const response = await fetch(`${API_BASE}/data/${formId}`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({data: data})
                });
                if (response.ok) {
                    alert('数据保存成功!');
                    form.reset();
                    loadDataRecords(formId);
                } else {
                    const error = await response.json();
                    alert('保存失败: ' + error.message);
                }
            } catch (error) {
                console.error('保存数据失败:', error);
            }
        }
        // 加载数据记录
        async function loadDataRecords(formId) {
            try {
                const response = await fetch(`${API_BASE}/data/${formId}`);
                const data =

抱歉,评论功能暂时关闭!