From 33fe528302300cb4ea8d6f10b6309778ebb6e2a9 Mon Sep 17 00:00:00 2001
From: luxiaotao1123 <t1341870251@163.com>
Date: 星期六, 17 二月 2024 13:33:13 +0800
Subject: [PATCH] #

---
 zy-asrs-framework/src/main/resources/templates/react/Index.txt                        |  350 ++++++++++++++
 zy-asrs-framework/src/main/resources/templates/react/Xml.txt                          |    5 
 zy-asrs-framework/src/main/resources/templates/react/Edit.txt                         |  112 ++++
 zy-asrs-framework/src/main/resources/templates/react/Entity.txt                       |   28 +
 zy-asrs-framework/src/main/resources/templates/react/Mapper.txt                       |   12 
 zy-asrs-framework/src/main/resources/templates/react/Controller.txt                   |  122 +++++
 zy-asrs-framework/src/main/resources/templates/react/ServiceImpl.txt                  |   12 
 zy-asrs-framework/src/main/java/com/zy/asrs/framework/generators/BetterGenerator.java |  769 ++++++++++++++++++++++++++++++++
 zy-asrs-framework/src/main/resources/templates/react/Sql.txt                          |   18 
 zy-asrs-framework/src/main/resources/templates/react/Service.txt                      |    8 
 10 files changed, 1,436 insertions(+), 0 deletions(-)

diff --git a/zy-asrs-framework/src/main/java/com/zy/asrs/framework/generators/BetterGenerator.java b/zy-asrs-framework/src/main/java/com/zy/asrs/framework/generators/BetterGenerator.java
new file mode 100644
index 0000000..82d466f
--- /dev/null
+++ b/zy-asrs-framework/src/main/java/com/zy/asrs/framework/generators/BetterGenerator.java
@@ -0,0 +1,769 @@
+package com.zy.asrs.framework.generators;
+
+import com.zy.asrs.framework.common.Cools;
+import com.zy.asrs.framework.generators.constant.SqlOsType;
+import com.zy.asrs.framework.generators.domain.Column;
+import com.zy.asrs.framework.generators.utils.GeneratorUtils;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.*;
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by vincent on 2019-06-18
+ */
+public class BetterGenerator {
+
+    private static final String BASE_DIR = "src/main/";
+    private static final String JAVA_DIR = BASE_DIR + "java/";
+    private static final String XML_DIR = BASE_DIR + "resources/mapper/";
+    private static final String HTML_DIR = BASE_DIR + "webapp/";
+    private static final String[] ALL_TEMPLATES = new String[]{
+            "Controller",
+            "Service",
+            "ServiceImpl",
+            "Mapper",
+            "Entity",
+            "Xml",
+            "Index",
+            "Edit",
+            "Sql"};
+    private static final ArrayList<String> SYSTEM_MODEL = new ArrayList<String>(){{
+            add("User");
+            add("Host");
+    }};
+
+
+    public String url;
+    public String username;
+    public String password;
+    public String table;
+    public String packagePath;
+    public boolean controller = true;
+    public boolean service = true;
+    public boolean mapper = true;
+    public boolean entity = true;
+    public boolean xml = true;
+    public boolean react = true;
+    public boolean sql = true;
+    public SqlOsType sqlOsType;
+    public String backendPrefixPath;
+    public String frontendPrefixPath;
+
+    private List<Column> columns = new ArrayList<>();
+    private String fullEntityName;
+    private String simpleEntityName;
+    private String entityImport;
+    private String entityContent;
+    private String xmlContent;
+    private String htmlContent;
+    private String htmlDialogContent;
+    private String jsTableContent;
+    private String jsForeignKeyContent;
+    private String jsDateContent;
+    private String jsPrimaryKeyDoms;
+    private String primaryKeyColumn;
+    private String majorColumn;
+    private String systemPackagePath;
+    private String systemPackage;
+    private String itemName;
+
+    public void build() throws Exception {
+        init();
+        for (String template : ALL_TEMPLATES){
+            boolean pass = false;
+            String lowerCase = template.toLowerCase();
+            String templatePath = lowerCase.contains("impl")?lowerCase.substring(0,lowerCase.length()-4)+"/"+lowerCase.substring(lowerCase.length()-4):lowerCase;
+            String directory="";
+            String fileName="";
+            switch (template){
+                case "Controller":
+                    pass = controller;
+                    directory = backendPrefixPath + JAVA_DIR + packagePath.replace(".", "/")+"/"+templatePath+"/";
+                    fileName = fullEntityName+template+".java";
+                    break;
+                case "Service":
+                    pass = service;
+                    directory = backendPrefixPath + JAVA_DIR + packagePath.replace(".", "/")+"/"+templatePath+"/";
+                    fileName = fullEntityName+template+".java";
+                    break;
+                case "ServiceImpl":
+                    pass = service;
+                    directory = backendPrefixPath + JAVA_DIR + packagePath.replace(".", "/")+"/"+templatePath+"/";
+                    fileName = fullEntityName+template+".java";
+                    break;
+                case "Mapper":
+                    pass = mapper;
+                    directory = backendPrefixPath + JAVA_DIR + packagePath.replace(".", "/")+"/"+templatePath+"/";
+                    fileName = fullEntityName+template+".java";
+                    break;
+                case "Entity":
+                    pass = entity;
+                    directory = backendPrefixPath + JAVA_DIR + packagePath.replace(".", "/")+"/"+templatePath+"/";
+                    fileName = fullEntityName+".java";
+                    break;
+                case "Xml":
+                    pass = xml;
+                    directory = backendPrefixPath + XML_DIR + itemName + "/";
+                    fileName = fullEntityName+"Mapper.xml";
+                    break;
+                case "Sql":
+                    pass = sql;
+                    directory = backendPrefixPath + JAVA_DIR;
+                    fileName = simpleEntityName+".sql";
+                    break;
+                case "Index":
+                    pass = react;
+                    directory = frontendPrefixPath + "/src/pages/" + itemName + "/";
+                    fileName = "index.jsx";
+                    break;
+                case "Edit":
+                    pass = react;
+                    directory = frontendPrefixPath + "/src/pages/" + itemName + "/components/";
+                    fileName = "edit.jsx";
+                    break;
+                default:
+                    break;
+            }
+            if (!pass){ continue; }
+            String content = readFile(template);
+            writeFile(content, directory, fileName, template);
+        }
+    }
+
+    private void init() throws Exception {
+        gainDbInfo();
+        fullEntityName = GeneratorUtils.getNameSpace(table);
+        simpleEntityName = fullEntityName.substring(0, 1).toLowerCase()+fullEntityName.substring(1);
+        entityContent = createEntityMsg();
+        htmlContent = createHtmlMsg();
+        htmlDialogContent = createHtmlDialogMsg();
+        jsTableContent = createJsTableMsg();
+        jsForeignKeyContent = createJsFkContent();
+        jsDateContent = createJsDateContent();
+        jsPrimaryKeyDoms = createJsPrimaryKeyMsg();
+        primaryKeyColumn = createPrimaryMsg();
+        majorColumn = createMajorMsg();
+        String[] packagePathSplit = packagePath.split("\\.");
+        systemPackagePath = packagePath.replaceAll(packagePathSplit[packagePathSplit.length-1], "system");
+        String[] split = systemPackagePath.split("\\.");
+        systemPackage = "";
+        for (int i = 1;i <= split.length; i++) {
+            if (i != split.length) {
+                if (i == split.length - 1) {
+                    systemPackage = systemPackage + split[i-1];
+                } else {
+                    systemPackage = systemPackage + split[i-1] + ".";
+                }
+            }
+        }
+        itemName = packagePathSplit[packagePathSplit.length - 1];
+    }
+
+    private String readFile(String template){
+        StringBuilder txtContentBuilder=new StringBuilder();
+        ClassPathResource classPath=new ClassPathResource("templates/react/"+template + ".txt");
+
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(classPath.getInputStream()))) {
+            String lineContent;
+            while ((lineContent = reader.readLine()) != null) {
+                txtContentBuilder.append(lineContent).append("\n");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return txtContentBuilder.toString();
+    }
+
+    private void writeFile(String content, String directory, String fileName, String template) throws IOException {
+        File codeDirectory=new File(directory);
+        if(!codeDirectory.exists()){
+            codeDirectory.mkdirs();
+        }
+
+        File writerFile=new File(directory+fileName);
+        if(!writerFile.exists()){
+            content=content.
+                    replaceAll("@\\{TABLENAME}", table)
+                    .replaceAll("@\\{ENTITYIMPORT}", entityImport)
+                    .replaceAll("@\\{ENTITYCONTENT}", entityContent)
+                    .replaceAll("@\\{ENTITYNAME}", fullEntityName)
+                    .replaceAll("@\\{SIMPLEENTITYNAME}", simpleEntityName)
+                    .replaceAll("@\\{UENTITYNAME}", simpleEntityName)
+                    .replaceAll("@\\{COMPANYNAME}",packagePath)
+                    .replaceAll("@\\{ITEMNAME}",itemName)
+//                    .replaceAll("@\\{XMLCONTENT}", xmlContent)
+                    .replaceAll("@\\{HTMLCONTENT}", htmlContent)
+                    .replaceAll("@\\{HTMLDIALOGCONTENT}", htmlDialogContent)
+                    .replaceAll("@\\{JSTABLECONTENT}", jsTableContent)
+                    .replaceAll("@\\{JSFOREIGNKEYCONTENT}", jsForeignKeyContent)
+                    .replaceAll("@\\{JSDATECONTENT}", jsDateContent)
+                    .replaceAll("@\\{JSPRIMARYKEYDOMS}", jsPrimaryKeyDoms)
+                    .replaceAll("@\\{MAJORCOLUMN}", GeneratorUtils.humpToLine(majorColumn))
+                    .replaceAll("@\\{MAJORCOLUMN0}", GeneratorUtils.firstCharConvert(majorColumn, false))
+                    .replaceAll("@\\{MAJORCOLUMN_UP}", GeneratorUtils.firstCharConvert(majorColumn, false))
+                    .replaceAll("@\\{PRIMARYKEYCOLUMN}", GeneratorUtils.firstCharConvert(primaryKeyColumn, false))
+                    .replaceAll("@\\{PRIMARYKEYCOLUMN0}", GeneratorUtils.firstCharConvert(primaryKeyColumn, true))
+                    .replaceAll("@\\{UPCASEMARJORCOLUMN}", GeneratorUtils.firstCharConvert(primaryKeyColumn, false))
+                    .replaceAll("@\\{SYSTEMPACKAGE}",systemPackage)
+            ;
+            writerFile.createNewFile();
+            BufferedWriter writer=new BufferedWriter(new FileWriter(writerFile));
+            writer.write(content);
+            writer.flush();
+            writer.close();
+            System.out.println(fullEntityName+template+" 婧愭枃浠跺垱寤烘垚鍔燂紒");
+        }else{
+            System.out.println(fullEntityName+template+" 婧愭枃浠跺凡缁忓瓨鍦ㄥ垱寤哄け璐ワ紒");
+        }
+    }
+
+    private void gainDbInfo() throws Exception {
+        Connection conn;
+        if (null == this.sqlOsType) {
+            throw new RuntimeException("璇锋寚瀹氭暟鎹簱绫诲瀷锛�");
+        }
+        switch (this.sqlOsType) {
+            case MYSQL:
+                Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
+                conn = DriverManager.getConnection("jdbc:mysql://"+url, username, password);
+                this.columns = getMysqlColumns(conn, table, true, sqlOsType);
+                break;
+            case SQL_SERVER:
+                Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance();
+                conn = DriverManager.getConnection("jdbc:sqlserver://"+url, username, password);
+                this.columns = getSqlServerColumns(conn, table, true, sqlOsType);
+                break;
+            default:
+                throw new RuntimeException("璇锋寚瀹氭暟鎹簱绫诲瀷锛�");
+        }
+
+    }
+
+    // mysql
+    public static List<Column> getMysqlColumns(Connection conn, String table, boolean init, SqlOsType sqlOsType) throws Exception {
+        List<Column> result = new ArrayList<>();
+        PreparedStatement ps = conn.prepareStatement("select * from " + table);
+        ResultSetMetaData meta = ps.executeQuery().getMetaData();
+        // 鍗曡〃瀛楁鏁伴噺
+        int count = meta.getColumnCount();
+        ResultSet resultSet = ps.executeQuery("show full columns from " + table);
+        for (int i = 1; i < count + 1; i++) {
+            String columnName = meta.getColumnName(i);
+            if (resultSet.next() && columnName.equals(resultSet.getString("Field"))){
+                result.add(new Column(
+                        conn,
+                        meta.getColumnName(i),
+                        GeneratorUtils.getType(meta.getColumnType(i)),
+                        resultSet.getString("Comment"),
+                        resultSet.getString("Key").equals("PRI"),
+                        resultSet.getString("Key").equals("PRI"),
+                        resultSet.getString("Null").equals("NO"),
+                        GeneratorUtils.getColumnLength(resultSet.getString("Type")),
+                        init,
+                        sqlOsType
+                ));
+            }
+            result.forEach(column -> System.out.println(column.toString()));
+        }
+        return result;
+    }
+
+    // sqlserver
+    public static List<Column> getSqlServerColumns(Connection conn, String table, boolean init, SqlOsType sqlOsType) throws Exception {
+        List<Column> result = new ArrayList<>();
+        PreparedStatement ps = conn.prepareStatement("select * from " + table);
+        ResultSetMetaData meta = ps.executeQuery().getMetaData();
+        // 鍗曡〃瀛楁鏁伴噺
+        int count = meta.getColumnCount();
+        StringBuilder sql = new StringBuilder("SELECT \n" +
+                "       'Field'= a.name,\n" +
+                "       'Comment'= isnull(g.[value],''),\n" +
+                "       'Key'= case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then 'PRI' else '' end,\n" +
+                "       'Main'= case when exists(SELECT 1 FROM sysobjects where xtype='PK' and parent_obj=a.id and name in (SELECT name FROM sysindexes WHERE indid in( SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid))) then 'PRI' else '' end,"+
+                "       'Type'= b.name,\n" +
+                "       'Length'= COLUMNPROPERTY(a.id,a.name,'PRECISION'),\n" +
+                "       'Decimals'= isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0),\n" +
+                "       'Null'= case when a.isnullable=1 then 'Yes' else 'No' end,\n" +
+                "       'Default' = isnull(e.text,'')\n" +
+                "FROM  syscolumns a\n" +
+                "LEFT JOIN  systypes b on a.xusertype=b.xusertype\n" +
+                "INNER JOIN sysobjects d on  a.id=d.id  and d.xtype='U' and  d.name<>'dtproperties'\n" +
+                "LEFT JOIN  syscomments e on  a.cdefault=e.id\n" +
+                "LEFT JOIN  sys.extended_properties g on  a.id=G.major_id and a.colid=g.minor_id  \n" +
+                "LEFT JOIN  sys.extended_properties f on  d.id=f.major_id and f.minor_id=0 where d.name = '")
+                .append(table).append("' ORDER BY a.colorder ASC");
+        ResultSet resultSet = conn.prepareStatement(sql.toString()).executeQuery();
+        for (int i = 1; i < count + 1; i++) {
+            String columnName = meta.getColumnName(i);
+            if (resultSet.next() && columnName.equals(resultSet.getString("Field"))){
+                result.add(new Column(
+                        conn,
+                        meta.getColumnName(i),
+                        GeneratorUtils.getType(meta.getColumnType(i)),
+                        resultSet.getString("Comment"),
+                        resultSet.getString("Key").equals("PRI"),
+                        resultSet.getString("Main").equals("PRI"),
+                        resultSet.getString("Null").equals("No"),
+                        GeneratorUtils.getColumnLength(resultSet.getString("Type")),
+                        init,
+                        sqlOsType
+                ));
+            }
+        }
+        result.forEach(column -> System.out.println(column.toString()));
+        return result;
+    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    /**********************************************************************************************/
+    /************************************* Entity鍔ㄦ�佸瓧娈� *******************************************/
+    /**********************************************************************************************/
+
+    private String createEntityMsg(){
+        if (Cools.isEmpty(systemPackagePath)) {
+            String[] packagePathSplit = packagePath.split("\\.");
+            systemPackagePath = packagePath.replaceAll(packagePathSplit[packagePathSplit.length-1], "system");
+        }
+        if (columns.isEmpty()){
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        StringBuilder entityIm = new StringBuilder();
+        boolean setTableField = true;
+        boolean setTableId = true;
+        boolean setDateTimeFormat = true;
+        for (Column column : columns){
+            if (column.getType().equals("Date")){
+                entityIm.append("import java.text.SimpleDateFormat;\n")
+                        .append("import java.util.Date;\n");
+            }
+
+            // 娉ㄩ噴
+            if (!Cools.isEmpty(column.getComment())){
+                sb.append("    /**\n")
+                        .append("     * ")
+                        .append(column.getWholeComment())
+                        .append("\n")
+                        .append("     */")
+                        .append("\n");
+            }
+
+            // swagger
+            sb.append("    @ApiModelProperty(value= \"")
+                    .append(column.getWholeComment())
+                    .append("\")\n");
+
+
+            // 涓婚敭淇グ
+            if (column.isMainKey()){
+                if (column.isOnly()){
+                    sb.append("    ")
+                            .append("@TableId(value = \"")
+                            .append(column.getName())
+                            .append("\", type = IdType.AUTO)")
+                            .append("\n");
+                } else {
+                    sb.append("    ")
+                            .append("@TableId(value = \"")
+                            .append(column.getName())
+                            .append("\", type = IdType.INPUT)")
+                            .append("\n");
+                }
+
+            }
+
+            if (column.getName().equals("deleted")) {
+                entityIm.append("import com.baomidou.mybatisplus.annotation.TableLogic;\n");
+                sb.append("    ")
+                        .append("@TableLogic\n");
+            }
+
+            if ("Date".equals(column.getType())){
+                if (setDateTimeFormat){
+                    entityIm.append("import org.springframework.format.annotation.DateTimeFormat;").append("\n");
+                    setDateTimeFormat = false;
+                }
+                sb.append("    ")
+                        .append("@DateTimeFormat(pattern=\"yyyy-MM-dd HH:mm:ss\")")
+                        .append("\n");
+            }
+
+            sb.append("    ")
+                    .append("private ")
+                    .append(column.getType())
+                    .append(" ")
+                    .append(column.getHumpName())
+                    .append(";")
+                    .append("\n")
+                    .append("\n");
+        }
+
+        // default constructor
+        sb.append("    public ").append(fullEntityName).append("() {}\n\n");
+        // full constructor
+        sb.append("    public ").append(fullEntityName).append("(");
+        for (Column column : columns){
+            if (column.isOnly()){ continue;}
+            sb.append(column.getType()).append(" ").append(column.getHumpName()).append(",");
+        }
+        sb.deleteCharAt(sb.length()-1);
+        sb.append(") {\n");
+        for (Column column : columns){
+            if (column.isPrimaryKey()){ continue;}
+            sb.append("        this.").append(column.getHumpName()).append(" = ").append(column.getHumpName()).append(";\n");
+        }
+        sb.append("    }\n\n");
+        // constructor tips
+        sb.append("//    ").append(fullEntityName).append(" ").append(simpleEntityName).append(" = new ").append(fullEntityName).append("(\n");
+        for (int i = 0; i<columns.size(); i++) {
+            if (columns.get(i).isOnly()){ continue;}
+            sb.append("//            null");
+            if (i < columns.size()-1){
+                sb.append(",");
+            }
+            sb.append("    // ").append(columns.get(i).getComment()).append(columns.get(i).isNotNull()?"[闈炵┖]":"");
+            if (i < columns.size()-1){
+                sb.append("\n");
+            }
+        }
+        sb.append("\n//    );\n\n");
+
+        // get set
+        for (Column column : columns){
+            // 鏃堕棿瀛楁澧炲姞$鏍煎紡鍖�
+            if ("Date".equals(column.getType())){
+                sb.append("    public String get")
+                        .append(column.getHumpName().substring(0, 1).toUpperCase()).append(column.getHumpName().substring(1))
+                        .append("\\$")
+                        .append("(){\n")
+                        .append("        if (Cools.isEmpty(this.").append(column.getHumpName()).append(")){\n")
+                        .append("            return \"\";\n")
+                        .append("        }\n")
+                        .append("        return new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(this.")
+                        .append(column.getHumpName())
+                        .append(");\n")
+                        .append("    }\n\n");
+                // 鏋氫妇瀛楁澧炲姞$鏍煎紡鍖�
+            } else if (!Cools.isEmpty(column.getEnums())){
+                sb.append("    public String get")
+                        .append(column.getHumpName().substring(0, 1).toUpperCase()).append(column.getHumpName().substring(1))
+                        .append("\\$")
+                        .append("(){\n")
+                        .append("        if (null == this.").append(column.getHumpName()).append("){ return null; }\n")
+                        .append("        switch (this.").append(column.getHumpName()).append("){\n");
+                for (Map<String, Object> map : column.getEnums()){
+                    for (Map.Entry<String, Object> entry : map.entrySet()){
+                        sb.append("            case ").append(entry.getKey()).append(":\n")
+                                .append("                return \"").append(entry.getValue()).append("\";\n");
+                    }
+                }
+                sb.append("            default:\n")
+                        .append("                return String.valueOf(this.").append(column.getHumpName()).append(");\n")
+                        .append("        }\n")
+                        .append("    }\n\n");
+            }
+
+            // 澶栭敭淇グ
+            if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                sb.append("    public String get").append(column.getHumpName().substring(0, 1).toUpperCase()).append(column.getHumpName().substring(1)).append("\\$").append("(){\n")
+                        .append("        ").append(column.getForeignKey()).append("Service service = SpringUtils.getBean(").append(column.getForeignKey()).append("Service.class);\n")
+                        .append("        ").append(column.getForeignKey()).append(" ").append(GeneratorUtils.firstCharConvert(column.getForeignKey()))
+                        .append(" = service.getById(this.").append(column.getHumpName()).append(");\n")
+                        .append("        if (!Cools.isEmpty(").append(GeneratorUtils.firstCharConvert(column.getForeignKey())).append(")){\n")
+                        .append("            return String.valueOf(").append(GeneratorUtils.firstCharConvert(column.getForeignKey())).append(".get").append(column.getForeignKeyMajor()).append("());\n")
+                        .append("        }\n")
+                        .append("        return null;\n")
+                        .append("    }\n\n");
+            }
+
+        }
+        entityImport = entityIm.toString();
+        return sb.toString();
+    }
+
+    /**********************************************************************************************/
+    /*********************************** Controller鍔ㄦ�佸瓧娈� *****************************************/
+    /**********************************************************************************************/
+
+    private String createPrimaryMsg(){
+        String defaultMajor = "id";
+        boolean havePrimary = false;
+        for (Column column: columns){
+            if (column.isPrimaryKey()){
+                defaultMajor = column.getHumpName();
+                havePrimary = true;
+            }
+        }
+        if (!havePrimary) {
+            for (Column column: columns){
+                if (column.isMainKey()){
+                    defaultMajor = column.getHumpName();
+                }
+            }
+        }
+        return defaultMajor;
+    }
+
+    private String createMajorMsg(){
+        String defaultMajor = "id";
+        for (Column column: columns){
+            if (column.isPrimaryKey()){
+                defaultMajor = column.getHumpName();
+            }
+            if (column.isMajor()){
+                return column.getHumpName();
+            }
+        }
+        return defaultMajor;
+    }
+
+    /**********************************************************************************************/
+    /************************************** Html鍔ㄦ�佸瓧娈� *******************************************/
+    /**********************************************************************************************/
+
+    private String createHtmlMsg(){
+        StringBuilder sb = new StringBuilder();
+        for (Column column : columns){
+            if (column.isPrimaryKey()){ continue;}
+            if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                sb.append("    <div class=\"layui-inline\">\n")
+                        .append("        <div class=\"layui-input-inline cool-auto-complete\">\n")
+                        .append("            <input id=\"").append(column.getHumpName()).append("\"")
+                        .append(" class=\"layui-input\" name=\"").append(column.getName()).append("\" type=\"text\" placeholder=\"璇疯緭鍏" autocomplete=\"off\" style=\"display: none\">\n")
+                        .append("            <input id=\"").append(column.getHumpName()).append("\\$")
+                        .append("\" class=\"layui-input cool-auto-complete-div\" onclick=\"autoShow(this.id)\" type=\"text\" placeholder=\"").append(GeneratorUtils.supportHtmlName(column.getComment())).append("\" onfocus=this.blur()>\n")
+                        .append("            <div class=\"cool-auto-complete-window\">\n")
+                        .append("                <input class=\"cool-auto-complete-window-input\" data-key=\"").append(GeneratorUtils.firstCharConvert(column.getForeignKey())).append("Query").append("By").append(column.getHumpName()).append("\" onkeyup=\"autoLoad(this.getAttribute('data-key'))\">\n")
+                        .append("                <select class=\"cool-auto-complete-window-select\" data-key=\"").append(GeneratorUtils.firstCharConvert(column.getForeignKey())).append("Query").append("By").append(column.getHumpName()).append("Select\" onchange=\"confirmed(this.getAttribute('data-key'))\" multiple=\"multiple\">\n")
+                        .append("                </select>\n")
+                        .append("            </div>\n")
+                        .append("        </div>\n")
+                        .append("    </div>\n");
+            }
+        }
+        return sb.toString();
+    }
+
+    private String createHtmlDialogMsg() {
+        StringBuilder sb = new StringBuilder();
+        for (Column column : columns){
+            if (column.isPrimaryKey()) {
+                continue;
+            }
+            sb.append("                <div class=\"layui-form-item\">\n");
+            sb.append("                    <label class=\"layui-form-label");
+            if (column.isNotNull()){
+                sb.append(" layui-form-required");
+            }
+            sb.append("\">").append(column.getComment()).append(": </label>\n");
+            sb.append("                    <div class=\"layui-input-block");
+            // 鍏宠仈澶栭敭
+            if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                sb.append(" cool-auto-complete");
+            }
+            sb.append("\">\n");
+            // 杈撳叆妗嗙被鍨�
+            if (Cools.isEmpty(column.getEnums())){
+                sb.append("                        <input class=\"layui-input\" name=\"").append(column.getHumpName());
+                if ("Date".equals(column.getType())){
+                    sb.append("\" id=\"").append(column.getHumpName()).append("\\$");
+                }
+                sb.append("\" placeholder=\"璇疯緭鍏�").append(column.getComment()).append("\"");
+                if (column.isNotNull()){
+                    sb.append(" lay-vertype=\"tips\" lay-verify=\"required\"");
+                }
+                // 鍏宠仈澶栭敭
+                if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                    sb.append(" style=\"display: none\"");
+                }
+                sb.append(">\n");
+
+                // 鍏宠仈澶栭敭
+                if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                    sb.append("                        <input id=\"").append(column.getHumpName()).append("\\$").append("\" name=\"").append(column.getHumpName()).append("\\$")
+                            .append("\" class=\"layui-input cool-auto-complete-div\" onclick=\"autoShow(this.id)\" type=\"text\" placeholder=\"璇疯緭鍏�").append(column.getComment()).append("\" onfocus=this.blur()>\n");
+                    sb.append("                        <div class=\"cool-auto-complete-window\">\n")
+                            .append("                            <input class=\"cool-auto-complete-window-input\" data-key=\"")
+                            .append(GeneratorUtils.firstCharConvert(column.getForeignKey())).append("Query").append("By").append(column.getHumpName()).append("\" onkeyup=\"autoLoad(this.getAttribute('data-key'))\">\n")
+                            .append("                            <select class=\"cool-auto-complete-window-select\" data-key=\"").append(GeneratorUtils.firstCharConvert(column.getForeignKey())).append("Query").append("By").append(column.getHumpName()).append("Select\" onchange=\"confirmed(this.getAttribute('data-key'))\" multiple=\"multiple\">\n")
+                            .append("                            </select>\n")
+                            .append("                        </div>\n");
+                }
+            // 鏋氫妇绫诲瀷
+            } else {
+                sb.append("                        <select name=\"").append(column.getHumpName()).append("\"");
+                if (column.isNotNull()){
+                    sb.append(" lay-vertype=\"tips\" lay-verify=\"required\"");
+                }
+                sb.append(">\n");
+                sb.append("                            <option value=\"\">").append("璇烽�夋嫨").append(column.getComment()).append("</option>\n");
+                for (Map<String, Object> map : column.getEnums()){
+                    for (Map.Entry<String, Object> entry : map.entrySet()){
+                        sb.append("                            <option value=\"")
+                                .append(entry.getKey())
+                                .append("\">")
+                                .append(entry.getValue())
+                                .append("</option>\n");
+                    }
+                }
+                sb.append("                        </select>\n");
+            }
+
+            sb.append("                    </div>\n");
+            sb.append("                </div>\n");
+        }
+        return sb.toString();
+    }
+
+    private String createJsTableMsg(){
+        StringBuilder sb = new StringBuilder();
+        for (Column column : columns){
+//            if (column.isPrimaryKey()){ continue;}
+            sb.append("            ,{field: '");
+            if ("Date".equals(column.getType()) || !Cools.isEmpty(column.getEnums())){
+                // 鏃堕棿銆佹灇涓�  鏍煎紡鍖�
+                sb.append(column.getHumpName()).append("\\$");
+            } else {
+                // 涓婚敭淇グ
+                if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                    sb.append(column.getHumpName()).append("\\$");
+                } else {
+                    sb.append(column.getHumpName());
+                }
+            }
+            sb.append("', align: 'center',title: '").append(column.getComment()).append("'");
+            // 澶嶉�夋
+            if (column.isCheckBox()){
+                sb.append(", templet:function(row){\n")
+                        .append("                    var html = \"<input value='")
+                        .append(column.getHumpName()).append("' type='checkbox' lay-skin='primary' lay-filter='tableCheckbox' table-index='\"+row.LAY_TABLE_INDEX+\"'\";\n")
+                        .append("                    if(row.").append(column.getHumpName()).append(" === 'Y'){html += \" checked \";}\n")
+                        .append("                    html += \">\";\n")
+                        .append("                    return html;\n")
+                        .append("                }");
+            }
+            sb.append("}\n");
+        }
+        return sb.toString();
+    }
+
+    private String createJsFkContent(){
+        StringBuilder sb = new StringBuilder();
+        for (Column column : columns){
+            // 濡傛灉鏈夊叧鑱斿鍋�
+            if (!Cools.isEmpty(column.getForeignKeyMajor())){
+                sb.append("    window.load").append(column.getForeignKey()).append("Sel = function () {").append("\n")
+                        .append("        return xmSelect.render({").append("\n")
+                        .append("            el: '#").append(GeneratorUtils.firstCharConvert(column.getForeignKey(), true)).append("XmlSel',").append("\n")
+                        .append("            autoRow: true,").append("\n")
+                        .append("            filterable: true,").append("\n")
+                        .append("            remoteSearch: true,").append("\n")
+                        .append("            radio: true,").append("\n")
+                        .append("            remoteMethod: function (val, cb, show) {").append("\n")
+                        .append("                \\$.ajax({").append("\n")
+                        .append("                    url: baseUrl + \"/").append(GeneratorUtils.firstCharConvert(column.getForeignKey(), true)).append("/all/get/kv\",").append("\n")
+                        .append("                    headers: {'token': localStorage.getItem('token')},").append("\n")
+                        .append("                    data: {").append("\n")
+                        .append("                        condition: val").append("\n")
+                        .append("                    },").append("\n")
+                        .append("                    method: 'POST',").append("\n")
+                        .append("                    success: function (res) {").append("\n")
+                        .append("                        if (res.code === 200) {").append("\n")
+                        .append("                            cb(res.data)").append("\n")
+                        .append("                        } else {").append("\n")
+                        .append("                            cb([]);").append("\n")
+                        .append("                            layer.msg(res.msg, {icon: 2});").append("\n")
+                        .append("                        }").append("\n")
+                        .append("                    }").append("\n")
+                        .append("                });").append("\n")
+                        .append("            }").append("\n")
+                        .append("        });").append("\n")
+                        .append("    }").append("\n")
+                        .append("\n");
+            }
+        }
+        return sb.toString();
+    }
+
+    private String createJsDateContent(){
+        StringBuilder sb = new StringBuilder();
+        for (Column column : columns) {
+            if (column.isPrimaryKey()) {
+                continue;
+            }
+            if ("Date".equals(column.getType())){
+                sb.append("            layDate.render({\n")
+                        .append("                elem: '#").append(column.getHumpName()).append("\\\\\\\\\\$',\n")
+                        .append("                type: 'datetime',\n")
+                        .append("                value: data!==undefined?data['").append(column.getHumpName()).append("\\$'").append("]:null\n")
+                        .append("            });\n");
+            }
+        }
+        return sb.toString();
+    }
+
+    private String createJsPrimaryKeyMsg(){
+        StringBuilder sb = new StringBuilder();
+        for (Column column : columns) {
+            if (column.isPrimaryKey()) {
+                sb.append("#").append(column.getHumpName()).append(",");
+            }
+        }
+        if (sb.length() > 1){
+            if (sb.substring(sb.length() - 1).equals(",")) {
+                sb.deleteCharAt(sb.length()-1);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**********************************************************************************************/
+    /************************************** Index鍔ㄦ�佸瓧娈� *******************************************/
+    /**********************************************************************************************/
+
+
+
+
+
+
+    /**********************************************************************************************/
+    /************************************** Edit鍔ㄦ�佸瓧娈� ********************************************/
+    /**********************************************************************************************/
+
+
+
+
+
+}
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Controller.txt b/zy-asrs-framework/src/main/resources/templates/react/Controller.txt
new file mode 100644
index 0000000..b5f7a2c
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Controller.txt
@@ -0,0 +1,122 @@
+package com.zy.asrs.wcs.sys.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zy.asrs.framework.common.Cools;
+import com.zy.asrs.framework.common.R;
+import com.zy.asrs.wcs.common.annotation.OperationLog;
+import com.zy.asrs.wcs.common.domain.BaseParam;
+import com.zy.asrs.wcs.common.domain.KeyValVo;
+import com.zy.asrs.wcs.common.domain.PageParam;
+import com.zy.asrs.wcs.sys.entity.Role;
+import com.zy.asrs.wcs.sys.service.RoleService;
+import com.zy.asrs.wcs.utils.ExcelUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by vincent on 2/13/2024
+ */
+@RestController
+@RequestMapping("/api")
+public class RoleController extends BaseController {
+
+    @Autowired
+    private RoleService roleService;
+
+    @PreAuthorize("hasAuthority('sys:role:list')")
+    @PostMapping("/role/page")
+    public R page(@RequestBody Map<String, Object> map) {
+        BaseParam baseParam = buildParam(map, BaseParam.class);
+        PageParam<Role, BaseParam> pageParam = new PageParam<>(baseParam, Role.class);
+        return R.ok().add(roleService.page(pageParam, pageParam.buildWrapper(true)));
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:list')")
+    @PostMapping("/role/list")
+    public R list(@RequestBody Map<String, Object> map) {
+        return R.ok().add(roleService.list());
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:list')")
+    @GetMapping("/role/{id}")
+    public R get(@PathVariable("id") Long id) {
+        return R.ok().add(roleService.getById(id));
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:save')")
+    @OperationLog("娣诲姞瑙掕壊")
+    @PostMapping("/role/save")
+    public R save(@RequestBody Role role) {
+        if (!Cools.isEmpty(role.getName())
+                && roleService.count(new LambdaQueryWrapper<Role>().eq(Role::getName, role.getName())) > 0) {
+            return R.error("瑙掕壊鍚嶇О宸插瓨鍦�");
+        }
+        if (!Cools.isEmpty(role.getCode())
+                && roleService.count(new LambdaQueryWrapper<Role>().eq(Role::getCode, role.getCode())) > 0) {
+            return R.error("瑙掕壊鏍囪瘑宸插瓨鍦�");
+        }
+        if (!roleService.save(role)) {
+            return R.error("娣诲姞澶辫触");
+        }
+        return R.ok("娣诲姞鎴愬姛");
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:update')")
+    @OperationLog("淇敼瑙掕壊")
+    @PostMapping("/role/update")
+    public R update(@RequestBody Role role) {
+        if (!Cools.isEmpty(role.getCode()) && roleService.count(new LambdaQueryWrapper<Role>()
+                .eq(Role::getCode, role.getCode())
+                .ne(Role::getId, role.getId())) > 0) {
+            return R.error("瑙掕壊鏍囪瘑宸插瓨鍦�");
+        }
+        if (!Cools.isEmpty(role.getName()) && roleService.count(new LambdaQueryWrapper<Role>()
+                .eq(Role::getName, role.getName())
+                .ne(Role::getId, role.getId())) > 0) {
+            return R.error("瑙掕壊鍚嶇О宸插瓨鍦�");
+        }
+        if (!roleService.updateById(role)) {
+            return R.error("淇敼澶辫触");
+        }
+        return R.ok("淇敼鎴愬姛");
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:remove')")
+    @OperationLog("鍒犻櫎瑙掕壊")
+    @PostMapping("/role/remove/{ids}")
+    public R remove(@PathVariable Long[] ids) {
+        if (!roleService.removeByIds(Arrays.asList(ids))) {
+            return R.error("鍒犻櫎澶辫触");
+        }
+        return R.ok("鍒犻櫎鎴愬姛");
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:list')")
+    @PostMapping("/role/query")
+    public R query(@RequestParam(required = false) String condition) {
+        List<KeyValVo> vos = new ArrayList<>();
+        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
+        if (!Cools.isEmpty(condition)) {
+            wrapper.like(Role::getName, condition);
+        }
+        roleService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
+                item -> vos.add(new KeyValVo(item.getId(), item.getName()))
+        );
+        return R.ok().add(vos);
+    }
+
+    @PreAuthorize("hasAuthority('sys:role:list')")
+    @PostMapping("/role/export")
+    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
+        ExcelUtil.build(ExcelUtil.create(roleService.list(), Role.class), response);
+    }
+
+}
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Edit.txt b/zy-asrs-framework/src/main/resources/templates/react/Edit.txt
new file mode 100644
index 0000000..aee6263
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Edit.txt
@@ -0,0 +1,112 @@
+import React, { useState, useRef, useEffect } from 'react';
+import {
+    ProForm,
+    ProFormDigit,
+    ProFormText,
+    ProFormSelect,
+    ProFormDateTimePicker
+} from '@ant-design/pro-components';
+import { Form, Modal } from 'antd';
+import moment from 'moment';
+import Http from '@/utils/http';
+
+const Edit = (props) => {
+    const [form] = Form.useForm();
+    const { } = props;
+
+    useEffect(() => {
+        form.resetFields();
+        form.setFieldsValue({
+            ...props.values
+        })
+    }, [form, props])
+
+    const handleCancel = () => {
+        props.onCancel();
+    };
+
+    const handleOk = () => {
+        form.submit();
+    }
+
+    const handleFinish = async (values) => {
+        props.onSubmit({ ...values });
+    }
+
+    return (
+        <>
+            <Modal
+                title="Edit"
+                width={640}
+                forceRender
+                destroyOnClose
+                open={props.open}
+                onCancel={handleCancel}
+                onOk={handleOk}
+            >
+                <ProForm
+                    form={form}
+                    submitter={false}
+                    onFinish={handleFinish}
+                    layout="horizontal"
+                    grid={true}
+                >
+                    <ProFormDigit
+                        name="id"
+                        disabled
+                        hidden={true}
+                    />
+                    <ProForm.Group>
+                        <ProFormText
+                            name="name"
+                            label="瑙掕壊鍚嶇О"
+                            colProps={{ md: 12, xl: 12 }}
+                            placeholder="璇疯緭鍏�"
+                            rules={[{ required: true, message: "璇疯緭鍏ヨ鑹插悕绉帮紒" }]}
+                        />
+                        <ProFormText
+                            name="code"
+                            label="瑙掕壊鏍囪瘑"
+                            colProps={{ md: 12, xl: 12 }}
+                            placeholder="璇疯緭鍏�"
+                            rules={[{ required: true, message: "璇疯緭鍏ヨ鑹叉爣璇嗭紒" }]}
+                        />
+                    </ProForm.Group>
+                    <ProForm.Group>
+                        <ProFormSelect
+                            name="status"
+                            label="鐘舵��"
+                            colProps={{ md: 12, xl: 12 }}
+                            options={[
+                                { label: '姝e父', value: 1 },
+                                { label: '绂佺敤', value: 0 },
+                            ]}
+                            rules={[{ required: true, message: "璇烽�夋嫨鐘舵�侊紒" }]}
+                        />
+                        <ProFormDateTimePicker
+                            name="updateTime"
+                            label="淇敼鏃堕棿"
+                            colProps={{ md: 12, xl: 12 }}
+                            transform={(value) => {
+                                return moment(value).toISOString();
+                            }}
+                        />
+                    </ProForm.Group>
+                    <ProFormSelect
+                        name="hostId"
+                        label="鏈烘瀯"
+                        colProps={{ md: 12, xl: 12 }}
+                        showSearch
+                        debounceTime={300}
+                        request={async ({ keyWords }) => {
+                            const resp = await Http.doPostForm('api/hostId/query', { condition: keyWords });
+                            return resp.data;
+                        }}
+                    />
+                </ProForm>
+            </Modal>
+        </>
+    )
+}
+
+export default Edit;
\ No newline at end of file
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Entity.txt b/zy-asrs-framework/src/main/resources/templates/react/Entity.txt
new file mode 100644
index 0000000..6684d91
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Entity.txt
@@ -0,0 +1,28 @@
+package @{COMPANYNAME}.entity;
+
+@{ENTITYIMPORT}
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import com.zy.asrs.framework.common.Cools;
+import com.zy.asrs.framework.common.SpringUtils;
+import com.zy.asrs.common.sys.entity.User;
+import com.zy.asrs.common.sys.entity.Host;
+import com.zy.asrs.common.sys.service.UserService;
+import com.zy.asrs.common.sys.service.HostService;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("@{TABLENAME}")
+public class @{ENTITYNAME} implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+@{ENTITYCONTENT}
+}
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Index.txt b/zy-asrs-framework/src/main/resources/templates/react/Index.txt
new file mode 100644
index 0000000..da61022
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Index.txt
@@ -0,0 +1,350 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { Button, message, Modal } from 'antd';
+import {
+    FooterToolbar,
+    PageContainer,
+    ProTable,
+    LightFilter,
+} from '@ant-design/pro-components';
+import { PlusOutlined, ExportOutlined } from '@ant-design/icons';
+import Http from '@/utils/http';
+import Edit from './components/edit'
+import { TextFilter, SelectFilter, DatetimeRangeFilter, LinkFilter } from '@/components/TableSearch'
+
+const handleSave = async (val) => {
+    const hide = message.loading('姝e湪娣诲姞');
+    try {
+        const resp = await Http.doPost('api/role/save', val);
+        if (resp.code === 200) {
+            message.success('娣诲姞鎴愬姛');
+            return true;
+        } else {
+            message.error(resp.msg);
+            return false;
+        }
+    } catch (error) {
+        message.error('娣诲姞澶辫触璇烽噸璇曪紒');
+        return false;
+    } finally {
+        hide();
+    }
+};
+
+const handleUpdate = async (val) => {
+    const hide = message.loading('姝e湪鏇存柊');
+    try {
+        const resp = await Http.doPost('api/role/update', val);
+        if (resp.code === 200) {
+            message.success('鏇存柊鎴愬姛');
+            return true;
+        } else {
+            message.error(resp.msg);
+            return false;
+        }
+    } catch (error) {
+        message.error('閰嶇疆澶辫触璇烽噸璇曪紒');
+        return false;
+    } finally {
+        hide();
+    }
+};
+
+const handleRemove = async (rows) => {
+    if (!rows) return true;
+    const hide = message.loading('姝e湪鍒犻櫎');
+    try {
+        const resp = await Http.doPost('api/role/remove/' + rows.map((row) => row.id).join(','));
+        if (resp.code === 200) {
+            message.success('鍒犻櫎鎴愬姛');
+            return true;
+        } else {
+            message.error(resp.msg);
+            return false;
+        }
+    } catch (error) {
+        message.error('鍒犻櫎澶辫触锛岃閲嶈瘯');
+        return false;
+    } finally {
+        hide();
+    }
+};
+
+const handleExport = async () => {
+    const hide = message.loading('姝e湪瀵煎嚭');
+    try {
+        const resp = await Http.doPostBlob('api/role/export');
+        const blob = new Blob([resp], { type: 'application/vnd.ms-excel' });
+        window.location.href = window.URL.createObjectURL(blob);
+        message.success('瀵煎嚭鎴愬姛');
+        return true;
+    } catch (error) {
+        message.error('瀵煎嚭澶辫触锛岃閲嶈瘯');
+        return false;
+    } finally {
+        hide();
+    }
+};
+
+
+const Main = () => {
+    const formTableRef = useRef();
+    const actionRef = useRef();
+    const [selectedRows, setSelectedRows] = useState([]);
+    const [modalVisible, setModalVisible] = useState(false);
+    const [currentRow, setCurrentRow] = useState();
+    const [searchParam, setSearchParam] = useState({});
+
+    useEffect(() => {
+
+    }, []);
+
+    const columns = [
+        {
+            title: 'No',
+            dataIndex: 'index',
+            valueType: 'indexBorder',
+            width: 48,
+        },
+        {
+            title: '濮撳悕',
+            dataIndex: 'name',
+            valueType: 'text',
+            copyable: true,
+            filterDropdown: (props) => <TextFilter
+                name='name'
+                {...props}
+                actionRef={actionRef}
+                setSearchParam={setSearchParam}
+            />,
+        },
+        {
+            title: '鏍囪瘑',
+            dataIndex: 'code',
+            valueType: 'text',
+            filterDropdown: (props) => <TextFilter
+                name='code'
+                {...props}
+                actionRef={actionRef}
+                setSearchParam={setSearchParam}
+            />,
+        },
+        {
+            title: '鏈烘瀯',
+            dataIndex: 'hostId$',
+            valueType: 'text',
+            filterDropdown: (props) => <LinkFilter
+                name='hostId'
+                {...props}
+                actionRef={actionRef}
+                setSearchParam={setSearchParam}
+            />,
+        },
+        {
+            title: '鐘舵��',
+            dataIndex: 'status$',
+            valueType: 'text',
+            filterDropdown: (props) => <SelectFilter
+                name='status'
+                {...props}
+                actionRef={actionRef}
+                setSearchParam={setSearchParam}
+                data={[
+                    { label: '姝e父', value: 1 },
+                    { label: '绂佺敤', value: 0 },
+                ]}
+            />,
+        },
+        {
+            title: '淇敼鏃堕棿',
+            dataIndex: 'updateTime$',
+            valueType: 'text',
+            filterDropdown: (props) => <DatetimeRangeFilter
+                name='updateTime'
+                {...props}
+                actionRef={actionRef}
+                setSearchParam={setSearchParam}
+            />,
+        },
+        {
+            title: '鎿嶄綔',
+            dataIndex: 'option',
+            width: 140,
+            valueType: 'option',
+            render: (_, record) => [
+                <Button
+                    type="link"
+                    key="edit"
+                    onClick={() => {
+                        setModalVisible(true);
+                        setCurrentRow(record);
+                    }}
+                >
+                    缂栬緫
+                </Button>,
+                <Button
+                    type="link"
+                    danger
+                    key="batchRemove"
+                    onClick={async () => {
+                        Modal.confirm({
+                            title: '鍒犻櫎',
+                            content: '纭畾鍒犻櫎璇ラ」鍚楋紵',
+                            onOk: async () => {
+                                const success = await handleRemove([record]);
+                                if (success) {
+                                    if (actionRef.current) {
+                                        actionRef.current.reload();
+                                    }
+                                }
+                            },
+                        });
+                    }}
+                >
+                    鍒犻櫎
+                </Button>,
+            ],
+        },
+    ];
+
+    return (
+        <PageContainer>
+            <div style={{ width: '100%', float: 'right' }}>
+                <ProTable
+                    key="role"
+                    rowKey="id"
+                    actionRef={actionRef}
+                    formRef={formTableRef}
+                    columns={columns}
+                    cardBordered
+                    dateFormatter="string"
+                    pagination={{ pageSize: 20 }}
+                    search={false}
+                    toolbar={{
+                        search: {
+                            onSearch: (value) => {
+                                setSearchParam(prevState => ({
+                                    ...prevState,
+                                    condition: value
+                                }));
+                                actionRef.current?.reload();
+                            },
+                        },
+                        filter: (
+                            <LightFilter
+                                onValuesChange={(val) => {
+                                }}
+                            >
+                            </LightFilter>
+                        ),
+                        actions: [
+                            <Button
+                                type="primary"
+                                key="save"
+                                onClick={async () => {
+                                    setModalVisible(true)
+                                }}
+                            >
+                                <PlusOutlined />
+                                娣诲姞
+                            </Button>,
+                            <Button
+                                key="export"
+                                onClick={async () => {
+                                    handleExport();
+                                }}
+                            >
+                                <ExportOutlined />
+                                瀵煎嚭
+                            </Button>,
+                        ],
+                    }}
+                    request={(params, sorter, filter) =>
+                        Http.doPostPromise('/api/role/page', { ...params, ...searchParam }, (res) => {
+                            return {
+                                data: res.data.records,
+                                total: res.data.total,
+                                success: true,
+                            }
+                        })
+                    }
+                    rowSelection={{
+                        onChange: (ids, rows) => {
+                            setSelectedRows(rows);
+                        }
+                    }}
+                    columnsState={{
+                        persistenceKey: 'pro-table-role',
+                        persistenceType: 'localStorage',
+                        defaultValue: {
+                            option: { fixed: 'right', disable: true },
+                        },
+                        onChange(value) {
+                        },
+                    }}
+                />
+            </div>
+
+            {selectedRows?.length > 0 && (
+                <FooterToolbar
+                    extra={
+                        <div>
+                            宸查�夋嫨
+                            <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+                            椤�
+                        </div>
+                    }
+                >
+                    <Button
+                        key="remove"
+                        danger
+                        onClick={async () => {
+                            Modal.confirm({
+                                title: '鍒犻櫎',
+                                content: '纭畾鍒犻櫎璇ラ」鍚楋紵',
+                                onOk: async () => {
+                                    const success = await handleRemove(selectedRows);
+                                    if (success) {
+                                        setSelectedRows([]);
+                                        actionRef.current?.reloadAndRest?.();
+                                    }
+                                },
+                            });
+                        }}
+                    >
+                        鎵归噺鍒犻櫎
+                    </Button>
+                </FooterToolbar>
+            )}
+
+            <Edit
+                open={modalVisible}
+                values={currentRow || {}}
+                onCancel={
+                    () => {
+                        setModalVisible(false);
+                        setCurrentRow(undefined);
+                    }
+                }
+                onSubmit={async (values) => {
+                    let ok = false;
+                    if (values.id) {
+                        ok = await handleUpdate({ ...values })
+                    } else {
+                        ok = await handleSave({ ...values })
+                    }
+                    if (ok) {
+                        setModalVisible(false);
+                        setCurrentRow(undefined);
+                        if (actionRef.current) {
+                            actionRef.current.reload();
+                        }
+                    }
+                }
+                }
+            />
+        </PageContainer>
+    );
+};
+
+export default Main;
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Mapper.txt b/zy-asrs-framework/src/main/resources/templates/react/Mapper.txt
new file mode 100644
index 0000000..5815c97
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Mapper.txt
@@ -0,0 +1,12 @@
+package @{COMPANYNAME}.mapper;
+
+import @{COMPANYNAME}.entity.@{ENTITYNAME};
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+@Mapper
+@Repository
+public interface @{ENTITYNAME}Mapper extends BaseMapper<@{ENTITYNAME}> {
+
+}
\ No newline at end of file
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Service.txt b/zy-asrs-framework/src/main/resources/templates/react/Service.txt
new file mode 100644
index 0000000..6129c08
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Service.txt
@@ -0,0 +1,8 @@
+package @{COMPANYNAME}.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import @{COMPANYNAME}.entity.@{ENTITYNAME};
+
+public interface @{ENTITYNAME}Service extends IService<@{ENTITYNAME}> {
+
+}
\ No newline at end of file
diff --git a/zy-asrs-framework/src/main/resources/templates/react/ServiceImpl.txt b/zy-asrs-framework/src/main/resources/templates/react/ServiceImpl.txt
new file mode 100644
index 0000000..63944cf
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/ServiceImpl.txt
@@ -0,0 +1,12 @@
+package @{COMPANYNAME}.service.impl;
+
+import @{COMPANYNAME}.mapper.@{ENTITYNAME}Mapper;
+import @{COMPANYNAME}.entity.@{ENTITYNAME};
+import @{COMPANYNAME}.service.@{ENTITYNAME}Service;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+@Service("@{SIMPLEENTITYNAME}Service")
+public class @{ENTITYNAME}ServiceImpl extends ServiceImpl<@{ENTITYNAME}Mapper, @{ENTITYNAME}> implements @{ENTITYNAME}Service {
+
+}
\ No newline at end of file
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Sql.txt b/zy-asrs-framework/src/main/resources/templates/react/Sql.txt
new file mode 100644
index 0000000..c11d64d
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Sql.txt
@@ -0,0 +1,18 @@
+-- save @{SIMPLEENTITYNAME} record
+-- mysql
+insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( '@{SIMPLEENTITYNAME}/@{SIMPLEENTITYNAME}.html', '@{SIMPLEENTITYNAME}绠$悊', null , '2', null , '1');
+
+insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( '@{SIMPLEENTITYNAME}#view', '鏌ヨ', '', '3', '0', '1');
+insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( '@{SIMPLEENTITYNAME}#btn-add', '鏂板', '', '3', '1', '1');
+insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( '@{SIMPLEENTITYNAME}#btn-edit', '缂栬緫', '', '3', '2', '1');
+insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( '@{SIMPLEENTITYNAME}#btn-delete', '鍒犻櫎', '', '3', '3', '1');
+insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( '@{SIMPLEENTITYNAME}#btn-export', '瀵煎嚭', '', '3', '4', '1');
+
+-- sqlserver
+insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'@{SIMPLEENTITYNAME}/@{SIMPLEENTITYNAME}.html', N'@{SIMPLEENTITYNAME}绠$悊', null, '2', null, '1');
+
+insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'@{SIMPLEENTITYNAME}#view', N'鏌ヨ', '', '3', '0', '1');
+insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'@{SIMPLEENTITYNAME}#btn-add', N'鏂板', '', '3', '1', '1');
+insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'@{SIMPLEENTITYNAME}#btn-edit', N'缂栬緫', '', '3', '2', '1');
+insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'@{SIMPLEENTITYNAME}#btn-delete', N'鍒犻櫎', '', '3', '3', '1');
+insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'@{SIMPLEENTITYNAME}#btn-export', N'瀵煎嚭', '', '3', '4', '1');
diff --git a/zy-asrs-framework/src/main/resources/templates/react/Xml.txt b/zy-asrs-framework/src/main/resources/templates/react/Xml.txt
new file mode 100644
index 0000000..94924d8
--- /dev/null
+++ b/zy-asrs-framework/src/main/resources/templates/react/Xml.txt
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="@{COMPANYNAME}.mapper.@{ENTITYNAME}Mapper">
+
+</mapper>
\ No newline at end of file

--
Gitblit v1.9.1