Quellcode durchsuchen

新增宗教信息导入、信息校核等一堆功能,目前满足评审功能要求

yawuga vor 5 Monaten
Ursprung
Commit
bae00ec95a
42 geänderte Dateien mit 9059 neuen und 2116 gelöschten Zeilen
  1. BIN
      D:/ruoyi/uploadPath/avatar/2025/12/05/2e6f4e43cc7d4bd498f5dd91b10807ca.png
  2. 38 23
      doc/宗教管理系统DEMO开发技术说明文档.md
  3. 447 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/controller/ReligionAuditLogController.java
  4. 173 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/domain/BizAuditLog.java
  5. 45 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/mapper/ReligionAuditLogMapper.java
  6. 45 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/service/IReligionAuditLogService.java
  7. 69 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/service/impl/ReligionAuditLogServiceImpl.java
  8. 352 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/controller/ReligionPersonCheckController.java
  9. 65 2
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/controller/ReligionPersonController.java
  10. 220 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/controller/ReligionPersonImportController.java
  11. 223 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/ReligionImportBatch.java
  12. 181 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/ReligionImportItem.java
  13. 87 4
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/ReligionPerson.java
  14. 354 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/dto/ReligionPersonBatchUpdateByConditionRequest.java
  15. 189 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/dto/ReligionPersonBatchUpdatePreviewResponse.java
  16. 61 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/mapper/ReligionImportBatchMapper.java
  17. 103 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/mapper/ReligionImportItemMapper.java
  18. 25 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/mapper/ReligionPersonMapper.java
  19. 78 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/IReligionPersonImportService.java
  20. 50 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/IReligionPersonService.java
  21. 1210 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/impl/ReligionPersonImportServiceImpl.java
  22. 986 1
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/impl/ReligionPersonServiceImpl.java
  23. 7 0
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/service/IReligionPlaceService.java
  24. 361 6
      ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/service/impl/ReligionPlaceServiceImpl.java
  25. 1 1
      ruoyi-admin/src/main/resources/application.yml
  26. 93 0
      ruoyi-admin/src/main/resources/mapper/religion/audit/ReligionAuditLogMapper.xml
  27. 112 0
      ruoyi-admin/src/main/resources/mapper/religion/person/ReligionImportBatchMapper.xml
  28. 131 0
      ruoyi-admin/src/main/resources/mapper/religion/person/ReligionImportItemMapper.xml
  29. 187 19
      ruoyi-admin/src/main/resources/mapper/religion/person/ReligionPersonMapper.xml
  30. BIN
      ruoyi-admin/src/main/resources/static/img/login-background.jpg
  31. BIN
      ruoyi-admin/src/main/resources/static/img/login-background1.jpg
  32. 4 4
      ruoyi-admin/src/main/resources/templates/index.html
  33. 10 10
      ruoyi-admin/src/main/resources/templates/login.html
  34. 3 1804
      ruoyi-admin/src/main/resources/templates/main.html
  35. 146 0
      ruoyi-admin/src/main/resources/templates/religion/audit/audit.html
  36. 263 0
      ruoyi-admin/src/main/resources/templates/religion/audit/detail.html
  37. 961 0
      ruoyi-admin/src/main/resources/templates/religion/person/personImport.html
  38. 63 0
      ruoyi-admin/src/main/resources/templates/religion/place/place.html
  39. 207 94
      ruoyi-admin/src/main/resources/templates/religion/religionPerson/add.html
  40. 207 93
      ruoyi-admin/src/main/resources/templates/religion/religionPerson/edit.html
  41. 532 0
      ruoyi-admin/src/main/resources/templates/religion/religionPerson/personCheck.html
  42. 770 55
      ruoyi-admin/src/main/resources/templates/religion/religionPerson/religionPerson.html

BIN
D:/ruoyi/uploadPath/avatar/2025/12/05/2e6f4e43cc7d4bd498f5dd91b10807ca.png


+ 38 - 23
doc/宗教管理系统DEMO开发技术说明文档.md

@@ -175,11 +175,12 @@ CREATE TABLE religion_place (
 
 ```sql
 CREATE TABLE religion_person (
-  person_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '人员ID',
+  person_id BIGINT PRIMARY_KEY AUTO_INCREMENT COMMENT '人员ID',
   place_id BIGINT NOT NULL COMMENT '所属场所ID(外键,关联 religion_place.place_id)',
   name VARCHAR(128) NOT NULL COMMENT '姓名',
   religion_type VARCHAR(64) NOT NULL COMMENT '宗教类别',
   gender VARCHAR(8) COMMENT '性别',
+  nation VARCHAR(64) COMMENT '民族(字典:sys_nation)',
   id_number VARCHAR(32) NOT NULL COMMENT '身份证号',
   birthday DATE COMMENT '出生日期',
   county VARCHAR(128) COMMENT '所属区县',
@@ -200,6 +201,7 @@ CREATE TABLE religion_person (
   update_time DATETIME COMMENT '更新时间',
   UNIQUE KEY uk_id_name (id_number, name),
   INDEX idx_place_id (place_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='教职人员表';
 );
 ```
 
@@ -208,18 +210,23 @@ CREATE TABLE religion_person (
 ### 4.2.3 导入批次表 religion_import_batch
 
 ```sql
-CREATE TABLE religion_import_batch (
-  batch_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '批次ID',
-  batch_no VARCHAR(64) COMMENT '批次号(可用时间戳+随机数生成)',
-  file_name VARCHAR(256) COMMENT '导入文件名',
-  uploader VARCHAR(64) COMMENT '上传人登录名',
-  upload_time DATETIME COMMENT '上传时间',
-  total_rows INT COMMENT '总记录数',
-  valid_rows INT COMMENT '有效记录数',
-  error_rows INT COMMENT '格式错误记录数',
-  conflict_rows INT COMMENT '冲突记录数',
-  status VARCHAR(32) COMMENT '状态:PARSED已解析/PARTIAL部分处理/FINISHED已完成',
-  extra JSON COMMENT '扩展字段(预留)'
+create table religion_import_batch
+(
+    batch_id          bigint auto_increment comment '批次ID'
+        primary key,
+    batch_no          varchar(64)  null comment '批次号(可用时间戳+随机数生成)',
+    file_name         varchar(256) null comment '导入文件名',
+    uploader          varchar(64)  null comment '上传人登录名',
+    upload_time       datetime     null comment '上传时间',
+    last_process_time datetime     null comment '最近一次处理时间',
+    total_rows        int          null comment '总记录数',
+    valid_rows        int          null comment '有效记录数',
+    error_rows        int          null comment '格式错误记录数',
+    conflict_rows     int          null comment '冲突记录数',
+    imported_rows     int          null comment '最终成功导入记录数(包含新增和更新)',
+    abandoned_rows    int          null comment '放弃导入记录数',
+    status            varchar(32)  null comment '状态:PARSED已解析/PARTIAL部分处理/FINISHED已完成',
+    extra             json         null comment '扩展字段(预留)'
 );
 ```
 
@@ -228,17 +235,25 @@ CREATE TABLE religion_import_batch (
 ### 4.2.4 导入项表 religion_import_item
 
 ```sql
-CREATE TABLE religion_import_item (
-  item_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '导入项ID',
-  batch_id BIGINT COMMENT '批次ID,对应 religion_import_batch.batch_id',
-  row_no INT COMMENT 'Excel 行号(从1开始)',
-  json_data JSON COMMENT '该行原始数据的 JSON 表示',
-  error_msg VARCHAR(512) COMMENT '格式错误说明,为空表示无格式错误',
-  conflict_flag TINYINT COMMENT '是否为冲突记录:0否1是',
-  processed_flag TINYINT COMMENT '是否已完成处理:0否1是',
-  create_time DATETIME COMMENT '记录创建时间',
-  INDEX idx_batch (batch_id)
+create table religion_import_item
+(
+    item_id        bigint auto_increment comment '导入项ID'
+        primary key,
+    batch_id       bigint       null comment '批次ID,对应 religion_import_batch.batch_id',
+    row_no         int          null comment 'Excel 行号(从1开始)',
+    json_data      json         null comment '该行原始数据的 JSON 表示',
+    error_msg      varchar(512) null comment '格式错误说明,为空表示无格式错误',
+    conflict_flag  tinyint      null comment '是否为冲突记录:0否1是',
+    status         varchar(32)  null comment '系统校验状态:NORMAL正常/ERROR格式错误/CONFLICT冲突',
+    action         varchar(32)  null comment '用户处理决策:IMPORT/OVERWRITE/KEEP/ABANDON/MANUAL',
+    action_by      varchar(64)  null comment '决策人登录名',
+    action_time    datetime     null comment '决策时间',
+    processed_flag tinyint      null comment '是否已完成处理:0否1是',
+    create_time    datetime     null comment '记录创建时间'
 );
+
+create index idx_batch
+    on religion_import_item (batch_id);
 ```
 
 ---

+ 447 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/controller/ReligionAuditLogController.java

@@ -0,0 +1,447 @@
+package com.ruoyi.project.religion.audit.controller;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+import com.ruoyi.project.religion.audit.service.IReligionAuditLogService;
+
+/**
+ * 业务审计日志Controller
+ * 
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/religion/audit")
+public class ReligionAuditLogController extends BaseController
+{
+    private String prefix = "religion/audit";
+
+    @Autowired
+    private IReligionAuditLogService religionAuditLogService;
+
+    /**
+     * 跳转到审计日志列表页面
+     */
+    @RequiresPermissions("religion:audit:view")
+    @GetMapping()
+    public String audit()
+    {
+        return prefix + "/audit";
+    }
+
+    /**
+     * 查询业务审计日志列表
+     */
+    @RequiresPermissions("religion:audit:list")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(BizAuditLog log)
+    {
+        startPage();
+        List<BizAuditLog> list = religionAuditLogService.selectReligionAuditLogList(log);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出业务审计日志列表
+     */
+    @RequiresPermissions("religion:audit:export")
+    @Log(title = "业务审计日志", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(BizAuditLog log)
+    {
+        List<BizAuditLog> list = religionAuditLogService.selectReligionAuditLogList(log);
+        ExcelUtil<BizAuditLog> util = new ExcelUtil<BizAuditLog>(BizAuditLog.class);
+        return util.exportExcel(list, "审计日志数据");
+    }
+
+    /**
+     * 查看审计日志详情
+     */
+    @RequiresPermissions("religion:audit:query")
+    @GetMapping("/detail/{logId}")
+    public String detail(@PathVariable("logId") Long logId, ModelMap mmap)
+    {
+        BizAuditLog log = religionAuditLogService.selectReligionAuditLogById(logId);
+        mmap.put("log", log);
+        
+        // 判断是否为批量导入操作
+        boolean isBatchImport = "BATCH_IMPORT_PERSON".equals(log.getOpType());
+        mmap.put("isBatchImport", isBatchImport);
+        
+        // 判断是否为新增类操作
+        boolean isCreate = log.getOpType() != null && log.getOpType().contains("新增");
+        mmap.put("isCreate", isCreate);
+        
+        // 字段中文名映射
+        Map<String, String> fieldLabelMap = getFieldLabelMap();
+        
+        // 解析 opDetail
+        String opDetail = log.getOpDetail();
+        List<AuditDiffItem> diffList = new ArrayList<>();
+        List<AuditCreateItem> createList = new ArrayList<>();
+        
+        // 如果是批量导入,直接传递原始JSON,不解析为字段变更
+        if (isBatchImport)
+        {
+            mmap.put("diffList", diffList);
+            mmap.put("createList", createList);
+            mmap.put("rawDetail", opDetail);
+            return prefix + "/detail";
+        }
+        
+        if (StringUtils.isNotEmpty(opDetail))
+        {
+            try
+            {
+                // 先尝试解析为数组(批量修改场景)
+                Object parsedDetail = JSON.parse(opDetail);
+                
+                if (parsedDetail instanceof JSONArray)
+                {
+                    // 批量修改场景:数组结构
+                    JSONArray detailArray = (JSONArray) parsedDetail;
+                    for (int i = 0; i < detailArray.size(); i++)
+                    {
+                        JSONObject personItem = detailArray.getJSONObject(i);
+                        String personName = personItem.getString("name");
+                        JSONArray changes = personItem.getJSONArray("changes");
+                        
+                        if (changes != null && changes.size() > 0)
+                        {
+                            for (int j = 0; j < changes.size(); j++)
+                            {
+                                JSONObject change = changes.getJSONObject(j);
+                                String field = change.getString("field");
+                                String label = change.getString("label");
+                                String oldValue = change.getString("old");
+                                String newValue = change.getString("new");
+                                
+                                // 过滤无变化字段
+                                if (skipNoChange(oldValue, newValue))
+                                {
+                                    continue;
+                                }
+                                
+                                // 创建变更明细项,包含人员姓名
+                                AuditDiffItem item = new AuditDiffItem();
+                                item.setPersonName(personName); // 新增人员姓名字段
+                                item.setFieldKey(field);
+                                item.setFieldLabel(StringUtils.isNotEmpty(label) ? label : fieldLabelMap.getOrDefault(field, field));
+                                item.setBeforeValue(oldValue != null ? oldValue : "");
+                                item.setAfterValue(newValue != null ? newValue : "");
+                                diffList.add(item);
+                            }
+                        }
+                    }
+                }
+                else if (parsedDetail instanceof JSONObject)
+                {
+                    // 单条修改或新增场景:对象结构
+                    JSONObject detailJson = (JSONObject) parsedDetail;
+                    
+                    if (isCreate)
+                    {
+                        // 新增类操作:解析为字段 -> 值
+                        for (String key : detailJson.keySet())
+                        {
+                            AuditCreateItem item = new AuditCreateItem();
+                            item.setFieldKey(key);
+                            item.setFieldLabel(fieldLabelMap.getOrDefault(key, key));
+                            Object value = detailJson.get(key);
+                            item.setValue(value != null ? value.toString() : "");
+                            createList.add(item);
+                        }
+                    }
+                    else
+                    {
+                        // 修改类操作:解析为字段 -> {"old": xx, "new": yy}
+                        for (String key : detailJson.keySet())
+                        {
+                            Object changeObj = detailJson.get(key);
+                            if (changeObj instanceof JSONObject)
+                            {
+                                JSONObject change = (JSONObject) changeObj;
+                                Object oldVal = change.get("old");
+                                Object newVal = change.get("new");
+                                
+                                // 过滤无变化字段:前后都为空或内容相同
+                                if (skipNoChange(oldVal, newVal))
+                                {
+                                    continue;
+                                }
+                                
+                                AuditDiffItem item = new AuditDiffItem();
+                                item.setFieldKey(key);
+                                item.setFieldLabel(fieldLabelMap.getOrDefault(key, key));
+                                item.setBeforeValue(oldVal != null ? oldVal.toString() : "");
+                                item.setAfterValue(newVal != null ? newVal.toString() : "");
+                                diffList.add(item);
+                            }
+                        }
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                // JSON 解析失败,保持列表为空
+                logger.error("解析审计日志 opDetail 失败: " + e.getMessage());
+            }
+        }
+        
+        mmap.put("diffList", diffList);
+        mmap.put("createList", createList);
+        mmap.put("rawDetail", opDetail);
+        
+        return prefix + "/detail";
+    }
+    
+    /**
+     * 判断字段是否无变化(用于过滤详情页显示)
+     * 规则:
+     * 1. 把 null、""、全空格 都认为是"空值"
+     * 2. 如果 old 和 new 都是空值 → 跳过
+     * 3. 如果 old 和 new 文本相同(去掉首尾空格后相等)→ 跳过
+     * 
+     * @param oldVal 旧值
+     * @param newVal 新值
+     * @return true 表示应跳过(无变化),false 表示有变化
+     */
+    private boolean skipNoChange(Object oldVal, Object newVal)
+    {
+        String oldStr = oldVal == null ? "" : oldVal.toString().trim();
+        String newStr = newVal == null ? "" : newVal.toString().trim();
+        
+        // 前后都为空:跳过
+        if (oldStr.isEmpty() && newStr.isEmpty())
+        {
+            return true;
+        }
+        
+        // 内容相同:跳过
+        return oldStr.equals(newStr);
+    }
+    
+    /**
+     * 获取字段中文名映射(支持驼峰和下划线两种格式)
+     */
+    private Map<String, String> getFieldLabelMap()
+    {
+        Map<String, String> map = new HashMap<>();
+        
+        // 场所管理字段(驼峰格式)
+        map.put("placeName", "场所名称");
+        map.put("religionType", "宗教类别");
+        map.put("placeCategory", "场所类别");
+        map.put("county", "所属区县");
+        map.put("address", "详细地址");
+        map.put("chargeName", "负责人姓名");
+        map.put("chargePhone", "负责人联系电话");
+        map.put("placeArea", "场所占地面积(平方米)");
+        map.put("buildingArea", "建筑面积(平方米)");
+        map.put("approveOrgan", "批准设立机关");
+        map.put("approveTime", "批准设立时间");
+        map.put("regOrgan", "登记机关");
+        map.put("regNo", "登记证号");
+        map.put("regTime", "登记时间");
+        map.put("legalQualified", "是否具有法人资格");
+        map.put("legalTime", "法人资格取得时间");
+        map.put("creditCode", "统一社会信用代码");
+        map.put("longitude", "经度");
+        map.put("latitude", "纬度");
+        
+        // 场所管理字段(下划线格式,兼容)
+        map.put("place_name", "场所名称");
+        map.put("religion_type", "宗教类别");
+        map.put("place_category", "场所类别");
+        map.put("charge_name", "负责人姓名");
+        map.put("charge_phone", "负责人联系电话");
+        map.put("place_area", "场所占地面积(平方米)");
+        map.put("building_area", "建筑面积(平方米)");
+        map.put("approve_organ", "批准设立机关");
+        map.put("approve_time", "批准设立时间");
+        map.put("reg_organ", "登记机关");
+        map.put("reg_no", "登记证号");
+        map.put("reg_time", "登记时间");
+        map.put("legal_qualified", "是否具有法人资格");
+        map.put("legal_time", "法人资格取得时间");
+        map.put("credit_code", "统一社会信用代码");
+        
+        // 教职人员管理字段(驼峰格式)
+        map.put("name", "姓名");
+        map.put("placeId", "所属场所ID");
+        map.put("religionType", "宗教类别");
+        map.put("gender", "性别");
+        map.put("nation", "民族");
+        map.put("idNumber", "身份证号");
+        map.put("birthday", "出生日期");
+        map.put("county", "所属区县");
+        map.put("address", "家庭地址/联系地址");
+        map.put("politicalStatus", "政治面貌");
+        map.put("eduNational", "国民教育学历");
+        map.put("eduReligious", "宗教学历");
+        map.put("joinTime", "入教/出家时间");
+        map.put("phone", "联系电话");
+        map.put("domicile", "户籍所在地");
+        map.put("nativePlace", "籍贯");
+        map.put("orgName", "所属宫观名称(原始值)");
+        map.put("orgAddress", "所属宫观地址(原始值)");
+        map.put("status", "状态");
+        
+        // 教职人员管理字段(下划线格式,兼容)
+        map.put("place_id", "所属场所ID");
+        map.put("id_number", "身份证号");
+        map.put("political_status", "政治面貌");
+        map.put("edu_national", "国民教育学历");
+        map.put("edu_religious", "宗教学历");
+        map.put("join_time", "入教/出家时间");
+        map.put("native_place", "籍贯");
+        map.put("org_name", "所属宫观名称(原始值)");
+        map.put("org_address", "所属宫观地址(原始值)");
+        
+        return map;
+    }
+
+    /**
+     * 删除业务审计日志
+     */
+    @RequiresPermissions("religion:audit:remove")
+    @Log(title = "业务审计日志", businessType = BusinessType.DELETE)
+    @PostMapping("/remove")
+    @ResponseBody
+    public AjaxResult remove(String ids)
+    {
+        String[] idArray = ids.split(",");
+        Long[] logIds = new Long[idArray.length];
+        for (int i = 0; i < idArray.length; i++)
+        {
+            logIds[i] = Long.parseLong(idArray[i]);
+        }
+        return toAjax(religionAuditLogService.deleteReligionAuditLogByIds(logIds));
+    }
+    
+    /**
+     * 审计日志字段变更明细项(修改类操作)
+     */
+    public static class AuditDiffItem
+    {
+        private String personName; // 人员姓名(批量修改时使用)
+        private String fieldKey;
+        private String fieldLabel;
+        private String beforeValue;
+        private String afterValue;
+        
+        public String getPersonName()
+        {
+            return personName;
+        }
+        
+        public void setPersonName(String personName)
+        {
+            this.personName = personName;
+        }
+        
+        public String getFieldKey()
+        {
+            return fieldKey;
+        }
+        
+        public void setFieldKey(String fieldKey)
+        {
+            this.fieldKey = fieldKey;
+        }
+        
+        public String getFieldLabel()
+        {
+            return fieldLabel;
+        }
+        
+        public void setFieldLabel(String fieldLabel)
+        {
+            this.fieldLabel = fieldLabel;
+        }
+        
+        public String getBeforeValue()
+        {
+            return beforeValue;
+        }
+        
+        public void setBeforeValue(String beforeValue)
+        {
+            this.beforeValue = beforeValue;
+        }
+        
+        public String getAfterValue()
+        {
+            return afterValue;
+        }
+        
+        public void setAfterValue(String afterValue)
+        {
+            this.afterValue = afterValue;
+        }
+    }
+    
+    /**
+     * 审计日志新增数据明细项(新增类操作)
+     */
+    public static class AuditCreateItem
+    {
+        private String fieldKey;
+        private String fieldLabel;
+        private String value;
+        
+        public String getFieldKey()
+        {
+            return fieldKey;
+        }
+        
+        public void setFieldKey(String fieldKey)
+        {
+            this.fieldKey = fieldKey;
+        }
+        
+        public String getFieldLabel()
+        {
+            return fieldLabel;
+        }
+        
+        public void setFieldLabel(String fieldLabel)
+        {
+            this.fieldLabel = fieldLabel;
+        }
+        
+        public String getValue()
+        {
+            return value;
+        }
+        
+        public void setValue(String value)
+        {
+            this.value = value;
+        }
+    }
+}
+

+ 173 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/domain/BizAuditLog.java

@@ -0,0 +1,173 @@
+package com.ruoyi.project.religion.audit.domain;
+
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 业务审计日志对象 religion_audit_log
+ * 
+ * @author ruoyi
+ */
+public class BizAuditLog extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 日志ID */
+    private Long logId;
+
+    /** 操作用户ID */
+    @Excel(name = "操作用户ID")
+    private String userId;
+
+    /** 操作用户姓名 */
+    @Excel(name = "操作用户")
+    private String userName;
+
+    /** 操作类型(ADD_PLACE/UPDATE_PLACE/BATCH_UPDATE_PERSON/IMPORT_PERSON/CHECK_PERSON等) */
+    @Excel(name = "操作类型")
+    private String opType;
+
+    /** 资源类型 place/person/import */
+    @Excel(name = "资源类型")
+    private String resourceType;
+
+    /** 资源ID(如 place_id/person_id/batch_id) */
+    @Excel(name = "资源ID")
+    private String resourceId;
+
+    /** 操作详情(字段差异JSON或附加说明) */
+    private String opDetail;
+
+    /** 操作时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date opTime;
+
+    /** 操作IP地址 */
+    @Excel(name = "操作IP")
+    private String ip;
+
+    /** 扩展字段(可记录请求参数快照等) */
+    private String extra;
+
+    public void setLogId(Long logId) 
+    {
+        this.logId = logId;
+    }
+
+    public Long getLogId() 
+    {
+        return logId;
+    }
+
+    public void setUserId(String userId) 
+    {
+        this.userId = userId;
+    }
+
+    public String getUserId() 
+    {
+        return userId;
+    }
+
+    public void setUserName(String userName) 
+    {
+        this.userName = userName;
+    }
+
+    public String getUserName() 
+    {
+        return userName;
+    }
+
+    public void setOpType(String opType) 
+    {
+        this.opType = opType;
+    }
+
+    public String getOpType() 
+    {
+        return opType;
+    }
+
+    public void setResourceType(String resourceType) 
+    {
+        this.resourceType = resourceType;
+    }
+
+    public String getResourceType() 
+    {
+        return resourceType;
+    }
+
+    public void setResourceId(String resourceId) 
+    {
+        this.resourceId = resourceId;
+    }
+
+    public String getResourceId() 
+    {
+        return resourceId;
+    }
+
+    public void setOpDetail(String opDetail) 
+    {
+        this.opDetail = opDetail;
+    }
+
+    public String getOpDetail() 
+    {
+        return opDetail;
+    }
+
+    public void setOpTime(Date opTime) 
+    {
+        this.opTime = opTime;
+    }
+
+    public Date getOpTime() 
+    {
+        return opTime;
+    }
+
+    public void setIp(String ip) 
+    {
+        this.ip = ip;
+    }
+
+    public String getIp() 
+    {
+        return ip;
+    }
+
+    public void setExtra(String extra) 
+    {
+        this.extra = extra;
+    }
+
+    public String getExtra() 
+    {
+        return extra;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("logId", getLogId())
+            .append("userId", getUserId())
+            .append("userName", getUserName())
+            .append("opType", getOpType())
+            .append("resourceType", getResourceType())
+            .append("resourceId", getResourceId())
+            .append("opDetail", getOpDetail())
+            .append("opTime", getOpTime())
+            .append("ip", getIp())
+            .append("extra", getExtra())
+            .toString();
+    }
+}
+

+ 45 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/mapper/ReligionAuditLogMapper.java

@@ -0,0 +1,45 @@
+package com.ruoyi.project.religion.audit.mapper;
+
+import java.util.List;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+
+/**
+ * 业务审计日志Mapper接口
+ * 
+ * @author ruoyi
+ */
+public interface ReligionAuditLogMapper 
+{
+    /**
+     * 查询业务审计日志列表
+     * 
+     * @param log 业务审计日志
+     * @return 业务审计日志集合
+     */
+    public List<BizAuditLog> selectReligionAuditLogList(BizAuditLog log);
+
+    /**
+     * 根据日志ID查询业务审计日志
+     * 
+     * @param logId 日志ID
+     * @return 业务审计日志
+     */
+    public BizAuditLog selectReligionAuditLogById(Long logId);
+
+    /**
+     * 新增业务审计日志
+     * 
+     * @param log 业务审计日志
+     * @return 结果
+     */
+    public int insertReligionAuditLog(BizAuditLog log);
+
+    /**
+     * 批量删除业务审计日志
+     * 
+     * @param logIds 需要删除的日志ID
+     * @return 结果
+     */
+    public int deleteReligionAuditLogByIds(Long[] logIds);
+}
+

+ 45 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/service/IReligionAuditLogService.java

@@ -0,0 +1,45 @@
+package com.ruoyi.project.religion.audit.service;
+
+import java.util.List;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+
+/**
+ * 业务审计日志Service接口
+ * 
+ * @author ruoyi
+ */
+public interface IReligionAuditLogService 
+{
+    /**
+     * 查询业务审计日志列表
+     * 
+     * @param log 业务审计日志
+     * @return 业务审计日志集合
+     */
+    public List<BizAuditLog> selectReligionAuditLogList(BizAuditLog log);
+
+    /**
+     * 根据日志ID查询业务审计日志
+     * 
+     * @param logId 日志ID
+     * @return 业务审计日志
+     */
+    public BizAuditLog selectReligionAuditLogById(Long logId);
+
+    /**
+     * 新增业务审计日志
+     * 
+     * @param log 业务审计日志
+     * @return 结果
+     */
+    public int insertReligionAuditLog(BizAuditLog log);
+
+    /**
+     * 批量删除业务审计日志
+     * 
+     * @param logIds 需要删除的日志ID
+     * @return 结果
+     */
+    public int deleteReligionAuditLogByIds(Long[] logIds);
+}
+

+ 69 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/audit/service/impl/ReligionAuditLogServiceImpl.java

@@ -0,0 +1,69 @@
+package com.ruoyi.project.religion.audit.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.religion.audit.mapper.ReligionAuditLogMapper;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+import com.ruoyi.project.religion.audit.service.IReligionAuditLogService;
+
+/**
+ * 业务审计日志Service业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class ReligionAuditLogServiceImpl implements IReligionAuditLogService 
+{
+    @Autowired
+    private ReligionAuditLogMapper religionAuditLogMapper;
+
+    /**
+     * 查询业务审计日志列表
+     * 
+     * @param log 业务审计日志
+     * @return 业务审计日志
+     */
+    @Override
+    public List<BizAuditLog> selectReligionAuditLogList(BizAuditLog log)
+    {
+        return religionAuditLogMapper.selectReligionAuditLogList(log);
+    }
+
+    /**
+     * 根据日志ID查询业务审计日志
+     * 
+     * @param logId 日志ID
+     * @return 业务审计日志
+     */
+    @Override
+    public BizAuditLog selectReligionAuditLogById(Long logId)
+    {
+        return religionAuditLogMapper.selectReligionAuditLogById(logId);
+    }
+
+    /**
+     * 新增业务审计日志
+     * 
+     * @param log 业务审计日志
+     * @return 结果
+     */
+    @Override
+    public int insertReligionAuditLog(BizAuditLog log)
+    {
+        return religionAuditLogMapper.insertReligionAuditLog(log);
+    }
+
+    /**
+     * 批量删除业务审计日志
+     * 
+     * @param logIds 需要删除的日志ID
+     * @return 结果
+     */
+    @Override
+    public int deleteReligionAuditLogByIds(Long[] logIds)
+    {
+        return religionAuditLogMapper.deleteReligionAuditLogByIds(logIds);
+    }
+}
+

+ 352 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/controller/ReligionPersonCheckController.java

@@ -0,0 +1,352 @@
+package com.ruoyi.project.religion.person.controller;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.IpUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.ShiroUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+import com.ruoyi.project.religion.audit.service.IReligionAuditLogService;
+import com.ruoyi.project.religion.person.domain.ReligionPerson;
+import com.ruoyi.project.religion.person.mapper.ReligionPersonMapper;
+import com.ruoyi.project.religion.person.service.IReligionPersonService;
+import com.ruoyi.project.religion.place.service.IReligionPlaceService;
+import org.springframework.ui.ModelMap;
+
+/**
+ * 区县校核工作台(教职人员)Controller
+ * 
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/religion/person/check")
+public class ReligionPersonCheckController extends BaseController
+{
+    private String prefix = "religion/religionPerson";
+
+    @Autowired
+    private IReligionPersonService religionPersonService;
+
+    @Autowired
+    private ReligionPersonMapper religionPersonMapper;
+
+    @Autowired
+    private IReligionAuditLogService religionAuditLogService;
+
+    @Autowired
+    private IReligionPlaceService placeService;
+
+    /**
+     * 跳转到区县校核工作台页面
+     */
+    @RequiresPermissions("religion:religionPerson:check")
+    @GetMapping()
+    public String personCheck(ModelMap mmap)
+    {
+        mmap.put("placeList", placeService.getPlaceOptions());
+        return prefix + "/personCheck";
+    }
+
+    /**
+     * 查询待校核/已校核教职人员列表
+     */
+    @RequiresPermissions("religion:religionPerson:check")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(ReligionPerson religionPerson)
+    {
+        startPage();
+        List<ReligionPerson> list = religionPersonService.selectReligionPersonList(religionPerson);
+        return getDataTable(list);
+    }
+
+    /**
+     * 单条校核通过
+     */
+    @RequiresPermissions("religion:religionPerson:check")
+    @Log(title = "区县校核-通过", businessType = BusinessType.UPDATE)
+    @PostMapping("/pass")
+    @ResponseBody
+    public AjaxResult pass(@RequestBody JSONObject params)
+    {
+        Long personId = params.getLong("personId");
+        String comment = params.getString("comment");
+
+        if (personId == null)
+        {
+            return AjaxResult.error("人员ID不能为空");
+        }
+
+        // 查询当前记录
+        ReligionPerson person = religionPersonService.selectReligionPersonByPersonId(personId);
+        if (person == null)
+        {
+            return AjaxResult.error("人员不存在");
+        }
+
+        // 校验状态
+        if (!"TO_CHECK".equals(person.getStatus()))
+        {
+            return AjaxResult.error("状态已变更,请刷新后重试");
+        }
+
+        // 获取当前登录用户
+        SysUser currentUser = ShiroUtils.getSysUser();
+        String checkBy = currentUser != null ? currentUser.getLoginName() : "system";
+
+        // 更新校核信息(直接调用 Mapper 更新,避免 Service 层将 status 置空)
+        person.setStatus("CHECKED");
+        person.setCountyCheckStatus("PASS");
+        person.setCountyCheckBy(checkBy);
+        person.setCountyCheckTime(new Date());
+        person.setCountyCheckComment(comment);
+        person.setUpdateTime(new Date());
+
+        int result = religionPersonMapper.updateReligionPerson(person);
+
+        if (result > 0)
+        {
+            // 记录审计日志
+            recordCheckAuditLog("COUNTY_CHECK_PERSON", personId, "PASS", comment);
+            return AjaxResult.success("校核通过");
+        }
+        else
+        {
+            return AjaxResult.error("校核失败");
+        }
+    }
+
+    /**
+     * 单条驳回
+     */
+    @RequiresPermissions("religion:religionPerson:check")
+    @Log(title = "区县校核-驳回", businessType = BusinessType.UPDATE)
+    @PostMapping("/reject")
+    @ResponseBody
+    public AjaxResult reject(@RequestBody JSONObject params)
+    {
+        Long personId = params.getLong("personId");
+        String comment = params.getString("comment");
+
+        if (personId == null)
+        {
+            return AjaxResult.error("人员ID不能为空");
+        }
+
+        if (StringUtils.isEmpty(comment))
+        {
+            return AjaxResult.error("驳回原因不能为空");
+        }
+
+        // 查询当前记录
+        ReligionPerson person = religionPersonService.selectReligionPersonByPersonId(personId);
+        if (person == null)
+        {
+            return AjaxResult.error("人员不存在");
+        }
+
+        // 校验状态
+        if (!"TO_CHECK".equals(person.getStatus()))
+        {
+            return AjaxResult.error("状态已变更,请刷新后重试");
+        }
+
+        // 获取当前登录用户
+        SysUser currentUser = ShiroUtils.getSysUser();
+        String checkBy = currentUser != null ? currentUser.getLoginName() : "system";
+
+        // 更新校核信息(状态仍保持 TO_CHECK,直接调用 Mapper 更新)
+        person.setCountyCheckStatus("REJECT");
+        person.setCountyCheckBy(checkBy);
+        person.setCountyCheckTime(new Date());
+        person.setCountyCheckComment(comment);
+        person.setUpdateTime(new Date());
+
+        int result = religionPersonMapper.updateReligionPerson(person);
+
+        if (result > 0)
+        {
+            // 记录审计日志
+            recordCheckAuditLog("COUNTY_REJECT_PERSON", personId, "REJECT", comment);
+            return AjaxResult.success("已驳回");
+        }
+        else
+        {
+            return AjaxResult.error("驳回失败");
+        }
+    }
+
+    /**
+     * 批量通过校核
+     */
+    @RequiresPermissions("religion:religionPerson:check")
+    @Log(title = "区县校核-批量通过", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchPass")
+    @ResponseBody
+    public AjaxResult batchPass(@RequestBody JSONObject params)
+    {
+        String personIdsStr = params.getString("personIds");
+        String comment = params.getString("comment");
+
+        if (StringUtils.isEmpty(personIdsStr))
+        {
+            return AjaxResult.error("请选择要校核的人员");
+        }
+
+        Long[] personIds = Convert.toLongArray(personIdsStr);
+        if (personIds == null || personIds.length == 0)
+        {
+            return AjaxResult.error("请选择要校核的人员");
+        }
+
+        // 获取当前登录用户
+        SysUser currentUser = ShiroUtils.getSysUser();
+        String checkBy = currentUser != null ? currentUser.getLoginName() : "system";
+
+        int totalSelected = personIds.length;
+        int successCount = 0;
+        int skipCount = 0;
+
+        // 逐条处理
+        for (Long personId : personIds)
+        {
+            ReligionPerson person = religionPersonService.selectReligionPersonByPersonId(personId);
+            
+            // 只处理状态为 TO_CHECK 的记录
+            if (person != null && "TO_CHECK".equals(person.getStatus()))
+            {
+                person.setStatus("CHECKED");
+                person.setCountyCheckStatus("PASS");
+                person.setCountyCheckBy(checkBy);
+                person.setCountyCheckTime(new Date());
+                person.setUpdateTime(new Date());
+                
+                // 如果传入了统一备注,使用统一备注;否则保持原值
+                if (StringUtils.isNotEmpty(comment))
+                {
+                    person.setCountyCheckComment(comment);
+                }
+
+                // 直接调用 Mapper 更新,避免 Service 层将 status 置空
+                int result = religionPersonMapper.updateReligionPerson(person);
+                if (result > 0)
+                {
+                    successCount++;
+                }
+            }
+            else
+            {
+                skipCount++;
+            }
+        }
+
+        // 记录批量审计日志
+        recordBatchCheckAuditLog(totalSelected, successCount, skipCount, comment);
+
+        return AjaxResult.success("批量校核完成,成功:" + successCount + " 条,跳过:" + skipCount + " 条");
+    }
+
+    /**
+     * 记录单条校核审计日志
+     */
+    private void recordCheckAuditLog(String opType, Long personId, String action, String comment)
+    {
+        try
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            auditLog.setOpType(opType);
+            auditLog.setResourceType("person_check");
+            auditLog.setResourceId(String.valueOf(personId));
+            auditLog.setOpTime(new Date());
+            auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+            
+            // 构造详情JSON
+            JSONObject detail = new JSONObject();
+            detail.put("action", action);
+            if (StringUtils.isNotEmpty(comment))
+            {
+                detail.put("comment", comment);
+            }
+            
+            auditLog.setOpDetail(detail.toJSONString());
+            
+            religionAuditLogService.insertReligionAuditLog(auditLog);
+        }
+        catch (Exception e)
+        {
+            logger.error("记录校核审计日志失败", e);
+        }
+    }
+
+    /**
+     * 记录批量校核审计日志
+     */
+    private void recordBatchCheckAuditLog(int totalSelected, int successCount, int skipCount, String comment)
+    {
+        try
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            auditLog.setOpType("COUNTY_BATCH_CHECK_PERSON");
+            auditLog.setResourceType("person_check");
+            auditLog.setResourceId("BATCH");
+            auditLog.setOpTime(new Date());
+            auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+            
+            // 构造概要JSON
+            JSONObject detail = new JSONObject();
+            detail.put("action", "BATCH_PASS");
+            detail.put("totalSelected", totalSelected);
+            detail.put("successCount", successCount);
+            detail.put("skipCount", skipCount);
+            if (StringUtils.isNotEmpty(comment))
+            {
+                detail.put("comment", comment);
+            }
+            
+            auditLog.setOpDetail(detail.toJSONString());
+            
+            religionAuditLogService.insertReligionAuditLog(auditLog);
+        }
+        catch (Exception e)
+        {
+            logger.error("记录批量校核审计日志失败", e);
+        }
+    }
+}
+

+ 65 - 2
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/controller/ReligionPersonController.java

@@ -8,11 +8,14 @@ import org.springframework.ui.ModelMap;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.project.religion.person.domain.ReligionPerson;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdatePreviewResponse;
 import com.ruoyi.project.religion.person.service.IReligionPersonService;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -34,10 +37,14 @@ public class ReligionPersonController extends BaseController
     @Autowired
     private IReligionPersonService religionPersonService;
 
+    @Autowired
+    private com.ruoyi.project.religion.place.service.IReligionPlaceService placeService;
+
     @RequiresPermissions("religion:religionPerson:view")
     @GetMapping()
-    public String religionPerson()
+    public String religionPerson(ModelMap mmap)
     {
+        mmap.put("placeList", placeService.getPlaceOptions());
         return prefix + "/religionPerson";
     }
 
@@ -73,8 +80,9 @@ public class ReligionPersonController extends BaseController
      */
     @RequiresPermissions("religion:religionPerson:add")
     @GetMapping("/add")
-    public String add()
+    public String add(ModelMap mmap)
     {
+        mmap.put("placeList", placeService.getPlaceOptions());
         return prefix + "/add";
     }
 
@@ -99,6 +107,7 @@ public class ReligionPersonController extends BaseController
     {
         ReligionPerson religionPerson = religionPersonService.selectReligionPersonByPersonId(personId);
         mmap.put("religionPerson", religionPerson);
+        mmap.put("placeList", placeService.getPlaceOptions());
         return prefix + "/edit";
     }
 
@@ -125,4 +134,58 @@ public class ReligionPersonController extends BaseController
     {
         return toAjax(religionPersonService.deleteReligionPersonByPersonIds(ids));
     }
+
+    /**
+     * 批量维护预览列表(支持分页)
+     */
+    @RequiresPermissions("religion:religionPerson:edit")
+    @PostMapping("/batchPreviewList")
+    @ResponseBody
+    public TableDataInfo batchPreviewList(@RequestBody ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        // 使用前端传入的分页参数,如果没有则使用默认值
+        Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1;
+        Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 20;
+        // 设置分页参数
+        com.github.pagehelper.PageHelper.startPage(pageNum, pageSize);
+        List<ReligionPerson> list = religionPersonService.selectByBatchCondition(req);
+        return getDataTable(list);
+    }
+
+    /**
+     * 批量维护按条件统计数量
+     */
+    @RequiresPermissions("religion:religionPerson:edit")
+    @PostMapping("/batchCountByCondition")
+    @ResponseBody
+    public AjaxResult batchCountByCondition(@RequestBody ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        int count = religionPersonService.countByBatchCondition(req);
+        return AjaxResult.success(count);
+    }
+
+    /**
+     * 批量维护按条件更新
+     */
+    @RequiresPermissions("religion:religionPerson:edit")
+    @Log(title = "教职人员批量维护", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchUpdateByCondition")
+    @ResponseBody
+    public AjaxResult batchUpdateByCondition(@RequestBody ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        int count = religionPersonService.batchUpdateByCondition(req);
+        return AjaxResult.success("批量更新成功", count);
+    }
+
+    /**
+     * 批量更新预览
+     */
+    @RequiresPermissions("religion:religionPerson:edit")
+    @PostMapping("/batchUpdatePreview")
+    @ResponseBody
+    public AjaxResult batchUpdatePreview(@RequestBody ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        ReligionPersonBatchUpdatePreviewResponse response = religionPersonService.buildBatchUpdatePreview(req);
+        return AjaxResult.success(response);
+    }
 }

+ 220 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/controller/ReligionPersonImportController.java

@@ -0,0 +1,220 @@
+package com.ruoyi.project.religion.person.controller;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.project.religion.person.domain.ReligionImportBatch;
+import com.ruoyi.project.religion.person.service.IReligionPersonImportService;
+
+/**
+ * 教职人员导入Controller
+ * 
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/religion/person/import")
+public class ReligionPersonImportController extends BaseController
+{
+    private String prefix = "religion/person";
+
+    @Autowired
+    private IReligionPersonImportService importService;
+
+    /**
+     * 跳转到导入页面
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @GetMapping()
+    public String personImport()
+    {
+        return prefix + "/personImport";
+    }
+
+    /**
+     * 查询导入批次列表
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @PostMapping("/batch/list")
+    @ResponseBody
+    public TableDataInfo batchList(ReligionImportBatch batch)
+    {
+        startPage();
+        List<ReligionImportBatch> list = importService.selectBatchList(batch);
+        return getDataTable(list);
+    }
+
+    /**
+     * 上传并解析Excel文件
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @Log(title = "教职人员导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/upload")
+    @ResponseBody
+    public AjaxResult upload(@RequestParam("file") MultipartFile file)
+    {
+        try
+        {
+            if (file.isEmpty())
+            {
+                return AjaxResult.error("上传文件不能为空");
+            }
+
+            String fileName = file.getOriginalFilename();
+            if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))
+            {
+                return AjaxResult.error("只支持Excel文件格式(.xlsx或.xls)");
+            }
+
+            Map<String, Object> result = importService.uploadAndParseExcel(file);
+            return AjaxResult.success("上传成功", result);
+        }
+        catch (Exception e)
+        {
+            logger.error("上传Excel文件失败", e);
+            return AjaxResult.error("上传失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询导入批次信息
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @GetMapping("/batch")
+    @ResponseBody
+    public AjaxResult getBatch(@RequestParam("batchId") Long batchId)
+    {
+        ReligionImportBatch batch = importService.selectBatchById(batchId);
+        if (batch == null)
+        {
+            return AjaxResult.error("批次不存在");
+        }
+        return AjaxResult.success(batch);
+    }
+
+    /**
+     * 查询导入项列表(分页)
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @GetMapping("/items")
+    @ResponseBody
+    public TableDataInfo getItems(
+            @RequestParam("batchId") Long batchId,
+            @RequestParam(value = "status", required = false) String status)
+    {
+        if (batchId == null)
+        {
+            return getDataTable(null);
+        }
+
+        // 打印请求参数,用于调试
+        logger.info("查询导入项列表 - batchId: {}, status: {}, pageNum: {}, pageSize: {}", 
+                    batchId, status, 
+                    ServletUtils.getParameter("pageNum"), 
+                    ServletUtils.getParameter("pageSize"));
+
+        startPage();
+        List<Map<String, Object>> list = importService.selectImportItems(batchId, status);
+        
+        logger.info("查询结果 - list.size: {}", list.size());
+        
+        TableDataInfo result = getDataTable(list);
+        logger.info("返回数据 - total: {}, rows.size: {}", result.getTotal(), result.getRows().size());
+        
+        return result;
+    }
+
+    /**
+     * 设置导入项的处理动作
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @Log(title = "教职人员导入-设置动作", businessType = BusinessType.UPDATE)
+    @PostMapping("/item/{itemId}/action")
+    @ResponseBody
+    public AjaxResult setAction(
+            @PathVariable("itemId") Long itemId,
+            @RequestBody Map<String, String> params)
+    {
+        try
+        {
+            String action = params.get("action");
+            if (action == null)
+            {
+                return AjaxResult.error("处理动作不能为空");
+            }
+
+            Map<String, Object> result = importService.setItemAction(itemId, action);
+            return AjaxResult.success("设置成功", result);
+        }
+        catch (Exception e)
+        {
+            logger.error("设置导入项动作失败", e);
+            return AjaxResult.error("设置失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 手工编辑导入项数据
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @Log(title = "教职人员导入-手工编辑", businessType = BusinessType.UPDATE)
+    @PostMapping("/item/{itemId}/edit")
+    @ResponseBody
+    public AjaxResult editItem(
+            @PathVariable("itemId") Long itemId,
+            @RequestBody Map<String, Object> editData)
+    {
+        try
+        {
+            if (editData == null || editData.isEmpty())
+            {
+                return AjaxResult.error("编辑数据不能为空");
+            }
+
+            Map<String, Object> result = importService.editItemData(itemId, editData);
+            return AjaxResult.success("编辑成功", result);
+        }
+        catch (Exception e)
+        {
+            logger.error("编辑导入项失败", e);
+            return AjaxResult.error("编辑失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 提交批次导入
+     */
+    @RequiresPermissions("religion:religionPerson:import")
+    @Log(title = "教职人员导入-提交导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/batch/{batchId}/commit")
+    @ResponseBody
+    public AjaxResult commitImport(@PathVariable("batchId") Long batchId)
+    {
+        try
+        {
+            Map<String, Object> result = importService.commitBatchImport(batchId);
+            return AjaxResult.success("导入成功", result);
+        }
+        catch (Exception e)
+        {
+            logger.error("提交批次导入失败", e);
+            return AjaxResult.error("导入失败:" + e.getMessage());
+        }
+    }
+}
+

+ 223 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/ReligionImportBatch.java

@@ -0,0 +1,223 @@
+package com.ruoyi.project.religion.person.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 教职人员导入批次对象 religion_import_batch
+ * 
+ * @author ruoyi
+ */
+public class ReligionImportBatch extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 批次ID */
+    private Long batchId;
+
+    /** 批次号 */
+    private String batchNo;
+
+    /** 文件名 */
+    private String fileName;
+
+    /** 上传人 */
+    private String uploader;
+
+    /** 上传时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date uploadTime;
+
+    /** 最后处理时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date lastProcessTime;
+
+    /** 总行数 */
+    private Integer totalRows;
+
+    /** 有效行数 */
+    private Integer validRows;
+
+    /** 错误行数 */
+    private Integer errorRows;
+
+    /** 冲突行数 */
+    private Integer conflictRows;
+
+    /** 已导入行数 */
+    private Integer importedRows;
+
+    /** 已放弃行数 */
+    private Integer abandonedRows;
+
+    /** 状态 */
+    private String status;
+
+    /** 扩展字段 */
+    private String extra;
+
+    public Long getBatchId()
+    {
+        return batchId;
+    }
+
+    public void setBatchId(Long batchId)
+    {
+        this.batchId = batchId;
+    }
+
+    public String getBatchNo()
+    {
+        return batchNo;
+    }
+
+    public void setBatchNo(String batchNo)
+    {
+        this.batchNo = batchNo;
+    }
+
+    public String getFileName()
+    {
+        return fileName;
+    }
+
+    public void setFileName(String fileName)
+    {
+        this.fileName = fileName;
+    }
+
+    public String getUploader()
+    {
+        return uploader;
+    }
+
+    public void setUploader(String uploader)
+    {
+        this.uploader = uploader;
+    }
+
+    public Date getUploadTime()
+    {
+        return uploadTime;
+    }
+
+    public void setUploadTime(Date uploadTime)
+    {
+        this.uploadTime = uploadTime;
+    }
+
+    public Date getLastProcessTime()
+    {
+        return lastProcessTime;
+    }
+
+    public void setLastProcessTime(Date lastProcessTime)
+    {
+        this.lastProcessTime = lastProcessTime;
+    }
+
+    public Integer getTotalRows()
+    {
+        return totalRows;
+    }
+
+    public void setTotalRows(Integer totalRows)
+    {
+        this.totalRows = totalRows;
+    }
+
+    public Integer getValidRows()
+    {
+        return validRows;
+    }
+
+    public void setValidRows(Integer validRows)
+    {
+        this.validRows = validRows;
+    }
+
+    public Integer getErrorRows()
+    {
+        return errorRows;
+    }
+
+    public void setErrorRows(Integer errorRows)
+    {
+        this.errorRows = errorRows;
+    }
+
+    public Integer getConflictRows()
+    {
+        return conflictRows;
+    }
+
+    public void setConflictRows(Integer conflictRows)
+    {
+        this.conflictRows = conflictRows;
+    }
+
+    public Integer getImportedRows()
+    {
+        return importedRows;
+    }
+
+    public void setImportedRows(Integer importedRows)
+    {
+        this.importedRows = importedRows;
+    }
+
+    public Integer getAbandonedRows()
+    {
+        return abandonedRows;
+    }
+
+    public void setAbandonedRows(Integer abandonedRows)
+    {
+        this.abandonedRows = abandonedRows;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getExtra()
+    {
+        return extra;
+    }
+
+    public void setExtra(String extra)
+    {
+        this.extra = extra;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("batchId", getBatchId())
+            .append("batchNo", getBatchNo())
+            .append("fileName", getFileName())
+            .append("uploader", getUploader())
+            .append("uploadTime", getUploadTime())
+            .append("lastProcessTime", getLastProcessTime())
+            .append("totalRows", getTotalRows())
+            .append("validRows", getValidRows())
+            .append("errorRows", getErrorRows())
+            .append("conflictRows", getConflictRows())
+            .append("importedRows", getImportedRows())
+            .append("abandonedRows", getAbandonedRows())
+            .append("status", getStatus())
+            .append("extra", getExtra())
+            .toString();
+    }
+}
+

+ 181 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/ReligionImportItem.java

@@ -0,0 +1,181 @@
+package com.ruoyi.project.religion.person.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 教职人员导入项对象 religion_import_item
+ * 
+ * @author ruoyi
+ */
+public class ReligionImportItem extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 导入项ID */
+    private Long itemId;
+
+    /** 批次ID */
+    private Long batchId;
+
+    /** 行号 */
+    private Integer rowNo;
+
+    /** JSON数据 */
+    private String jsonData;
+
+    /** 错误信息 */
+    private String errorMsg;
+
+    /** 冲突标记 */
+    private Integer conflictFlag;
+
+    /** 状态 */
+    private String status;
+
+    /** 操作 */
+    private String action;
+
+    /** 操作人 */
+    private String actionBy;
+
+    /** 操作时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date actionTime;
+
+    /** 处理标记 */
+    private Integer processedFlag;
+
+    public Long getItemId()
+    {
+        return itemId;
+    }
+
+    public void setItemId(Long itemId)
+    {
+        this.itemId = itemId;
+    }
+
+    public Long getBatchId()
+    {
+        return batchId;
+    }
+
+    public void setBatchId(Long batchId)
+    {
+        this.batchId = batchId;
+    }
+
+    public Integer getRowNo()
+    {
+        return rowNo;
+    }
+
+    public void setRowNo(Integer rowNo)
+    {
+        this.rowNo = rowNo;
+    }
+
+    public String getJsonData()
+    {
+        return jsonData;
+    }
+
+    public void setJsonData(String jsonData)
+    {
+        this.jsonData = jsonData;
+    }
+
+    public String getErrorMsg()
+    {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg)
+    {
+        this.errorMsg = errorMsg;
+    }
+
+    public Integer getConflictFlag()
+    {
+        return conflictFlag;
+    }
+
+    public void setConflictFlag(Integer conflictFlag)
+    {
+        this.conflictFlag = conflictFlag;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getAction()
+    {
+        return action;
+    }
+
+    public void setAction(String action)
+    {
+        this.action = action;
+    }
+
+    public String getActionBy()
+    {
+        return actionBy;
+    }
+
+    public void setActionBy(String actionBy)
+    {
+        this.actionBy = actionBy;
+    }
+
+    public Date getActionTime()
+    {
+        return actionTime;
+    }
+
+    public void setActionTime(Date actionTime)
+    {
+        this.actionTime = actionTime;
+    }
+
+    public Integer getProcessedFlag()
+    {
+        return processedFlag;
+    }
+
+    public void setProcessedFlag(Integer processedFlag)
+    {
+        this.processedFlag = processedFlag;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("itemId", getItemId())
+            .append("batchId", getBatchId())
+            .append("rowNo", getRowNo())
+            .append("jsonData", getJsonData())
+            .append("errorMsg", getErrorMsg())
+            .append("conflictFlag", getConflictFlag())
+            .append("status", getStatus())
+            .append("action", getAction())
+            .append("actionBy", getActionBy())
+            .append("actionTime", getActionTime())
+            .append("processedFlag", getProcessedFlag())
+            .append("createTime", getCreateTime())
+            .toString();
+    }
+}
+

+ 87 - 4
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/ReligionPerson.java

@@ -21,9 +21,13 @@ public class ReligionPerson extends BaseEntity
     private Long personId;
 
     /** 所属场所ID(外键,关联 religion_place.place_id) */
-    @Excel(name = "所属场所ID", readConverterExp = "外=键,关联,r=eligion_place.place_id")
+    @Excel(name = "所属场所ID")
     private Long placeId;
 
+    /** 所属场所名称(非持久化字段,用于前端显示) */
+    @Excel(name = "所属场所")
+    private String placeName;
+
     /** 姓名 */
     @Excel(name = "姓名")
     private String name;
@@ -36,6 +40,10 @@ public class ReligionPerson extends BaseEntity
     @Excel(name = "性别")
     private String gender;
 
+    /** 民族 */
+    @Excel(name = "民族", dictType = "sys_nation")
+    private String nation;
+
     /** 身份证号 */
     @Excel(name = "身份证号")
     private String idNumber;
@@ -83,17 +91,30 @@ public class ReligionPerson extends BaseEntity
     private String nativePlace;
 
     /** 所属宫观名称(Excel 原始值,仅留档) */
-    @Excel(name = "所属宫观名称", readConverterExp = "E=xcel,原=始值,仅留档")
+    @Excel(name = "所属宫观名称")
     private String orgName;
 
     /** 所属宫观地址(Excel 原始值,仅留档) */
-    @Excel(name = "所属宫观地址", readConverterExp = "E=xcel,原=始值,仅留档")
+    @Excel(name = "所属宫观地址")
     private String orgAddress;
 
     /** 状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报 */
-    @Excel(name = "状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报")
+    @Excel(name = "状态", readConverterExp = "TO_CHECK=待校核,CHECKED=已校核,REPORTED=已上报")
     private String status;
 
+    /** 区县校核状态:NONE未校核/PASS通过/REJECT驳回 */
+    private String countyCheckStatus;
+
+    /** 区县校核人 */
+    private String countyCheckBy;
+
+    /** 区县校核时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date countyCheckTime;
+
+    /** 区县校核意见 */
+    private String countyCheckComment;
+
     public void setPersonId(Long personId) 
     {
         this.personId = personId;
@@ -114,6 +135,16 @@ public class ReligionPerson extends BaseEntity
         return placeId;
     }
 
+    public void setPlaceName(String placeName) 
+    {
+        this.placeName = placeName;
+    }
+
+    public String getPlaceName() 
+    {
+        return placeName;
+    }
+
     public void setName(String name) 
     {
         this.name = name;
@@ -144,6 +175,16 @@ public class ReligionPerson extends BaseEntity
         return gender;
     }
 
+    public void setNation(String nation) 
+    {
+        this.nation = nation;
+    }
+
+    public String getNation() 
+    {
+        return nation;
+    }
+
     public void setIdNumber(String idNumber) 
     {
         this.idNumber = idNumber;
@@ -284,14 +325,56 @@ public class ReligionPerson extends BaseEntity
         return status;
     }
 
+    public void setCountyCheckStatus(String countyCheckStatus) 
+    {
+        this.countyCheckStatus = countyCheckStatus;
+    }
+
+    public String getCountyCheckStatus() 
+    {
+        return countyCheckStatus;
+    }
+
+    public void setCountyCheckBy(String countyCheckBy) 
+    {
+        this.countyCheckBy = countyCheckBy;
+    }
+
+    public String getCountyCheckBy() 
+    {
+        return countyCheckBy;
+    }
+
+    public void setCountyCheckTime(Date countyCheckTime) 
+    {
+        this.countyCheckTime = countyCheckTime;
+    }
+
+    public Date getCountyCheckTime() 
+    {
+        return countyCheckTime;
+    }
+
+    public void setCountyCheckComment(String countyCheckComment) 
+    {
+        this.countyCheckComment = countyCheckComment;
+    }
+
+    public String getCountyCheckComment() 
+    {
+        return countyCheckComment;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
             .append("personId", getPersonId())
             .append("placeId", getPlaceId())
+            .append("placeName", getPlaceName())
             .append("name", getName())
             .append("religionType", getReligionType())
             .append("gender", getGender())
+            .append("nation", getNation())
             .append("idNumber", getIdNumber())
             .append("birthday", getBirthday())
             .append("county", getCounty())

+ 354 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/dto/ReligionPersonBatchUpdateByConditionRequest.java

@@ -0,0 +1,354 @@
+package com.ruoyi.project.religion.person.domain.dto;
+
+import java.util.Date;
+
+/**
+ * 教职人员批量维护请求DTO
+ * 
+ * @author ruoyi
+ */
+public class ReligionPersonBatchUpdateByConditionRequest
+{
+    // ========== 分页参数 ==========
+    /** 当前页码 */
+    private Integer pageNum;
+    
+    /** 每页条数 */
+    private Integer pageSize;
+
+    // ========== 筛选条件 ==========
+    /** 姓名 */
+    private String name;
+    
+    /** 身份证号 */
+    private String idNumber;
+    
+    /** 所属场所ID */
+    private Long placeId;
+    
+    /** 宗教类别 */
+    private String religionType;
+    
+    /** 性别 */
+    private String gender;
+    
+    /** 民族 */
+    private String nation;
+    
+    /** 所属区县 */
+    private String county;
+    
+    /** 状态 */
+    private String status;
+
+    // ========== 批量更新字段 ==========
+    /** 新-所属场所ID */
+    private Long newPlaceId;
+    
+    /** 新-宗教类别 */
+    private String newReligionType;
+    
+    /** 新-性别 */
+    private String newGender;
+    
+    /** 新-民族 */
+    private String newNation;
+    
+    /** 新-出生日期 */
+    private Date newBirthday;
+    
+    /** 新-所属区县 */
+    private String newCounty;
+    
+    /** 新-家庭地址 */
+    private String newAddress;
+    
+    /** 新-政治面貌 */
+    private String newPoliticalStatus;
+    
+    /** 新-国民教育学历 */
+    private String newEduNational;
+    
+    /** 新-宗教学历 */
+    private String newEduReligious;
+    
+    /** 新-入教/出家时间 */
+    private Date newJoinTime;
+    
+    /** 新-联系电话 */
+    private String newPhone;
+    
+    /** 新-户籍所在地 */
+    private String newDomicile;
+    
+    /** 新-籍贯 */
+    private String newNativePlace;
+    
+    /** 新-所属宫观名称 */
+    private String newOrgName;
+    
+    /** 新-所属宫观地址 */
+    private String newOrgAddress;
+
+    // ========== Getter/Setter ==========
+    public Integer getPageNum()
+    {
+        return pageNum;
+    }
+
+    public void setPageNum(Integer pageNum)
+    {
+        this.pageNum = pageNum;
+    }
+
+    public Integer getPageSize()
+    {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize)
+    {
+        this.pageSize = pageSize;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getIdNumber()
+    {
+        return idNumber;
+    }
+
+    public void setIdNumber(String idNumber)
+    {
+        this.idNumber = idNumber;
+    }
+
+    public Long getPlaceId()
+    {
+        return placeId;
+    }
+
+    public void setPlaceId(Long placeId)
+    {
+        this.placeId = placeId;
+    }
+
+    public String getReligionType()
+    {
+        return religionType;
+    }
+
+    public void setReligionType(String religionType)
+    {
+        this.religionType = religionType;
+    }
+
+    public String getGender()
+    {
+        return gender;
+    }
+
+    public void setGender(String gender)
+    {
+        this.gender = gender;
+    }
+
+    public String getNation()
+    {
+        return nation;
+    }
+
+    public void setNation(String nation)
+    {
+        this.nation = nation;
+    }
+
+    public String getCounty()
+    {
+        return county;
+    }
+
+    public void setCounty(String county)
+    {
+        this.county = county;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public Long getNewPlaceId()
+    {
+        return newPlaceId;
+    }
+
+    public void setNewPlaceId(Long newPlaceId)
+    {
+        this.newPlaceId = newPlaceId;
+    }
+
+    public String getNewReligionType()
+    {
+        return newReligionType;
+    }
+
+    public void setNewReligionType(String newReligionType)
+    {
+        this.newReligionType = newReligionType;
+    }
+
+    public String getNewGender()
+    {
+        return newGender;
+    }
+
+    public void setNewGender(String newGender)
+    {
+        this.newGender = newGender;
+    }
+
+    public String getNewNation()
+    {
+        return newNation;
+    }
+
+    public void setNewNation(String newNation)
+    {
+        this.newNation = newNation;
+    }
+
+    public Date getNewBirthday()
+    {
+        return newBirthday;
+    }
+
+    public void setNewBirthday(Date newBirthday)
+    {
+        this.newBirthday = newBirthday;
+    }
+
+    public String getNewCounty()
+    {
+        return newCounty;
+    }
+
+    public void setNewCounty(String newCounty)
+    {
+        this.newCounty = newCounty;
+    }
+
+    public String getNewAddress()
+    {
+        return newAddress;
+    }
+
+    public void setNewAddress(String newAddress)
+    {
+        this.newAddress = newAddress;
+    }
+
+    public String getNewPoliticalStatus()
+    {
+        return newPoliticalStatus;
+    }
+
+    public void setNewPoliticalStatus(String newPoliticalStatus)
+    {
+        this.newPoliticalStatus = newPoliticalStatus;
+    }
+
+    public String getNewEduNational()
+    {
+        return newEduNational;
+    }
+
+    public void setNewEduNational(String newEduNational)
+    {
+        this.newEduNational = newEduNational;
+    }
+
+    public String getNewEduReligious()
+    {
+        return newEduReligious;
+    }
+
+    public void setNewEduReligious(String newEduReligious)
+    {
+        this.newEduReligious = newEduReligious;
+    }
+
+    public Date getNewJoinTime()
+    {
+        return newJoinTime;
+    }
+
+    public void setNewJoinTime(Date newJoinTime)
+    {
+        this.newJoinTime = newJoinTime;
+    }
+
+    public String getNewPhone()
+    {
+        return newPhone;
+    }
+
+    public void setNewPhone(String newPhone)
+    {
+        this.newPhone = newPhone;
+    }
+
+    public String getNewDomicile()
+    {
+        return newDomicile;
+    }
+
+    public void setNewDomicile(String newDomicile)
+    {
+        this.newDomicile = newDomicile;
+    }
+
+    public String getNewNativePlace()
+    {
+        return newNativePlace;
+    }
+
+    public void setNewNativePlace(String newNativePlace)
+    {
+        this.newNativePlace = newNativePlace;
+    }
+
+    public String getNewOrgName()
+    {
+        return newOrgName;
+    }
+
+    public void setNewOrgName(String newOrgName)
+    {
+        this.newOrgName = newOrgName;
+    }
+
+    public String getNewOrgAddress()
+    {
+        return newOrgAddress;
+    }
+
+    public void setNewOrgAddress(String newOrgAddress)
+    {
+        this.newOrgAddress = newOrgAddress;
+    }
+}
+

+ 189 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/domain/dto/ReligionPersonBatchUpdatePreviewResponse.java

@@ -0,0 +1,189 @@
+package com.ruoyi.project.religion.person.domain.dto;
+
+import java.util.List;
+
+/**
+ * 批量更新预览响应DTO
+ * 
+ * @author ruoyi
+ */
+public class ReligionPersonBatchUpdatePreviewResponse
+{
+    /** 受影响人员数量 */
+    private Integer totalPersons;
+    
+    /** 字段变更记录条数 */
+    private Integer totalChanges;
+    
+    /** 变更明细列表 */
+    private List<ChangeDetail> rows;
+
+    public ReligionPersonBatchUpdatePreviewResponse()
+    {
+    }
+
+    public ReligionPersonBatchUpdatePreviewResponse(Integer totalPersons, Integer totalChanges, List<ChangeDetail> rows)
+    {
+        this.totalPersons = totalPersons;
+        this.totalChanges = totalChanges;
+        this.rows = rows;
+    }
+
+    public Integer getTotalPersons()
+    {
+        return totalPersons;
+    }
+
+    public void setTotalPersons(Integer totalPersons)
+    {
+        this.totalPersons = totalPersons;
+    }
+
+    public Integer getTotalChanges()
+    {
+        return totalChanges;
+    }
+
+    public void setTotalChanges(Integer totalChanges)
+    {
+        this.totalChanges = totalChanges;
+    }
+
+    public List<ChangeDetail> getRows()
+    {
+        return rows;
+    }
+
+    public void setRows(List<ChangeDetail> rows)
+    {
+        this.rows = rows;
+    }
+
+    /**
+     * 变更明细
+     */
+    public static class ChangeDetail
+    {
+        /** 人员ID */
+        private Long personId;
+        
+        /** 姓名 */
+        private String name;
+        
+        /** 所属场所ID */
+        private Long placeId;
+        
+        /** 所属场所名称 */
+        private String placeName;
+        
+        /** 字段key */
+        private String fieldKey;
+        
+        /** 字段标签 */
+        private String fieldLabel;
+        
+        /** 旧值 */
+        private String oldValue;
+        
+        /** 新值 */
+        private String newValue;
+
+        public ChangeDetail()
+        {
+        }
+
+        public ChangeDetail(Long personId, String name, Long placeId, String placeName, 
+                           String fieldKey, String fieldLabel, String oldValue, String newValue)
+        {
+            this.personId = personId;
+            this.name = name;
+            this.placeId = placeId;
+            this.placeName = placeName;
+            this.fieldKey = fieldKey;
+            this.fieldLabel = fieldLabel;
+            this.oldValue = oldValue;
+            this.newValue = newValue;
+        }
+
+        public Long getPersonId()
+        {
+            return personId;
+        }
+
+        public void setPersonId(Long personId)
+        {
+            this.personId = personId;
+        }
+
+        public String getName()
+        {
+            return name;
+        }
+
+        public void setName(String name)
+        {
+            this.name = name;
+        }
+
+        public Long getPlaceId()
+        {
+            return placeId;
+        }
+
+        public void setPlaceId(Long placeId)
+        {
+            this.placeId = placeId;
+        }
+
+        public String getPlaceName()
+        {
+            return placeName;
+        }
+
+        public void setPlaceName(String placeName)
+        {
+            this.placeName = placeName;
+        }
+
+        public String getFieldKey()
+        {
+            return fieldKey;
+        }
+
+        public void setFieldKey(String fieldKey)
+        {
+            this.fieldKey = fieldKey;
+        }
+
+        public String getFieldLabel()
+        {
+            return fieldLabel;
+        }
+
+        public void setFieldLabel(String fieldLabel)
+        {
+            this.fieldLabel = fieldLabel;
+        }
+
+        public String getOldValue()
+        {
+            return oldValue;
+        }
+
+        public void setOldValue(String oldValue)
+        {
+            this.oldValue = oldValue;
+        }
+
+        public String getNewValue()
+        {
+            return newValue;
+        }
+
+        public void setNewValue(String newValue)
+        {
+            this.newValue = newValue;
+        }
+    }
+}
+

+ 61 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/mapper/ReligionImportBatchMapper.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.religion.person.mapper;
+
+import java.util.List;
+import com.ruoyi.project.religion.person.domain.ReligionImportBatch;
+
+/**
+ * 教职人员导入批次Mapper接口
+ * 
+ * @author ruoyi
+ */
+public interface ReligionImportBatchMapper 
+{
+    /**
+     * 查询教职人员导入批次
+     * 
+     * @param batchId 教职人员导入批次主键
+     * @return 教职人员导入批次
+     */
+    public ReligionImportBatch selectReligionImportBatchByBatchId(Long batchId);
+
+    /**
+     * 查询教职人员导入批次列表
+     * 
+     * @param religionImportBatch 教职人员导入批次
+     * @return 教职人员导入批次集合
+     */
+    public List<ReligionImportBatch> selectReligionImportBatchList(ReligionImportBatch religionImportBatch);
+
+    /**
+     * 新增教职人员导入批次
+     * 
+     * @param religionImportBatch 教职人员导入批次
+     * @return 结果
+     */
+    public int insertReligionImportBatch(ReligionImportBatch religionImportBatch);
+
+    /**
+     * 修改教职人员导入批次
+     * 
+     * @param religionImportBatch 教职人员导入批次
+     * @return 结果
+     */
+    public int updateReligionImportBatch(ReligionImportBatch religionImportBatch);
+
+    /**
+     * 删除教职人员导入批次
+     * 
+     * @param batchId 教职人员导入批次主键
+     * @return 结果
+     */
+    public int deleteReligionImportBatchByBatchId(Long batchId);
+
+    /**
+     * 批量删除教职人员导入批次
+     * 
+     * @param batchIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteReligionImportBatchByBatchIds(Long[] batchIds);
+}
+

+ 103 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/mapper/ReligionImportItemMapper.java

@@ -0,0 +1,103 @@
+package com.ruoyi.project.religion.person.mapper;
+
+import java.util.List;
+import com.ruoyi.project.religion.person.domain.ReligionImportItem;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 教职人员导入项Mapper接口
+ * 
+ * @author ruoyi
+ */
+public interface ReligionImportItemMapper 
+{
+    /**
+     * 查询教职人员导入项
+     * 
+     * @param itemId 教职人员导入项主键
+     * @return 教职人员导入项
+     */
+    public ReligionImportItem selectReligionImportItemByItemId(Long itemId);
+
+    /**
+     * 查询教职人员导入项列表
+     * 
+     * @param religionImportItem 教职人员导入项
+     * @return 教职人员导入项集合
+     */
+    public List<ReligionImportItem> selectReligionImportItemList(ReligionImportItem religionImportItem);
+
+    /**
+     * 新增教职人员导入项
+     * 
+     * @param religionImportItem 教职人员导入项
+     * @return 结果
+     */
+    public int insertReligionImportItem(ReligionImportItem religionImportItem);
+
+    /**
+     * 批量新增教职人员导入项
+     * 
+     * @param items 教职人员导入项列表
+     * @return 结果
+     */
+    public int batchInsertReligionImportItem(List<ReligionImportItem> items);
+
+    /**
+     * 修改教职人员导入项
+     * 
+     * @param religionImportItem 教职人员导入项
+     * @return 结果
+     */
+    public int updateReligionImportItem(ReligionImportItem religionImportItem);
+
+    /**
+     * 删除教职人员导入项
+     * 
+     * @param itemId 教职人员导入项主键
+     * @return 结果
+     */
+    public int deleteReligionImportItemByItemId(Long itemId);
+
+    /**
+     * 批量删除教职人员导入项
+     * 
+     * @param itemIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteReligionImportItemByItemIds(Long[] itemIds);
+
+    /**
+     * 根据批次ID删除导入项
+     * 
+     * @param batchId 批次ID
+     * @return 结果
+     */
+    public int deleteReligionImportItemByBatchId(Long batchId);
+
+    /**
+     * 根据身份证号和姓名查询是否存在教职人员
+     * 
+     * @param idNumber 身份证号
+     * @param name 姓名
+     * @return 结果数量
+     */
+    public int checkPersonExists(@Param("idNumber") String idNumber, @Param("name") String name);
+
+    /**
+     * 根据身份证号查询是否存在教职人员
+     * 
+     * @param idNumber 身份证号
+     * @return 结果数量
+     */
+    public int checkPersonExistsByIdNumber(@Param("idNumber") String idNumber);
+
+    /**
+     * 根据姓名查询是否存在教职人员
+     * 
+     * @param name 姓名
+     * @return 结果数量
+     */
+    public int checkPersonExistsByName(@Param("name") String name);
+}
+

+ 25 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/mapper/ReligionPersonMapper.java

@@ -2,6 +2,7 @@ package com.ruoyi.project.religion.person.mapper;
 
 import java.util.List;
 import com.ruoyi.project.religion.person.domain.ReligionPerson;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest;
 
 /**
  * 教职人员Mapper接口
@@ -58,4 +59,28 @@ public interface ReligionPersonMapper
      * @return 结果
      */
     public int deleteReligionPersonByPersonIds(String[] personIds);
+
+    /**
+     * 根据批量条件查询教职人员列表(用于批量维护预览)
+     * 
+     * @param req 批量查询条件
+     * @return 教职人员集合
+     */
+    public List<ReligionPerson> selectByBatchCondition(ReligionPersonBatchUpdateByConditionRequest req);
+
+    /**
+     * 根据批量条件统计教职人员数量
+     * 
+     * @param req 批量查询条件
+     * @return 数量
+     */
+    public int countByBatchCondition(ReligionPersonBatchUpdateByConditionRequest req);
+
+    /**
+     * 根据批量条件更新教职人员
+     * 
+     * @param req 批量更新请求(包含条件和新值)
+     * @return 更新的记录数
+     */
+    public int batchUpdateByCondition(ReligionPersonBatchUpdateByConditionRequest req);
 }

+ 78 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/IReligionPersonImportService.java

@@ -0,0 +1,78 @@
+package com.ruoyi.project.religion.person.service;
+
+import java.util.List;
+import java.util.Map;
+import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.project.religion.person.domain.ReligionImportBatch;
+
+/**
+ * 教职人员导入Service接口
+ * 
+ * @author ruoyi
+ */
+public interface IReligionPersonImportService 
+{
+    /**
+     * 上传并解析Excel文件
+     * 
+     * @param file Excel文件
+     * @return 批次信息及预览数据
+     * @throws Exception 异常
+     */
+    public Map<String, Object> uploadAndParseExcel(MultipartFile file) throws Exception;
+
+    /**
+     * 查询导入批次列表
+     * 
+     * @param batch 批次查询条件
+     * @return 批次列表
+     */
+    public List<ReligionImportBatch> selectBatchList(ReligionImportBatch batch);
+
+    /**
+     * 查询导入批次
+     * 
+     * @param batchId 批次ID
+     * @return 批次信息
+     */
+    public ReligionImportBatch selectBatchById(Long batchId);
+
+    /**
+     * 查询导入项列表
+     * 
+     * @param batchId 批次ID
+     * @param status 状态(可选)
+     * @return 导入项列表
+     */
+    public List<Map<String, Object>> selectImportItems(Long batchId, String status);
+
+    /**
+     * 设置导入项的处理动作
+     * 
+     * @param itemId 导入项ID
+     * @param action 处理动作
+     * @return 更新后的导入项
+     * @throws Exception 异常
+     */
+    public Map<String, Object> setItemAction(Long itemId, String action) throws Exception;
+
+    /**
+     * 手工编辑导入项数据
+     * 
+     * @param itemId 导入项ID
+     * @param editData 编辑的数据
+     * @return 更新后的导入项
+     * @throws Exception 异常
+     */
+    public Map<String, Object> editItemData(Long itemId, Map<String, Object> editData) throws Exception;
+
+    /**
+     * 提交批次导入
+     * 
+     * @param batchId 批次ID
+     * @return 导入结果统计
+     * @throws Exception 异常
+     */
+    public Map<String, Object> commitBatchImport(Long batchId) throws Exception;
+}
+

+ 50 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/IReligionPersonService.java

@@ -2,6 +2,8 @@ package com.ruoyi.project.religion.person.service;
 
 import java.util.List;
 import com.ruoyi.project.religion.person.domain.ReligionPerson;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdatePreviewResponse;
 
 /**
  * 教职人员Service接口
@@ -35,6 +37,14 @@ public interface IReligionPersonService
      */
     public int insertReligionPerson(ReligionPerson religionPerson);
 
+    /**
+     * 新增教职人员(不记录审计日志,用于批量导入)
+     * 
+     * @param religionPerson 教职人员
+     * @return 结果
+     */
+    public int insertReligionPersonWithoutAudit(ReligionPerson religionPerson);
+
     /**
      * 修改教职人员
      * 
@@ -43,6 +53,14 @@ public interface IReligionPersonService
      */
     public int updateReligionPerson(ReligionPerson religionPerson);
 
+    /**
+     * 修改教职人员(不记录审计日志,用于批量导入)
+     * 
+     * @param religionPerson 教职人员
+     * @return 结果
+     */
+    public int updateReligionPersonWithoutAudit(ReligionPerson religionPerson);
+
     /**
      * 批量删除教职人员
      * 
@@ -58,4 +76,36 @@ public interface IReligionPersonService
      * @return 结果
      */
     public int deleteReligionPersonByPersonId(Long personId);
+
+    /**
+     * 根据批量条件查询教职人员列表(用于批量维护预览)
+     * 
+     * @param req 批量查询条件
+     * @return 教职人员集合
+     */
+    public List<ReligionPerson> selectByBatchCondition(ReligionPersonBatchUpdateByConditionRequest req);
+
+    /**
+     * 根据批量条件统计教职人员数量
+     * 
+     * @param req 批量查询条件
+     * @return 数量
+     */
+    public int countByBatchCondition(ReligionPersonBatchUpdateByConditionRequest req);
+
+    /**
+     * 根据批量条件更新教职人员
+     * 
+     * @param req 批量更新请求(包含条件和新值)
+     * @return 更新的记录数
+     */
+    public int batchUpdateByCondition(ReligionPersonBatchUpdateByConditionRequest req);
+
+    /**
+     * 批量更新预览
+     * 
+     * @param req 批量更新请求(包含条件和新值)
+     * @return 预览响应(包含受影响人员数量、变更记录数、变更明细列表)
+     */
+    public ReligionPersonBatchUpdatePreviewResponse buildBatchUpdatePreview(ReligionPersonBatchUpdateByConditionRequest req);
 }

+ 1210 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/impl/ReligionPersonImportServiceImpl.java

@@ -0,0 +1,1210 @@
+package com.ruoyi.project.religion.person.service.impl;
+
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.github.pagehelper.Page;
+import com.github.pagehelper.PageInfo;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.IpUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.ShiroUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+import com.ruoyi.project.religion.audit.service.IReligionAuditLogService;
+import com.ruoyi.project.religion.person.domain.ReligionImportBatch;
+import com.ruoyi.project.religion.person.domain.ReligionImportItem;
+import com.ruoyi.project.religion.person.domain.ReligionPerson;
+import com.ruoyi.project.religion.person.mapper.ReligionImportBatchMapper;
+import com.ruoyi.project.religion.person.mapper.ReligionImportItemMapper;
+import com.ruoyi.project.religion.person.mapper.ReligionPersonMapper;
+import com.ruoyi.project.religion.person.service.IReligionPersonImportService;
+import com.ruoyi.project.religion.person.service.IReligionPersonService;
+import com.ruoyi.project.religion.place.domain.ReligionPlace;
+import com.ruoyi.project.religion.place.mapper.ReligionPlaceMapper;
+
+/**
+ * 教职人员导入Service业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class ReligionPersonImportServiceImpl implements IReligionPersonImportService 
+{
+    /**
+     * 标准Excel模板表头(按顺序严格匹配)
+     */
+    private static final List<String> EXPECTED_HEADERS = new ArrayList<String>() {{
+        add("所属场所名称");
+        add("姓名");
+        add("宗教类别");
+        add("性别");
+        add("民族");
+        add("身份证号");
+        add("出生日期");
+        add("所属区县");
+        add("家庭地址");
+        add("政治面貌");
+        add("国民教育学历");
+        add("宗教学历");
+        add("入教/出家时间");
+        add("联系电话");
+        add("户籍所在地");
+        add("籍贯");
+        add("所属宫观名称");
+        add("所属宫观地址");
+    }};
+
+    @Autowired
+    private ReligionImportBatchMapper batchMapper;
+
+    @Autowired
+    private ReligionImportItemMapper itemMapper;
+
+    @Autowired
+    private ReligionPersonMapper personMapper;
+
+    @Autowired
+    private IReligionPersonService personService;
+
+    @Autowired
+    private ReligionPlaceMapper placeMapper;
+
+    @Autowired
+    private IReligionAuditLogService religionAuditLogService;
+
+    /**
+     * 上传并解析Excel文件
+     * 
+     * @param file Excel文件
+     * @return 批次信息及预览数据
+     * @throws Exception 异常
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> uploadAndParseExcel(MultipartFile file) throws Exception
+    {
+        // 1. 先校验Excel模板表头格式(不创建批次记录)
+        try (InputStream is = file.getInputStream();
+             Workbook workbook = WorkbookFactory.create(is))
+        {
+            Sheet sheet = workbook.getSheetAt(0);
+            
+            // 读取表头(第一行)
+            Row headerRow = sheet.getRow(0);
+            if (headerRow == null)
+            {
+                throw new Exception("Excel文件格式错误:缺少表头");
+            }
+
+            // 校验表头格式
+            validateExcelHeaders(headerRow);
+        }
+
+        // 2. 表头校验通过后,创建批次记录
+        ReligionImportBatch batch = new ReligionImportBatch();
+        batch.setBatchNo(generateBatchNo());
+        batch.setFileName(file.getOriginalFilename());
+        batch.setUploader(ShiroUtils.getLoginName());
+        batch.setUploadTime(new Date());
+        batch.setStatus("PARSED");
+        batch.setTotalRows(0);
+        batch.setValidRows(0);
+        batch.setErrorRows(0);
+        batch.setConflictRows(0);
+        batch.setImportedRows(0);
+        batch.setAbandonedRows(0);
+        
+        batchMapper.insertReligionImportBatch(batch);
+        Long batchId = batch.getBatchId();
+
+        // 3. 加载所有场所,构建名称到ID的映射
+        Map<String, List<Long>> placeNameMap = buildPlaceNameMap();
+
+        // 4. 解析Excel数据
+        List<ReligionImportItem> items = new ArrayList<>();
+        int totalRows = 0;
+        int validRows = 0;
+        int errorRows = 0;
+        int conflictRows = 0;
+
+        try (InputStream is = file.getInputStream();
+             Workbook workbook = WorkbookFactory.create(is))
+        {
+            Sheet sheet = workbook.getSheetAt(0);
+            int lastRowNum = sheet.getLastRowNum();
+
+            // 从第二行开始读取数据
+            for (int i = 1; i <= lastRowNum; i++)
+            {
+                Row row = sheet.getRow(i);
+                if (row == null || isEmptyRow(row))
+                {
+                    continue;
+                }
+
+                totalRows++;
+                
+                // 解析行数据
+                Map<String, Object> rowData = parseRow(row);
+                
+                // 保存原始场所名称
+                String originalPlaceName = (String) rowData.get("placeName");
+                rowData.put("originalPlaceName", originalPlaceName);
+                
+                // 根据场所名称匹配place_id
+                String placeMatchError = matchPlaceId(rowData, placeNameMap);
+                
+                // 校验并生成导入项
+                ReligionImportItem item = new ReligionImportItem();
+                item.setBatchId(batchId);
+                item.setRowNo(i); // 行号从1开始
+                item.setJsonData(JSON.toJSONString(rowData));
+                item.setProcessedFlag(0);
+                item.setCreateTime(new Date());
+
+                // 先检查场所匹配错误
+                if (StringUtils.isNotEmpty(placeMatchError))
+                {
+                    item.setStatus("ERROR");
+                    item.setErrorMsg(placeMatchError);
+                    item.setConflictFlag(0);
+                    if (placeMatchError.contains("多个同名场所"))
+                    {
+                        item.setAction("MANUAL");
+                    }
+                    else
+                    {
+                        item.setAction("ABANDON");
+                    }
+                    errorRows++;
+                }
+                else
+                {
+                    // 校验数据
+                    String errorMsg = validateRowData(rowData);
+                    
+                    if (StringUtils.isNotEmpty(errorMsg))
+                    {
+                        // 有错误
+                        item.setStatus("ERROR");
+                        item.setErrorMsg(errorMsg);
+                        item.setConflictFlag(0);
+                        item.setAction(null);
+                        errorRows++;
+                    }
+                    else
+                    {
+                        // 检查冲突
+                        String conflictMsg = checkConflict(rowData);
+                        
+                        if (StringUtils.isNotEmpty(conflictMsg))
+                        {
+                            // 有冲突
+                            item.setStatus("CONFLICT");
+                            item.setErrorMsg(conflictMsg);
+                            item.setConflictFlag(1);
+                            item.setAction(null);
+                            conflictRows++;
+                        }
+                        else
+                        {
+                            // 正常
+                            item.setStatus("NORMAL");
+                            item.setErrorMsg(null);
+                            item.setConflictFlag(0);
+                            item.setAction("IMPORT");
+                            validRows++;
+                        }
+                    }
+                }
+
+                items.add(item);
+            }
+        }
+
+        // 4. 批量插入导入项
+        if (!items.isEmpty())
+        {
+            itemMapper.batchInsertReligionImportItem(items);
+        }
+
+        // 5. 更新批次统计信息
+        batch.setTotalRows(totalRows);
+        batch.setValidRows(validRows);
+        batch.setErrorRows(errorRows);
+        batch.setConflictRows(conflictRows);
+        batch.setLastProcessTime(new Date());
+        batchMapper.updateReligionImportBatch(batch);
+
+        // 6. 准备返回数据
+        Map<String, Object> result = new HashMap<>();
+        result.put("batchId", batchId);
+        result.put("batchNo", batch.getBatchNo());
+        result.put("fileName", batch.getFileName());
+        result.put("uploadTime", batch.getUploadTime());
+        result.put("totalRows", totalRows);
+        result.put("validRows", validRows);
+        result.put("errorRows", errorRows);
+        result.put("conflictRows", conflictRows);
+
+        // 返回前50行预览数据
+        List<Map<String, Object>> previewItems = new ArrayList<>();
+        int previewCount = Math.min(50, items.size());
+        for (int i = 0; i < previewCount; i++)
+        {
+            ReligionImportItem item = items.get(i);
+            Map<String, Object> previewItem = new HashMap<>();
+            previewItem.put("itemId", item.getItemId());
+            previewItem.put("rowNo", item.getRowNo());
+            previewItem.put("status", item.getStatus());
+            previewItem.put("errorMsg", item.getErrorMsg());
+            previewItem.put("action", item.getAction());
+            
+            // 解析json_data中的主要字段
+            @SuppressWarnings("unchecked")
+            Map<String, Object> data = JSON.parseObject(item.getJsonData(), Map.class);
+            previewItem.put("placeName", data.get("placeName"));
+            previewItem.put("name", data.get("name"));
+            previewItem.put("religionType", data.get("religionType"));
+            previewItem.put("gender", data.get("gender"));
+            previewItem.put("idNumber", data.get("idNumber"));
+            
+            previewItems.add(previewItem);
+        }
+        result.put("previewItems", previewItems);
+
+        return result;
+    }
+
+    /**
+     * 查询导入批次列表
+     * 
+     * @param batch 批次查询条件
+     * @return 批次列表
+     */
+    @Override
+    public List<ReligionImportBatch> selectBatchList(ReligionImportBatch batch)
+    {
+        return batchMapper.selectReligionImportBatchList(batch);
+    }
+
+    /**
+     * 查询导入批次
+     * 
+     * @param batchId 批次ID
+     * @return 批次信息
+     */
+    @Override
+    public ReligionImportBatch selectBatchById(Long batchId)
+    {
+        return batchMapper.selectReligionImportBatchByBatchId(batchId);
+    }
+
+    /**
+     * 查询导入项列表
+     * 
+     * @param batchId 批次ID
+     * @param status 状态(可选)
+     * @return 导入项列表
+     */
+    @Override
+    public List<Map<String, Object>> selectImportItems(Long batchId, String status)
+    {
+        ReligionImportItem query = new ReligionImportItem();
+        query.setBatchId(batchId);
+        if (StringUtils.isNotEmpty(status))
+        {
+            query.setStatus(status);
+        }
+
+        // 注意:这里必须先查询出 List<ReligionImportItem>,PageHelper 会自动处理分页
+        // PageHelper 会将返回的 List 包装成 Page 对象,包含 total 等分页信息
+        List<ReligionImportItem> items = itemMapper.selectReligionImportItemList(query);
+        
+        // 创建一个新的 Page 对象来保持分页信息
+        // 如果 items 是 Page 类型,我们需要保留其分页信息
+        Page<Map<String, Object>> result;
+        if (items instanceof Page)
+        {
+            Page<ReligionImportItem> page = (Page<ReligionImportItem>) items;
+            result = new Page<>(page.getPageNum(), page.getPageSize());
+            result.setTotal(page.getTotal());
+            result.setPages(page.getPages());
+        }
+        else
+        {
+            result = new Page<>();
+        }
+        
+        // 将分页后的结果转换为 Map 格式
+        for (ReligionImportItem item : items)
+        {
+            Map<String, Object> itemMap = new HashMap<>();
+            itemMap.put("itemId", item.getItemId());
+            itemMap.put("batchId", item.getBatchId());
+            itemMap.put("rowNo", item.getRowNo());
+            itemMap.put("status", item.getStatus());
+            itemMap.put("errorMsg", item.getErrorMsg());
+            itemMap.put("action", item.getAction());
+            itemMap.put("processedFlag", item.getProcessedFlag());
+
+            // 解析json_data中的业务字段
+            if (StringUtils.isNotEmpty(item.getJsonData()))
+            {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> data = JSON.parseObject(item.getJsonData(), Map.class);
+                itemMap.put("placeId", data.get("placeId"));
+                itemMap.put("placeName", data.get("placeName"));
+                itemMap.put("originalPlaceName", data.get("originalPlaceName"));
+                itemMap.put("name", data.get("name"));
+                itemMap.put("religionType", data.get("religionType"));
+                itemMap.put("gender", data.get("gender"));
+                itemMap.put("nation", data.get("nation"));
+                itemMap.put("idNumber", data.get("idNumber"));
+                itemMap.put("birthday", data.get("birthday"));
+                itemMap.put("county", data.get("county"));
+                itemMap.put("address", data.get("address"));
+                itemMap.put("politicalStatus", data.get("politicalStatus"));
+                itemMap.put("eduNational", data.get("eduNational"));
+                itemMap.put("eduReligious", data.get("eduReligious"));
+                itemMap.put("joinTime", data.get("joinTime"));
+                itemMap.put("phone", data.get("phone"));
+                itemMap.put("domicile", data.get("domicile"));
+                itemMap.put("nativePlace", data.get("nativePlace"));
+                itemMap.put("orgName", data.get("orgName"));
+                itemMap.put("orgAddress", data.get("orgAddress"));
+            }
+
+            result.add(itemMap);
+        }
+
+        return result;
+    }
+
+    /**
+     * 生成批次号
+     * 
+     * @return 批次号
+     */
+    private String generateBatchNo()
+    {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+        String dateStr = sdf.format(new Date());
+        long timestamp = System.currentTimeMillis() % 10000;
+        return dateStr + "-" + String.format("%04d", timestamp);
+    }
+
+    /**
+     * 判断行是否为空
+     * 
+     * @param row 行
+     * @return 是否为空
+     */
+    private boolean isEmptyRow(Row row)
+    {
+        for (int i = 0; i < row.getLastCellNum(); i++)
+        {
+            Cell cell = row.getCell(i);
+            if (cell != null && cell.getCellType() != CellType.BLANK)
+            {
+                String value = getCellValue(cell);
+                if (StringUtils.isNotEmpty(value))
+                {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 解析行数据
+     * 
+     * @param row 行
+     * @return 行数据Map
+     */
+    private Map<String, Object> parseRow(Row row)
+    {
+        Map<String, Object> data = new HashMap<>();
+        
+        // 按固定列顺序解析
+        data.put("placeName", getCellValue(row.getCell(0)));           // 所属场所名称
+        data.put("name", getCellValue(row.getCell(1)));                // 姓名
+        data.put("religionType", getCellValue(row.getCell(2)));        // 宗教类别
+        data.put("gender", getCellValue(row.getCell(3)));              // 性别
+        data.put("nation", getCellValue(row.getCell(4)));              // 民族
+        data.put("idNumber", getCellValue(row.getCell(5)));            // 身份证号
+        data.put("birthday", getCellValue(row.getCell(6)));            // 出生日期
+        data.put("county", getCellValue(row.getCell(7)));              // 所属区县
+        data.put("address", getCellValue(row.getCell(8)));             // 家庭地址
+        data.put("politicalStatus", getCellValue(row.getCell(9)));     // 政治面貌
+        data.put("eduNational", getCellValue(row.getCell(10)));        // 国民教育学历
+        data.put("eduReligious", getCellValue(row.getCell(11)));       // 宗教学历
+        data.put("joinTime", getCellValue(row.getCell(12)));           // 入教/出家时间
+        data.put("phone", getCellValue(row.getCell(13)));              // 联系电话
+        data.put("domicile", getCellValue(row.getCell(14)));           // 户籍所在地
+        data.put("nativePlace", getCellValue(row.getCell(15)));        // 籍贯
+        data.put("orgName", getCellValue(row.getCell(16)));            // 所属宫观名称
+        data.put("orgAddress", getCellValue(row.getCell(17)));         // 所属宫观地址
+
+        return data;
+    }
+
+    /**
+     * 获取单元格值
+     * 
+     * @param cell 单元格
+     * @return 单元格值
+     */
+    private String getCellValue(Cell cell)
+    {
+        if (cell == null)
+        {
+            return "";
+        }
+
+        switch (cell.getCellType())
+        {
+            case STRING:
+                return cell.getStringCellValue().trim();
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(cell))
+                {
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                    return sdf.format(cell.getDateCellValue());
+                }
+                else
+                {
+                    // 数字类型,转为字符串(避免科学计数法)
+                    double value = cell.getNumericCellValue();
+                    if (value == (long) value)
+                    {
+                        return String.valueOf((long) value);
+                    }
+                    else
+                    {
+                        return String.valueOf(value);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                try
+                {
+                    return cell.getStringCellValue().trim();
+                }
+                catch (Exception e)
+                {
+                    return String.valueOf(cell.getNumericCellValue());
+                }
+            default:
+                return "";
+        }
+    }
+
+    /**
+     * 校验行数据
+     * 
+     * @param rowData 行数据
+     * @return 错误信息,无错误返回null
+     */
+    private String validateRowData(Map<String, Object> rowData)
+    {
+        List<String> errors = new ArrayList<>();
+
+        // 必填项校验
+        if (StringUtils.isEmpty((String) rowData.get("placeName")))
+        {
+            errors.add("所属场所名称为空");
+        }
+        if (StringUtils.isEmpty((String) rowData.get("name")))
+        {
+            errors.add("姓名为空");
+        }
+        if (StringUtils.isEmpty((String) rowData.get("religionType")))
+        {
+            errors.add("宗教类别为空");
+        }
+        if (StringUtils.isEmpty((String) rowData.get("idNumber")))
+        {
+            errors.add("身份证号为空");
+        }
+
+        // 格式校验
+        String idNumber = (String) rowData.get("idNumber");
+        if (StringUtils.isNotEmpty(idNumber))
+        {
+            if (idNumber.length() != 15 && idNumber.length() != 18)
+            {
+                errors.add("身份证号长度不正确");
+            }
+        }
+
+        // 日期格式校验
+        String birthday = (String) rowData.get("birthday");
+        if (StringUtils.isNotEmpty(birthday) && !isValidDate(birthday))
+        {
+            errors.add("出生日期格式不正确");
+        }
+
+        String joinTime = (String) rowData.get("joinTime");
+        if (StringUtils.isNotEmpty(joinTime) && !isValidDate(joinTime))
+        {
+            errors.add("入教/出家时间格式不正确");
+        }
+
+        return errors.isEmpty() ? null : String.join(";", errors);
+    }
+
+    /**
+     * 检查冲突
+     * 
+     * @param rowData 行数据
+     * @return 冲突信息,无冲突返回null
+     */
+    private String checkConflict(Map<String, Object> rowData)
+    {
+        String idNumber = (String) rowData.get("idNumber");
+        String name = (String) rowData.get("name");
+
+        // 检查身份证号+姓名是否都存在
+        int bothExist = itemMapper.checkPersonExists(idNumber, name);
+        if (bothExist > 0)
+        {
+            return "身份证号和姓名均与库中已有教职人员冲突";
+        }
+
+        // 检查仅身份证号相同
+        int idExist = itemMapper.checkPersonExistsByIdNumber(idNumber);
+        if (idExist > 0)
+        {
+            return "仅身份证号相同,建议手工处理";
+        }
+
+        // 检查仅姓名相同
+        int nameExist = itemMapper.checkPersonExistsByName(name);
+        if (nameExist > 0)
+        {
+            return "仅姓名相同,建议手工处理";
+        }
+
+        return null;
+    }
+
+    /**
+     * 验证日期格式
+     * 
+     * @param dateStr 日期字符串
+     * @return 是否有效
+     */
+    private boolean isValidDate(String dateStr)
+    {
+        try
+        {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            sdf.setLenient(false);
+            sdf.parse(dateStr);
+            return true;
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+
+    /**
+     * 设置导入项的处理动作
+     * 
+     * @param itemId 导入项ID
+     * @param action 处理动作
+     * @return 更新后的导入项
+     * @throws Exception 异常
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> setItemAction(Long itemId, String action) throws Exception
+    {
+        // 1. 查询导入项
+        ReligionImportItem item = itemMapper.selectReligionImportItemByItemId(itemId);
+        if (item == null)
+        {
+            throw new Exception("导入项不存在");
+        }
+
+        // 2. 查询批次,校验状态
+        ReligionImportBatch batch = batchMapper.selectReligionImportBatchByBatchId(item.getBatchId());
+        if (batch == null)
+        {
+            throw new Exception("批次不存在");
+        }
+        if ("FINISHED".equals(batch.getStatus()))
+        {
+            throw new Exception("该批次已完成导入,不可修改");
+        }
+
+        // 3. 验证action取值
+        if (!"OVERWRITE".equals(action) && !"KEEP".equals(action) 
+            && !"ABANDON".equals(action) && !"MANUAL".equals(action))
+        {
+            throw new Exception("无效的处理动作:" + action);
+        }
+
+        // 4. 更新导入项
+        item.setAction(action);
+        item.setActionBy(ShiroUtils.getLoginName());
+        item.setActionTime(new Date());
+        itemMapper.updateReligionImportItem(item);
+
+        // 5. 返回更新后的数据
+        return buildItemMap(item);
+    }
+
+    /**
+     * 手工编辑导入项数据
+     * 
+     * @param itemId 导入项ID
+     * @param editData 编辑的数据
+     * @return 更新后的导入项
+     * @throws Exception 异常
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> editItemData(Long itemId, Map<String, Object> editData) throws Exception
+    {
+        // 1. 查询导入项
+        ReligionImportItem item = itemMapper.selectReligionImportItemByItemId(itemId);
+        if (item == null)
+        {
+            throw new Exception("导入项不存在");
+        }
+
+        // 2. 查询批次,校验状态
+        ReligionImportBatch batch = batchMapper.selectReligionImportBatchByBatchId(item.getBatchId());
+        if (batch == null)
+        {
+            throw new Exception("批次不存在");
+        }
+        if ("FINISHED".equals(batch.getStatus()))
+        {
+            throw new Exception("该批次已完成导入,不可修改");
+        }
+
+        // 3. 解析原json_data
+        @SuppressWarnings("unchecked")
+        Map<String, Object> rowData = JSON.parseObject(item.getJsonData(), Map.class);
+
+        // 4. 构建场所名称映射
+        Map<String, List<Long>> placeNameMap = buildPlaceNameMap();
+
+        // 5. 用editData覆盖相应字段
+        if (editData.containsKey("placeName")) 
+        {
+            rowData.put("placeName", editData.get("placeName"));
+            rowData.put("originalPlaceName", editData.get("placeName"));
+        }
+        if (editData.containsKey("name")) rowData.put("name", editData.get("name"));
+        if (editData.containsKey("religionType")) rowData.put("religionType", editData.get("religionType"));
+        if (editData.containsKey("gender")) rowData.put("gender", editData.get("gender"));
+        if (editData.containsKey("nation")) rowData.put("nation", editData.get("nation"));
+        if (editData.containsKey("idNumber")) rowData.put("idNumber", editData.get("idNumber"));
+        if (editData.containsKey("birthday")) rowData.put("birthday", editData.get("birthday"));
+        if (editData.containsKey("county")) rowData.put("county", editData.get("county"));
+        if (editData.containsKey("address")) rowData.put("address", editData.get("address"));
+        if (editData.containsKey("politicalStatus")) rowData.put("politicalStatus", editData.get("politicalStatus"));
+        if (editData.containsKey("eduNational")) rowData.put("eduNational", editData.get("eduNational"));
+        if (editData.containsKey("eduReligious")) rowData.put("eduReligious", editData.get("eduReligious"));
+        if (editData.containsKey("joinTime")) rowData.put("joinTime", editData.get("joinTime"));
+        if (editData.containsKey("phone")) rowData.put("phone", editData.get("phone"));
+        if (editData.containsKey("domicile")) rowData.put("domicile", editData.get("domicile"));
+        if (editData.containsKey("nativePlace")) rowData.put("nativePlace", editData.get("nativePlace"));
+        if (editData.containsKey("orgName")) rowData.put("orgName", editData.get("orgName"));
+        if (editData.containsKey("orgAddress")) rowData.put("orgAddress", editData.get("orgAddress"));
+
+        // 6. 重新匹配场所ID
+        String placeMatchError = matchPlaceId(rowData, placeNameMap);
+
+        // 7. 重新校验
+        String errorMsg = null;
+        String conflictMsg = null;
+        
+        if (StringUtils.isNotEmpty(placeMatchError))
+        {
+            errorMsg = placeMatchError;
+        }
+        else
+        {
+            errorMsg = validateRowData(rowData);
+            if (StringUtils.isEmpty(errorMsg))
+            {
+                conflictMsg = checkConflict(rowData);
+            }
+        }
+
+        // 8. 更新状态和action
+        if (StringUtils.isNotEmpty(errorMsg))
+        {
+            item.setStatus("ERROR");
+            item.setErrorMsg(errorMsg);
+            item.setConflictFlag(0);
+            item.setAction("MANUAL");
+        }
+        else if (StringUtils.isNotEmpty(conflictMsg))
+        {
+            item.setStatus("CONFLICT");
+            item.setErrorMsg(conflictMsg);
+            item.setConflictFlag(1);
+            item.setAction("MANUAL");
+        }
+        else
+        {
+            item.setStatus("NORMAL");
+            item.setErrorMsg(null);
+            item.setConflictFlag(0);
+            item.setAction("IMPORT");
+        }
+
+        // 9. 更新json_data和其他字段
+        item.setJsonData(JSON.toJSONString(rowData));
+        item.setActionBy(ShiroUtils.getLoginName());
+        item.setActionTime(new Date());
+        itemMapper.updateReligionImportItem(item);
+
+        // 10. 返回更新后的数据
+        return buildItemMap(item);
+    }
+
+    /**
+     * 提交批次导入
+     * 
+     * @param batchId 批次ID
+     * @return 导入结果统计
+     * @throws Exception 异常
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> commitBatchImport(Long batchId) throws Exception
+    {
+        // 1. 查询批次
+        ReligionImportBatch batch = batchMapper.selectReligionImportBatchByBatchId(batchId);
+        if (batch == null)
+        {
+            throw new Exception("批次不存在");
+        }
+        if ("FINISHED".equals(batch.getStatus()))
+        {
+            throw new Exception("该批次已完成导入,不可重复提交");
+        }
+
+        // 2. 查询所有导入项
+        ReligionImportItem query = new ReligionImportItem();
+        query.setBatchId(batchId);
+        List<ReligionImportItem> items = itemMapper.selectReligionImportItemList(query);
+
+        // 3. 校验是否存在未决策的异常记录
+        for (ReligionImportItem item : items)
+        {
+            if (("ERROR".equals(item.getStatus()) || "CONFLICT".equals(item.getStatus()))
+                && (StringUtils.isEmpty(item.getAction()) || "MANUAL".equals(item.getAction())))
+            {
+                throw new Exception("存在未处理的异常/冲突记录(行号:" + item.getRowNo() + "),请先设置处理方式或手工修正后再提交");
+            }
+        }
+
+        // 4. 遍历导入项,执行导入
+        int importedCount = 0;
+        int abandonedCount = 0;
+
+        for (ReligionImportItem item : items)
+        {
+            String action = item.getAction();
+            String status = item.getStatus();
+
+            // 跳过已处理的记录
+            if (item.getProcessedFlag() != null && item.getProcessedFlag() == 1)
+            {
+                continue;
+            }
+
+            // 解析json_data
+            @SuppressWarnings("unchecked")
+            Map<String, Object> data = JSON.parseObject(item.getJsonData(), Map.class);
+
+            if ("ABANDON".equals(action) || "KEEP".equals(action))
+            {
+                // 放弃或保留,不操作主表
+                item.setProcessedFlag(1);
+                itemMapper.updateReligionImportItem(item);
+                abandonedCount++;
+            }
+            else if ("NORMAL".equals(status) && (StringUtils.isEmpty(action) || "IMPORT".equals(action)))
+            {
+                // 正常导入
+                importPersonData(data);
+                item.setProcessedFlag(1);
+                itemMapper.updateReligionImportItem(item);
+                importedCount++;
+            }
+            else if (("ERROR".equals(status) || "CONFLICT".equals(status)) && "OVERWRITE".equals(action))
+            {
+                // 覆盖导入
+                importPersonData(data);
+                item.setProcessedFlag(1);
+                itemMapper.updateReligionImportItem(item);
+                importedCount++;
+            }
+        }
+
+        // 5. 更新批次信息
+        batch.setImportedRows(importedCount);
+        batch.setAbandonedRows(abandonedCount);
+        batch.setLastProcessTime(new Date());
+        
+        // 检查是否所有记录都已处理
+        boolean allProcessed = true;
+        for (ReligionImportItem item : items)
+        {
+            if (item.getProcessedFlag() == null || item.getProcessedFlag() == 0)
+            {
+                allProcessed = false;
+                break;
+            }
+        }
+        batch.setStatus(allProcessed ? "FINISHED" : "PARTIAL");
+        batchMapper.updateReligionImportBatch(batch);
+
+        // 6. 记录批量导入审计日志
+        recordBatchImportAuditLog(batch);
+
+        // 7. 返回结果
+        Map<String, Object> result = new HashMap<>();
+        result.put("batchId", batchId);
+        result.put("importedCount", importedCount);
+        result.put("abandonedCount", abandonedCount);
+        result.put("status", batch.getStatus());
+        return result;
+    }
+
+    /**
+     * 记录批量导入审计日志
+     * 
+     * @param batch 导入批次信息
+     */
+    private void recordBatchImportAuditLog(ReligionImportBatch batch)
+    {
+        try
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            // 设置审计日志基本信息
+            auditLog.setOpType("BATCH_IMPORT_PERSON");
+            auditLog.setResourceType("person_import");
+            auditLog.setResourceId(String.valueOf(batch.getBatchId()));
+            auditLog.setOpTime(new Date());
+            auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+            
+            // 构造导入概要信息
+            JSONObject detail = new JSONObject();
+            detail.put("batchId", batch.getBatchId());
+            detail.put("batchNo", batch.getBatchNo());
+            detail.put("fileName", batch.getFileName());
+            detail.put("totalRows", batch.getTotalRows());
+            detail.put("validRows", batch.getValidRows());
+            detail.put("errorRows", batch.getErrorRows());
+            detail.put("conflictRows", batch.getConflictRows());
+            detail.put("importedRows", batch.getImportedRows());
+            detail.put("abandonedRows", batch.getAbandonedRows());
+            
+            // 格式化时间
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            if (batch.getUploadTime() != null)
+            {
+                detail.put("uploadTime", sdf.format(batch.getUploadTime()));
+            }
+            if (batch.getLastProcessTime() != null)
+            {
+                detail.put("lastProcessTime", sdf.format(batch.getLastProcessTime()));
+            }
+            
+            auditLog.setOpDetail(detail.toJSONString());
+            
+            // 插入审计日志
+            religionAuditLogService.insertReligionAuditLog(auditLog);
+        }
+        catch (Exception e)
+        {
+            // 审计日志记录失败不影响主流程,仅记录错误
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 导入人员数据到religion_person表
+     * 
+     * @param data 人员数据
+     * @throws Exception 异常
+     */
+    private void importPersonData(Map<String, Object> data) throws Exception
+    {
+        String idNumber = (String) data.get("idNumber");
+        String name = (String) data.get("name");
+        
+        // 获取placeId(必须存在)
+        Object placeIdObj = data.get("placeId");
+        if (placeIdObj == null)
+        {
+            throw new Exception("导入数据缺少场所ID,无法导入");
+        }
+        Long placeId = null;
+        if (placeIdObj instanceof Long)
+        {
+            placeId = (Long) placeIdObj;
+        }
+        else if (placeIdObj instanceof Integer)
+        {
+            placeId = ((Integer) placeIdObj).longValue();
+        }
+        else
+        {
+            placeId = Long.parseLong(placeIdObj.toString());
+        }
+
+        // 查询是否存在
+        ReligionPerson query = new ReligionPerson();
+        query.setIdNumber(idNumber);
+        query.setName(name);
+        List<ReligionPerson> existList = personMapper.selectReligionPersonList(query);
+
+        ReligionPerson person = new ReligionPerson();
+        
+        // 设置场所ID
+        person.setPlaceId(placeId);
+        person.setName(name);
+        person.setReligionType((String) data.get("religionType"));
+        person.setGender((String) data.get("gender"));
+        person.setNation((String) data.get("nation"));
+        person.setIdNumber(idNumber);
+        
+        // 日期字段处理
+        if (data.get("birthday") != null)
+        {
+            person.setBirthday(parseDate((String) data.get("birthday")));
+        }
+        if (data.get("joinTime") != null)
+        {
+            person.setJoinTime(parseDate((String) data.get("joinTime")));
+        }
+        
+        person.setCounty((String) data.get("county"));
+        person.setAddress((String) data.get("address"));
+        person.setPoliticalStatus((String) data.get("politicalStatus"));
+        person.setEduNational((String) data.get("eduNational"));
+        person.setEduReligious((String) data.get("eduReligious"));
+        person.setPhone((String) data.get("phone"));
+        person.setDomicile((String) data.get("domicile"));
+        person.setNativePlace((String) data.get("nativePlace"));
+        person.setOrgName((String) data.get("orgName"));
+        person.setOrgAddress((String) data.get("orgAddress"));
+
+        if (existList != null && !existList.isEmpty())
+        {
+            // 更新现有记录(不记录审计日志,由批量导入统一记录)
+            ReligionPerson existPerson = existList.get(0);
+            person.setPersonId(existPerson.getPersonId());
+            // 使用导入数据中的placeId(已在上面设置)
+            personService.updateReligionPersonWithoutAudit(person);
+        }
+        else
+        {
+            // 新增记录(不记录审计日志,由批量导入统一记录)
+            personService.insertReligionPersonWithoutAudit(person);
+        }
+    }
+
+    /**
+     * 解析日期字符串
+     * 
+     * @param dateStr 日期字符串
+     * @return 日期对象
+     */
+    private Date parseDate(String dateStr)
+    {
+        if (StringUtils.isEmpty(dateStr))
+        {
+            return null;
+        }
+        try
+        {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            return sdf.parse(dateStr);
+        }
+        catch (Exception e)
+        {
+            return null;
+        }
+    }
+
+    /**
+     * 构建导入项Map
+     * 
+     * @param item 导入项
+     * @return Map
+     */
+    private Map<String, Object> buildItemMap(ReligionImportItem item)
+    {
+        Map<String, Object> itemMap = new HashMap<>();
+        itemMap.put("itemId", item.getItemId());
+        itemMap.put("batchId", item.getBatchId());
+        itemMap.put("rowNo", item.getRowNo());
+        itemMap.put("status", item.getStatus());
+        itemMap.put("errorMsg", item.getErrorMsg());
+        itemMap.put("conflictFlag", item.getConflictFlag());
+        itemMap.put("action", item.getAction());
+        itemMap.put("actionBy", item.getActionBy());
+        itemMap.put("actionTime", item.getActionTime());
+        itemMap.put("processedFlag", item.getProcessedFlag());
+
+        // 解析json_data
+        if (StringUtils.isNotEmpty(item.getJsonData()))
+        {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> data = JSON.parseObject(item.getJsonData(), Map.class);
+            itemMap.putAll(data);
+        }
+
+        return itemMap;
+    }
+
+    /**
+     * 构建场所名称到ID的映射
+     * 
+     * @return Map<场所名称, List<场所ID>>
+     */
+    private Map<String, List<Long>> buildPlaceNameMap()
+    {
+        Map<String, List<Long>> placeNameMap = new HashMap<>();
+        
+        // 查询所有场所
+        List<ReligionPlace> places = placeMapper.selectPlaceList(new ReligionPlace());
+        
+        for (ReligionPlace place : places)
+        {
+            String placeName = place.getPlaceName();
+            if (StringUtils.isNotEmpty(placeName))
+            {
+                if (!placeNameMap.containsKey(placeName))
+                {
+                    placeNameMap.put(placeName, new ArrayList<>());
+                }
+                placeNameMap.get(placeName).add(place.getPlaceId());
+            }
+        }
+        
+        return placeNameMap;
+    }
+
+    /**
+     * 根据场所名称匹配place_id
+     * 
+     * @param rowData 行数据
+     * @param placeNameMap 场所名称映射
+     * @return 错误信息,无错误返回null
+     */
+    private String matchPlaceId(Map<String, Object> rowData, Map<String, List<Long>> placeNameMap)
+    {
+        String placeName = (String) rowData.get("placeName");
+        
+        if (StringUtils.isEmpty(placeName))
+        {
+            return null; // 必填项校验会处理
+        }
+        
+        List<Long> placeIds = placeNameMap.get(placeName);
+        
+        if (placeIds == null || placeIds.isEmpty())
+        {
+            // 没有匹配
+            return "所属场所名称在系统中不存在";
+        }
+        else if (placeIds.size() > 1)
+        {
+            // 多个同名场所
+            return "系统存在多个同名场所,无法确定归属";
+        }
+        else
+        {
+            // 唯一匹配
+            rowData.put("placeId", placeIds.get(0));
+            return null;
+        }
+    }
+
+    /**
+     * 校验Excel表头格式
+     * 
+     * @param headerRow 表头行
+     * @throws Exception 表头格式不符合要求时抛出异常
+     */
+    private void validateExcelHeaders(Row headerRow) throws Exception
+    {
+        // 读取实际表头
+        List<String> actualHeaders = new ArrayList<>();
+        int lastCellNum = headerRow.getLastCellNum();
+        
+        for (int i = 0; i < lastCellNum; i++)
+        {
+            Cell cell = headerRow.getCell(i);
+            String cellValue = getCellValue(cell);
+            
+            // getCellValue已经做了trim处理,只需要检查是否非空
+            if (StringUtils.isNotEmpty(cellValue))
+            {
+                actualHeaders.add(cellValue);
+            }
+        }
+
+        // 校验列数
+        if (actualHeaders.size() != EXPECTED_HEADERS.size())
+        {
+            throw new Exception("Excel模板格式不符合要求,请下载标准模板后重新导入。");
+        }
+
+        // 校验每一列的表头文本
+        for (int i = 0; i < EXPECTED_HEADERS.size(); i++)
+        {
+            String expected = EXPECTED_HEADERS.get(i);
+            String actual = actualHeaders.get(i);
+            
+            if (!expected.equals(actual))
+            {
+                throw new Exception("Excel模板格式不符合要求,请下载标准模板后重新导入。");
+            }
+        }
+    }
+}
+

+ 986 - 1
ruoyi-admin/src/main/java/com/ruoyi/project/religion/person/service/impl/ReligionPersonServiceImpl.java

@@ -1,13 +1,28 @@
 package com.ruoyi.project.religion.person.service.impl;
 
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import com.ruoyi.common.utils.DateUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.IpUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.ShiroUtils;
 import com.ruoyi.project.religion.person.mapper.ReligionPersonMapper;
 import com.ruoyi.project.religion.person.domain.ReligionPerson;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdatePreviewResponse;
+import com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdatePreviewResponse.ChangeDetail;
 import com.ruoyi.project.religion.person.service.IReligionPersonService;
 import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+import com.ruoyi.project.religion.audit.service.IReligionAuditLogService;
 
 /**
  * 教职人员Service业务层处理
@@ -21,6 +36,9 @@ public class ReligionPersonServiceImpl implements IReligionPersonService
     @Autowired
     private ReligionPersonMapper religionPersonMapper;
 
+    @Autowired
+    private IReligionAuditLogService religionAuditLogService;
+
     /**
      * 查询教职人员
      * 
@@ -54,8 +72,68 @@ public class ReligionPersonServiceImpl implements IReligionPersonService
     @Override
     public int insertReligionPerson(ReligionPerson religionPerson)
     {
+        // 无论前端有没有传 status,这里都强制设置为待校核
+        religionPerson.setStatus("TO_CHECK");
         religionPerson.setCreateTime(DateUtils.getNowDate());
-        return religionPersonMapper.insertReligionPerson(religionPerson);
+        int result = religionPersonMapper.insertReligionPerson(religionPerson);
+        
+        // 记录审计日志:新增教职人员
+        if (result > 0)
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            auditLog.setOpType("新增");
+            auditLog.setResourceType("宗教教职人员管理");
+            auditLog.setResourceId(String.valueOf(religionPerson.getPersonId()));
+            
+            // 记录新增的教职人员信息(覆盖所有字段,只记录非空字段)
+            JSONObject detail = new JSONObject();
+            
+            // 基本信息字段
+            putIfNotNull(detail, "placeId", religionPerson.getPlaceId());
+            putIfNotBlank(detail, "name", religionPerson.getName());
+            putIfNotBlank(detail, "religionType", religionPerson.getReligionType());
+            putIfNotBlank(detail, "gender", religionPerson.getGender());
+            putIfNotBlank(detail, "nation", religionPerson.getNation());
+            putIfNotBlank(detail, "idNumber", religionPerson.getIdNumber());
+            putIfNotNull(detail, "birthday", religionPerson.getBirthday());
+            putIfNotBlank(detail, "county", religionPerson.getCounty());
+            putIfNotBlank(detail, "address", religionPerson.getAddress());
+            
+            // 政治与学历字段
+            putIfNotBlank(detail, "politicalStatus", religionPerson.getPoliticalStatus());
+            putIfNotBlank(detail, "eduNational", religionPerson.getEduNational());
+            putIfNotBlank(detail, "eduReligious", religionPerson.getEduReligious());
+            putIfNotNull(detail, "joinTime", religionPerson.getJoinTime());
+            
+            // 联系与户籍字段
+            putIfNotBlank(detail, "phone", religionPerson.getPhone());
+            putIfNotBlank(detail, "domicile", religionPerson.getDomicile());
+            putIfNotBlank(detail, "nativePlace", religionPerson.getNativePlace());
+            
+            // 宫观信息字段
+            putIfNotBlank(detail, "orgName", religionPerson.getOrgName());
+            putIfNotBlank(detail, "orgAddress", religionPerson.getOrgAddress());
+            
+            // 状态字段
+            putIfNotBlank(detail, "status", religionPerson.getStatus());
+            
+            auditLog.setOpDetail(detail.toJSONString());
+            auditLog.setOpTime(new Date());
+            auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+            
+            religionAuditLogService.insertReligionAuditLog(auditLog);
+        }
+        
+        return result;
     }
 
     /**
@@ -67,6 +145,218 @@ public class ReligionPersonServiceImpl implements IReligionPersonService
     @Override
     public int updateReligionPerson(ReligionPerson religionPerson)
     {
+        // 查询旧数据,用于对比字段差异
+        ReligionPerson oldPerson = religionPersonMapper.selectReligionPersonByPersonId(religionPerson.getPersonId());
+        
+        // 普通编辑不允许修改状态,将 status 置空,让 SQL 不更新该字段
+        religionPerson.setStatus(null);
+        religionPerson.setUpdateTime(DateUtils.getNowDate());
+        int result = religionPersonMapper.updateReligionPerson(religionPerson);
+        
+        // 记录审计日志:修改教职人员
+        if (result > 0 && oldPerson != null)
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            auditLog.setOpType("修改");
+            auditLog.setResourceType("宗教教职人员管理");
+            auditLog.setResourceId(String.valueOf(religionPerson.getPersonId()));
+            
+            // 对比字段差异,只记录确实发生变化的字段
+            JSONObject detail = new JSONObject();
+            
+            if (hasChanged(oldPerson.getPlaceId(), religionPerson.getPlaceId()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getPlaceId());
+                change.put("new", religionPerson.getPlaceId());
+                detail.put("placeId", change);
+            }
+            
+            if (hasChanged(oldPerson.getName(), religionPerson.getName()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getName());
+                change.put("new", religionPerson.getName());
+                detail.put("name", change);
+            }
+            
+            if (hasChanged(oldPerson.getReligionType(), religionPerson.getReligionType()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getReligionType());
+                change.put("new", religionPerson.getReligionType());
+                detail.put("religionType", change);
+            }
+            
+            if (hasChanged(oldPerson.getGender(), religionPerson.getGender()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getGender());
+                change.put("new", religionPerson.getGender());
+                detail.put("gender", change);
+            }
+            
+            if (hasChanged(oldPerson.getNation(), religionPerson.getNation()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getNation());
+                change.put("new", religionPerson.getNation());
+                detail.put("nation", change);
+            }
+            
+            if (hasChanged(oldPerson.getIdNumber(), religionPerson.getIdNumber()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getIdNumber());
+                change.put("new", religionPerson.getIdNumber());
+                detail.put("idNumber", change);
+            }
+            
+            if (hasChanged(oldPerson.getBirthday(), religionPerson.getBirthday()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", formatDate(oldPerson.getBirthday()));
+                change.put("new", formatDate(religionPerson.getBirthday()));
+                detail.put("birthday", change);
+            }
+            
+            if (hasChanged(oldPerson.getCounty(), religionPerson.getCounty()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getCounty());
+                change.put("new", religionPerson.getCounty());
+                detail.put("county", change);
+            }
+            
+            if (hasChanged(oldPerson.getAddress(), religionPerson.getAddress()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getAddress());
+                change.put("new", religionPerson.getAddress());
+                detail.put("address", change);
+            }
+            
+            if (hasChanged(oldPerson.getPoliticalStatus(), religionPerson.getPoliticalStatus()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getPoliticalStatus());
+                change.put("new", religionPerson.getPoliticalStatus());
+                detail.put("politicalStatus", change);
+            }
+            
+            if (hasChanged(oldPerson.getEduNational(), religionPerson.getEduNational()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getEduNational());
+                change.put("new", religionPerson.getEduNational());
+                detail.put("eduNational", change);
+            }
+            
+            if (hasChanged(oldPerson.getEduReligious(), religionPerson.getEduReligious()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getEduReligious());
+                change.put("new", religionPerson.getEduReligious());
+                detail.put("eduReligious", change);
+            }
+            
+            if (hasChanged(oldPerson.getJoinTime(), religionPerson.getJoinTime()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", formatDate(oldPerson.getJoinTime()));
+                change.put("new", formatDate(religionPerson.getJoinTime()));
+                detail.put("joinTime", change);
+            }
+            
+            if (hasChanged(oldPerson.getPhone(), religionPerson.getPhone()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getPhone());
+                change.put("new", religionPerson.getPhone());
+                detail.put("phone", change);
+            }
+            
+            if (hasChanged(oldPerson.getDomicile(), religionPerson.getDomicile()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getDomicile());
+                change.put("new", religionPerson.getDomicile());
+                detail.put("domicile", change);
+            }
+            
+            if (hasChanged(oldPerson.getNativePlace(), religionPerson.getNativePlace()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getNativePlace());
+                change.put("new", religionPerson.getNativePlace());
+                detail.put("nativePlace", change);
+            }
+            
+            if (hasChanged(oldPerson.getOrgName(), religionPerson.getOrgName()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getOrgName());
+                change.put("new", religionPerson.getOrgName());
+                detail.put("orgName", change);
+            }
+            
+            if (hasChanged(oldPerson.getOrgAddress(), religionPerson.getOrgAddress()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPerson.getOrgAddress());
+                change.put("new", religionPerson.getOrgAddress());
+                detail.put("orgAddress", change);
+            }
+            
+            // 只有当有字段变更时才记录审计日志
+            if (!detail.isEmpty())
+            {
+                auditLog.setOpDetail(detail.toJSONString());
+                auditLog.setOpTime(new Date());
+                auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+                
+                religionAuditLogService.insertReligionAuditLog(auditLog);
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     * 新增教职人员(不记录审计日志,用于批量导入)
+     * 
+     * @param religionPerson 教职人员
+     * @return 结果
+     */
+    @Override
+    public int insertReligionPersonWithoutAudit(ReligionPerson religionPerson)
+    {
+        // 无论前端有没有传 status,这里都强制设置为待校核
+        religionPerson.setStatus("TO_CHECK");
+        religionPerson.setCreateTime(DateUtils.getNowDate());
+        return religionPersonMapper.insertReligionPerson(religionPerson);
+    }
+
+    /**
+     * 修改教职人员(不记录审计日志,用于批量导入)
+     * 
+     * @param religionPerson 教职人员
+     * @return 结果
+     */
+    @Override
+    public int updateReligionPersonWithoutAudit(ReligionPerson religionPerson)
+    {
+        // 普通编辑不允许修改状态,将 status 置空,让 SQL 不更新该字段
+        religionPerson.setStatus(null);
         religionPerson.setUpdateTime(DateUtils.getNowDate());
         return religionPersonMapper.updateReligionPerson(religionPerson);
     }
@@ -94,4 +384,699 @@ public class ReligionPersonServiceImpl implements IReligionPersonService
     {
         return religionPersonMapper.deleteReligionPersonByPersonId(personId);
     }
+    
+    /**
+     * 判断字段值是否发生变化
+     * 规则:
+     * 1. 把 null、""、全空格 都认为是"空值"
+     * 2. 如果 old 和 new 都是空值 → 未变更
+     * 3. 如果 old 和 new 文本相同(去掉首尾空格后相等)→ 未变更
+     * 4. 只有 old 和 new 不相等时,才认为是"有变更"
+     * 
+     * @param oldVal 旧值
+     * @param newVal 新值
+     * @return true 表示有变更,false 表示未变更
+     */
+    private boolean hasChanged(Object oldVal, Object newVal)
+    {
+        String oldStr = oldVal == null ? "" : oldVal.toString().trim();
+        String newStr = newVal == null ? "" : newVal.toString().trim();
+        
+        // old 和 new 都为空:认为未变更
+        if (oldStr.isEmpty() && newStr.isEmpty())
+        {
+            return false;
+        }
+        
+        // 文本内容完全相同:认为未变更
+        return !oldStr.equals(newStr);
+    }
+    
+    /**
+     * 判断值是否非空(用于新增操作时过滤空字段)
+     * 
+     * @param value 值
+     * @return true 表示非空,false 表示为空
+     */
+    private boolean isNotEmpty(Object value)
+    {
+        if (value == null)
+        {
+            return false;
+        }
+        String str = value.toString().trim();
+        return !str.isEmpty();
+    }
+    
+    /**
+     * 将字符串字段放入 JSONObject(仅当非空时)
+     * 
+     * @param json JSONObject 对象
+     * @param key 字段 key
+     * @param value 字段值
+     */
+    private void putIfNotBlank(JSONObject json, String key, String value)
+    {
+        if (value != null && !value.trim().isEmpty())
+        {
+            json.put(key, value);
+        }
+    }
+    
+    /**
+     * 将非字符串字段放入 JSONObject(仅当非 null 时)
+     * 
+     * @param json JSONObject 对象
+     * @param key 字段 key
+     * @param value 字段值
+     */
+    private void putIfNotNull(JSONObject json, String key, Object value)
+    {
+        if (value != null)
+        {
+            // 如果是 Date 类型,格式化为字符串
+            if (value instanceof Date)
+            {
+                json.put(key, formatDate((Date) value));
+            }
+            else
+            {
+                json.put(key, value);
+            }
+        }
+    }
+    
+    /**
+     * 格式化日期为字符串(yyyy-MM-dd)
+     * 
+     * @param date 日期对象
+     * @return 格式化后的日期字符串
+     */
+    private String formatDate(Date date)
+    {
+        if (date == null)
+        {
+            return null;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        return sdf.format(date);
+    }
+
+    /**
+     * 根据批量条件查询教职人员列表(用于批量维护预览)
+     * 
+     * @param req 批量查询条件
+     * @return 教职人员集合
+     */
+    @Override
+    public List<ReligionPerson> selectByBatchCondition(ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        return religionPersonMapper.selectByBatchCondition(req);
+    }
+
+    /**
+     * 根据批量条件统计教职人员数量
+     * 
+     * @param req 批量查询条件
+     * @return 数量
+     */
+    @Override
+    public int countByBatchCondition(ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        return religionPersonMapper.countByBatchCondition(req);
+    }
+
+    /**
+     * 根据批量条件更新教职人员
+     * 
+     * @param req 批量更新请求(包含条件和新值)
+     * @return 更新的记录数
+     */
+    @Override
+    public int batchUpdateByCondition(ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        // 1. 批量更新前先查询受影响的人员,用于生成操作日志
+        List<ReligionPerson> affectedPersons = religionPersonMapper.selectByBatchCondition(req);
+        
+        // 2. 执行批量更新
+        int updateCount = religionPersonMapper.batchUpdateByCondition(req);
+        
+        // 3. 如果更新成功且有受影响的记录,记录操作日志
+        if (updateCount > 0 && affectedPersons != null && !affectedPersons.isEmpty())
+        {
+            // 构建字段变化列表
+            List<Map<String, Object>> changeList = new ArrayList<>();
+            
+            for (ReligionPerson person : affectedPersons)
+            {
+                List<Map<String, Object>> personChanges = new ArrayList<>();
+                
+                // 检查所属场所
+                if (req.getNewPlaceId() != null && !req.getNewPlaceId().equals(person.getPlaceId()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "place_id");
+                    change.put("label", "所属场所");
+                    change.put("old", person.getPlaceId() != null ? person.getPlaceId().toString() : "");
+                    change.put("new", req.getNewPlaceId().toString());
+                    personChanges.add(change);
+                }
+                
+                // 检查宗教类别
+                if (isNotEmpty(req.getNewReligionType()) && !req.getNewReligionType().equals(person.getReligionType()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "religion_type");
+                    change.put("label", "宗教类别");
+                    change.put("old", person.getReligionType() != null ? person.getReligionType() : "");
+                    change.put("new", req.getNewReligionType());
+                    personChanges.add(change);
+                }
+                
+                // 检查性别
+                if (isNotEmpty(req.getNewGender()) && !req.getNewGender().equals(person.getGender()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "gender");
+                    change.put("label", "性别");
+                    change.put("old", person.getGender() != null ? person.getGender() : "");
+                    change.put("new", req.getNewGender());
+                    personChanges.add(change);
+                }
+                
+                // 检查民族
+                if (isNotEmpty(req.getNewNation()) && !req.getNewNation().equals(person.getNation()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "nation");
+                    change.put("label", "民族");
+                    change.put("old", person.getNation() != null ? person.getNation() : "");
+                    change.put("new", req.getNewNation());
+                    personChanges.add(change);
+                }
+                
+                // 检查出生日期
+                if (req.getNewBirthday() != null && !isSameDate(req.getNewBirthday(), person.getBirthday()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "birthday");
+                    change.put("label", "出生日期");
+                    change.put("old", formatDate(person.getBirthday()));
+                    change.put("new", formatDate(req.getNewBirthday()));
+                    personChanges.add(change);
+                }
+                
+                // 检查所属区县
+                if (isNotEmpty(req.getNewCounty()) && !req.getNewCounty().equals(person.getCounty()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "county");
+                    change.put("label", "所属区县");
+                    change.put("old", person.getCounty() != null ? person.getCounty() : "");
+                    change.put("new", req.getNewCounty());
+                    personChanges.add(change);
+                }
+                
+                // 检查家庭地址
+                if (isNotEmpty(req.getNewAddress()) && !req.getNewAddress().equals(person.getAddress()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "address");
+                    change.put("label", "家庭地址");
+                    change.put("old", person.getAddress() != null ? person.getAddress() : "");
+                    change.put("new", req.getNewAddress());
+                    personChanges.add(change);
+                }
+                
+                // 检查政治面貌
+                if (isNotEmpty(req.getNewPoliticalStatus()) && !req.getNewPoliticalStatus().equals(person.getPoliticalStatus()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "political_status");
+                    change.put("label", "政治面貌");
+                    change.put("old", person.getPoliticalStatus() != null ? person.getPoliticalStatus() : "");
+                    change.put("new", req.getNewPoliticalStatus());
+                    personChanges.add(change);
+                }
+                
+                // 检查国民教育学历
+                if (isNotEmpty(req.getNewEduNational()) && !req.getNewEduNational().equals(person.getEduNational()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "edu_national");
+                    change.put("label", "国民教育学历");
+                    change.put("old", person.getEduNational() != null ? person.getEduNational() : "");
+                    change.put("new", req.getNewEduNational());
+                    personChanges.add(change);
+                }
+                
+                // 检查宗教学历
+                if (isNotEmpty(req.getNewEduReligious()) && !req.getNewEduReligious().equals(person.getEduReligious()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "edu_religious");
+                    change.put("label", "宗教学历");
+                    change.put("old", person.getEduReligious() != null ? person.getEduReligious() : "");
+                    change.put("new", req.getNewEduReligious());
+                    personChanges.add(change);
+                }
+                
+                // 检查入教/出家时间
+                if (req.getNewJoinTime() != null && !isSameDate(req.getNewJoinTime(), person.getJoinTime()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "join_time");
+                    change.put("label", "入教/出家时间");
+                    change.put("old", formatDate(person.getJoinTime()));
+                    change.put("new", formatDate(req.getNewJoinTime()));
+                    personChanges.add(change);
+                }
+                
+                // 检查联系电话
+                if (isNotEmpty(req.getNewPhone()) && !req.getNewPhone().equals(person.getPhone()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "phone");
+                    change.put("label", "联系电话");
+                    change.put("old", person.getPhone() != null ? person.getPhone() : "");
+                    change.put("new", req.getNewPhone());
+                    personChanges.add(change);
+                }
+                
+                // 检查户籍所在地
+                if (isNotEmpty(req.getNewDomicile()) && !req.getNewDomicile().equals(person.getDomicile()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "domicile");
+                    change.put("label", "户籍所在地");
+                    change.put("old", person.getDomicile() != null ? person.getDomicile() : "");
+                    change.put("new", req.getNewDomicile());
+                    personChanges.add(change);
+                }
+                
+                // 检查籍贯
+                if (isNotEmpty(req.getNewNativePlace()) && !req.getNewNativePlace().equals(person.getNativePlace()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "native_place");
+                    change.put("label", "籍贯");
+                    change.put("old", person.getNativePlace() != null ? person.getNativePlace() : "");
+                    change.put("new", req.getNewNativePlace());
+                    personChanges.add(change);
+                }
+                
+                // 检查所属宫观名称
+                if (isNotEmpty(req.getNewOrgName()) && !req.getNewOrgName().equals(person.getOrgName()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "org_name");
+                    change.put("label", "所属宫观名称");
+                    change.put("old", person.getOrgName() != null ? person.getOrgName() : "");
+                    change.put("new", req.getNewOrgName());
+                    personChanges.add(change);
+                }
+                
+                // 检查所属宫观地址
+                if (isNotEmpty(req.getNewOrgAddress()) && !req.getNewOrgAddress().equals(person.getOrgAddress()))
+                {
+                    Map<String, Object> change = new HashMap<>();
+                    change.put("field", "org_address");
+                    change.put("label", "所属宫观地址");
+                    change.put("old", person.getOrgAddress() != null ? person.getOrgAddress() : "");
+                    change.put("new", req.getNewOrgAddress());
+                    personChanges.add(change);
+                }
+                
+                // 只记录有变更的人员
+                if (!personChanges.isEmpty())
+                {
+                    Map<String, Object> personChangeRecord = new HashMap<>();
+                    personChangeRecord.put("personId", person.getPersonId());
+                    personChangeRecord.put("name", person.getName());
+                    personChangeRecord.put("changes", personChanges);
+                    changeList.add(personChangeRecord);
+                }
+            }
+            
+            // 4. 写入操作日志
+            if (!changeList.isEmpty())
+            {
+                BizAuditLog auditLog = new BizAuditLog();
+                
+                // 获取当前登录用户信息
+                SysUser currentUser = ShiroUtils.getSysUser();
+                if (currentUser != null)
+                {
+                    auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                    auditLog.setUserName(currentUser.getLoginName());
+                }
+                
+                auditLog.setOpType("批量修改");
+                auditLog.setResourceType("宗教教职人员管理");
+                auditLog.setResourceId("0");
+                auditLog.setOpDetail(JSONObject.toJSONString(changeList));
+                auditLog.setOpTime(new Date());
+                auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+                auditLog.setExtra(null);
+                
+                religionAuditLogService.insertReligionAuditLog(auditLog);
+            }
+        }
+        
+        return updateCount;
+    }
+
+    /**
+     * 批量更新预览
+     * 
+     * @param req 批量更新请求(包含条件和新值)
+     * @return 预览响应(包含受影响人员数量、变更记录数、变更明细列表)
+     */
+    @Override
+    public ReligionPersonBatchUpdatePreviewResponse buildBatchUpdatePreview(ReligionPersonBatchUpdateByConditionRequest req)
+    {
+        // 1. 按筛选条件查询所有匹配人员
+        List<ReligionPerson> persons = religionPersonMapper.selectByBatchCondition(req);
+        
+        if (persons == null || persons.isEmpty())
+        {
+            return new ReligionPersonBatchUpdatePreviewResponse(0, 0, new ArrayList<>());
+        }
+        
+        // 2. 构建字段映射表(字段key -> 字段标签)
+        Map<String, String> fieldLabelMap = buildFieldLabelMap();
+        
+        // 3. 遍历每个人员,检查所有允许批量修改的字段
+        List<ChangeDetail> changeDetails = new ArrayList<>();
+        
+        for (ReligionPerson person : persons)
+        {
+            // 检查所属场所
+            if (req.getNewPlaceId() != null && !req.getNewPlaceId().equals(person.getPlaceId()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "placeId",
+                    fieldLabelMap.get("placeId"),
+                    person.getPlaceId() != null ? person.getPlaceId().toString() : "",
+                    req.getNewPlaceId().toString()
+                ));
+            }
+            
+            // 检查宗教类别
+            if (isNotEmpty(req.getNewReligionType()) && !req.getNewReligionType().equals(person.getReligionType()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "religionType",
+                    fieldLabelMap.get("religionType"),
+                    person.getReligionType() != null ? person.getReligionType() : "",
+                    req.getNewReligionType()
+                ));
+            }
+            
+            // 检查性别
+            if (isNotEmpty(req.getNewGender()) && !req.getNewGender().equals(person.getGender()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "gender",
+                    fieldLabelMap.get("gender"),
+                    person.getGender() != null ? person.getGender() : "",
+                    req.getNewGender()
+                ));
+            }
+            
+            // 检查民族
+            if (isNotEmpty(req.getNewNation()) && !req.getNewNation().equals(person.getNation()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "nation",
+                    fieldLabelMap.get("nation"),
+                    person.getNation() != null ? person.getNation() : "",
+                    req.getNewNation()
+                ));
+            }
+            
+            // 检查出生日期
+            if (req.getNewBirthday() != null && !isSameDate(req.getNewBirthday(), person.getBirthday()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "birthday",
+                    fieldLabelMap.get("birthday"),
+                    formatDate(person.getBirthday()),
+                    formatDate(req.getNewBirthday())
+                ));
+            }
+            
+            // 检查所属区县
+            if (isNotEmpty(req.getNewCounty()) && !req.getNewCounty().equals(person.getCounty()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "county",
+                    fieldLabelMap.get("county"),
+                    person.getCounty() != null ? person.getCounty() : "",
+                    req.getNewCounty()
+                ));
+            }
+            
+            // 检查家庭地址
+            if (isNotEmpty(req.getNewAddress()) && !req.getNewAddress().equals(person.getAddress()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "address",
+                    fieldLabelMap.get("address"),
+                    person.getAddress() != null ? person.getAddress() : "",
+                    req.getNewAddress()
+                ));
+            }
+            
+            // 检查政治面貌
+            if (isNotEmpty(req.getNewPoliticalStatus()) && !req.getNewPoliticalStatus().equals(person.getPoliticalStatus()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "politicalStatus",
+                    fieldLabelMap.get("politicalStatus"),
+                    person.getPoliticalStatus() != null ? person.getPoliticalStatus() : "",
+                    req.getNewPoliticalStatus()
+                ));
+            }
+            
+            // 检查国民教育学历
+            if (isNotEmpty(req.getNewEduNational()) && !req.getNewEduNational().equals(person.getEduNational()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "eduNational",
+                    fieldLabelMap.get("eduNational"),
+                    person.getEduNational() != null ? person.getEduNational() : "",
+                    req.getNewEduNational()
+                ));
+            }
+            
+            // 检查宗教学历
+            if (isNotEmpty(req.getNewEduReligious()) && !req.getNewEduReligious().equals(person.getEduReligious()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "eduReligious",
+                    fieldLabelMap.get("eduReligious"),
+                    person.getEduReligious() != null ? person.getEduReligious() : "",
+                    req.getNewEduReligious()
+                ));
+            }
+            
+            // 检查入教/出家时间
+            if (req.getNewJoinTime() != null && !isSameDate(req.getNewJoinTime(), person.getJoinTime()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "joinTime",
+                    fieldLabelMap.get("joinTime"),
+                    formatDate(person.getJoinTime()),
+                    formatDate(req.getNewJoinTime())
+                ));
+            }
+            
+            // 检查联系电话
+            if (isNotEmpty(req.getNewPhone()) && !req.getNewPhone().equals(person.getPhone()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "phone",
+                    fieldLabelMap.get("phone"),
+                    person.getPhone() != null ? person.getPhone() : "",
+                    req.getNewPhone()
+                ));
+            }
+            
+            // 检查户籍所在地
+            if (isNotEmpty(req.getNewDomicile()) && !req.getNewDomicile().equals(person.getDomicile()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "domicile",
+                    fieldLabelMap.get("domicile"),
+                    person.getDomicile() != null ? person.getDomicile() : "",
+                    req.getNewDomicile()
+                ));
+            }
+            
+            // 检查籍贯
+            if (isNotEmpty(req.getNewNativePlace()) && !req.getNewNativePlace().equals(person.getNativePlace()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "nativePlace",
+                    fieldLabelMap.get("nativePlace"),
+                    person.getNativePlace() != null ? person.getNativePlace() : "",
+                    req.getNewNativePlace()
+                ));
+            }
+            
+            // 检查所属宫观名称
+            if (isNotEmpty(req.getNewOrgName()) && !req.getNewOrgName().equals(person.getOrgName()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "orgName",
+                    fieldLabelMap.get("orgName"),
+                    person.getOrgName() != null ? person.getOrgName() : "",
+                    req.getNewOrgName()
+                ));
+            }
+            
+            // 检查所属宫观地址
+            if (isNotEmpty(req.getNewOrgAddress()) && !req.getNewOrgAddress().equals(person.getOrgAddress()))
+            {
+                changeDetails.add(new ChangeDetail(
+                    person.getPersonId(),
+                    person.getName(),
+                    person.getPlaceId(),
+                    person.getPlaceName(),
+                    "orgAddress",
+                    fieldLabelMap.get("orgAddress"),
+                    person.getOrgAddress() != null ? person.getOrgAddress() : "",
+                    req.getNewOrgAddress()
+                ));
+            }
+        }
+        
+        // 4. 如果没有变更,返回空结果
+        if (changeDetails.isEmpty())
+        {
+            return new ReligionPersonBatchUpdatePreviewResponse(0, 0, new ArrayList<>());
+        }
+        
+        // 5. 统计受影响人员数量(去重)
+        long uniquePersonCount = changeDetails.stream()
+            .map(ChangeDetail::getPersonId)
+            .distinct()
+            .count();
+        
+        return new ReligionPersonBatchUpdatePreviewResponse(
+            (int) uniquePersonCount,
+            changeDetails.size(),
+            changeDetails
+        );
+    }
+    
+    /**
+     * 构建字段标签映射表
+     * 
+     * @return 字段key -> 字段标签的映射
+     */
+    private Map<String, String> buildFieldLabelMap()
+    {
+        Map<String, String> map = new HashMap<>();
+        map.put("placeId", "所属场所");
+        map.put("religionType", "宗教类别");
+        map.put("gender", "性别");
+        map.put("nation", "民族");
+        map.put("birthday", "出生日期");
+        map.put("county", "所属区县");
+        map.put("address", "家庭地址");
+        map.put("politicalStatus", "政治面貌");
+        map.put("eduNational", "国民教育学历");
+        map.put("eduReligious", "宗教学历");
+        map.put("joinTime", "入教/出家时间");
+        map.put("phone", "联系电话");
+        map.put("domicile", "户籍所在地");
+        map.put("nativePlace", "籍贯");
+        map.put("orgName", "所属宫观名称");
+        map.put("orgAddress", "所属宫观地址");
+        return map;
+    }
+    
+    /**
+     * 判断两个日期是否相同(只比较日期部分,忽略时间)
+     * 
+     * @param date1 日期1
+     * @param date2 日期2
+     * @return true表示相同,false表示不同
+     */
+    private boolean isSameDate(Date date1, Date date2)
+    {
+        if (date1 == null && date2 == null)
+        {
+            return true;
+        }
+        if (date1 == null || date2 == null)
+        {
+            return false;
+        }
+        String str1 = formatDate(date1);
+        String str2 = formatDate(date2);
+        return str1.equals(str2);
+    }
 }

+ 7 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/service/IReligionPlaceService.java

@@ -57,6 +57,13 @@ public interface IReligionPlaceService
      * @return 结果
      */
     public int deletePlaceById(Long placeId);
+
+    /**
+     * 获取所有场所选项(用于下拉框)
+     * 
+     * @return 场所列表(仅包含 placeId 和 placeName)
+     */
+    public List<ReligionPlace> getPlaceOptions();
 }
 
 

+ 361 - 6
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/service/impl/ReligionPlaceServiceImpl.java

@@ -1,12 +1,21 @@
 package com.ruoyi.project.religion.place.service.impl;
 
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.IpUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.ShiroUtils;
 import com.ruoyi.project.religion.place.mapper.ReligionPlaceMapper;
 import com.ruoyi.project.religion.place.domain.ReligionPlace;
 import com.ruoyi.project.religion.place.service.IReligionPlaceService;
+import com.ruoyi.project.religion.audit.domain.BizAuditLog;
+import com.ruoyi.project.religion.audit.service.IReligionAuditLogService;
 
 /**
  * 宗教活动场所Service业务层处理
@@ -19,6 +28,9 @@ public class ReligionPlaceServiceImpl implements IReligionPlaceService
     @Autowired
     private ReligionPlaceMapper religionPlaceMapper;
 
+    @Autowired
+    private IReligionAuditLogService religionAuditLogService;
+
     /**
      * 查询宗教活动场所列表
      * 
@@ -55,8 +67,63 @@ public class ReligionPlaceServiceImpl implements IReligionPlaceService
         place.setCreateTime(DateUtils.getNowDate());
         int result = religionPlaceMapper.insertPlace(place);
         
-        // TODO: 调用审计日志服务,记录新增操作
-        // auditLogService.logCreate("place", place.getPlaceId(), place);
+        // 记录审计日志:新增场所
+        if (result > 0)
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            auditLog.setOpType("新增");
+            auditLog.setResourceType("宗教活动场所管理");
+            auditLog.setResourceId(String.valueOf(place.getPlaceId()));
+            
+            // 记录新增的场所信息(覆盖所有字段,只记录非空字段)
+            JSONObject detail = new JSONObject();
+            
+            // 基本信息字段
+            putIfNotBlank(detail, "placeName", place.getPlaceName());
+            putIfNotBlank(detail, "religionType", place.getReligionType());
+            putIfNotBlank(detail, "placeCategory", place.getPlaceCategory());
+            putIfNotBlank(detail, "county", place.getCounty());
+            putIfNotBlank(detail, "address", place.getAddress());
+            putIfNotBlank(detail, "chargeName", place.getChargeName());
+            putIfNotBlank(detail, "chargePhone", place.getChargePhone());
+            
+            // 面积字段
+            putIfNotNull(detail, "placeArea", place.getPlaceArea());
+            putIfNotNull(detail, "buildingArea", place.getBuildingArea());
+            
+            // 批准设立相关字段
+            putIfNotBlank(detail, "approveOrgan", place.getApproveOrgan());
+            putIfNotNull(detail, "approveTime", place.getApproveTime());
+            
+            // 登记相关字段
+            putIfNotBlank(detail, "regOrgan", place.getRegOrgan());
+            putIfNotBlank(detail, "regNo", place.getRegNo());
+            putIfNotNull(detail, "regTime", place.getRegTime());
+            
+            // 法人资格相关字段
+            putIfNotNull(detail, "legalQualified", place.getLegalQualified());
+            putIfNotNull(detail, "legalTime", place.getLegalTime());
+            
+            // 其他字段
+            putIfNotBlank(detail, "creditCode", place.getCreditCode());
+            putIfNotNull(detail, "longitude", place.getLongitude());
+            putIfNotNull(detail, "latitude", place.getLatitude());
+            
+            auditLog.setOpDetail(detail.toJSONString());
+            auditLog.setOpTime(new Date());
+            auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+            
+            religionAuditLogService.insertReligionAuditLog(auditLog);
+        }
         
         return result;
     }
@@ -70,14 +137,194 @@ public class ReligionPlaceServiceImpl implements IReligionPlaceService
     @Override
     public int updatePlace(ReligionPlace place)
     {
-        // TODO: 查询旧数据,计算字段差异
-        // ReligionPlace oldPlace = religionPlaceMapper.selectPlaceById(place.getPlaceId());
+        // 查询旧数据,用于对比字段差异
+        ReligionPlace oldPlace = religionPlaceMapper.selectPlaceById(place.getPlaceId());
         
         place.setUpdateTime(DateUtils.getNowDate());
         int result = religionPlaceMapper.updatePlace(place);
         
-        // TODO: 调用审计日志服务,记录修改操作及字段差异
-        // auditLogService.logUpdate("place", place.getPlaceId(), oldPlace, place);
+        // 记录审计日志:修改场所
+        if (result > 0 && oldPlace != null)
+        {
+            BizAuditLog auditLog = new BizAuditLog();
+            
+            // 获取当前登录用户信息
+            SysUser currentUser = ShiroUtils.getSysUser();
+            if (currentUser != null)
+            {
+                auditLog.setUserId(String.valueOf(currentUser.getUserId()));
+                auditLog.setUserName(currentUser.getLoginName());
+            }
+            
+            auditLog.setOpType("修改");
+            auditLog.setResourceType("宗教活动场所管理");
+            auditLog.setResourceId(String.valueOf(place.getPlaceId()));
+            
+            // 对比字段差异,只记录确实发生变化的字段
+            JSONObject detail = new JSONObject();
+            
+            if (hasChanged(oldPlace.getPlaceName(), place.getPlaceName()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getPlaceName());
+                change.put("new", place.getPlaceName());
+                detail.put("placeName", change);
+            }
+            
+            if (hasChanged(oldPlace.getReligionType(), place.getReligionType()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getReligionType());
+                change.put("new", place.getReligionType());
+                detail.put("religionType", change);
+            }
+            
+            if (hasChanged(oldPlace.getPlaceCategory(), place.getPlaceCategory()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getPlaceCategory());
+                change.put("new", place.getPlaceCategory());
+                detail.put("placeCategory", change);
+            }
+            
+            if (hasChanged(oldPlace.getChargeName(), place.getChargeName()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getChargeName());
+                change.put("new", place.getChargeName());
+                detail.put("chargeName", change);
+            }
+            
+            if (hasChanged(oldPlace.getChargePhone(), place.getChargePhone()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getChargePhone());
+                change.put("new", place.getChargePhone());
+                detail.put("chargePhone", change);
+            }
+            
+            if (hasChanged(oldPlace.getCounty(), place.getCounty()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getCounty());
+                change.put("new", place.getCounty());
+                detail.put("county", change);
+            }
+            
+            if (hasChanged(oldPlace.getAddress(), place.getAddress()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getAddress());
+                change.put("new", place.getAddress());
+                detail.put("address", change);
+            }
+            
+            if (hasChanged(oldPlace.getPlaceArea(), place.getPlaceArea()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getPlaceArea());
+                change.put("new", place.getPlaceArea());
+                detail.put("placeArea", change);
+            }
+            
+            if (hasChanged(oldPlace.getBuildingArea(), place.getBuildingArea()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getBuildingArea());
+                change.put("new", place.getBuildingArea());
+                detail.put("buildingArea", change);
+            }
+            
+            if (hasChanged(oldPlace.getApproveOrgan(), place.getApproveOrgan()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getApproveOrgan());
+                change.put("new", place.getApproveOrgan());
+                detail.put("approveOrgan", change);
+            }
+            
+            if (hasChanged(oldPlace.getApproveTime(), place.getApproveTime()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", formatDate(oldPlace.getApproveTime()));
+                change.put("new", formatDate(place.getApproveTime()));
+                detail.put("approveTime", change);
+            }
+            
+            if (hasChanged(oldPlace.getRegOrgan(), place.getRegOrgan()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getRegOrgan());
+                change.put("new", place.getRegOrgan());
+                detail.put("regOrgan", change);
+            }
+            
+            if (hasChanged(oldPlace.getRegNo(), place.getRegNo()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getRegNo());
+                change.put("new", place.getRegNo());
+                detail.put("regNo", change);
+            }
+            
+            if (hasChanged(oldPlace.getRegTime(), place.getRegTime()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", formatDate(oldPlace.getRegTime()));
+                change.put("new", formatDate(place.getRegTime()));
+                detail.put("regTime", change);
+            }
+            
+            if (hasChanged(oldPlace.getLegalQualified(), place.getLegalQualified()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getLegalQualified());
+                change.put("new", place.getLegalQualified());
+                detail.put("legalQualified", change);
+            }
+            
+            if (hasChanged(oldPlace.getLegalTime(), place.getLegalTime()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", formatDate(oldPlace.getLegalTime()));
+                change.put("new", formatDate(place.getLegalTime()));
+                detail.put("legalTime", change);
+            }
+            
+            if (hasChanged(oldPlace.getCreditCode(), place.getCreditCode()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getCreditCode());
+                change.put("new", place.getCreditCode());
+                detail.put("creditCode", change);
+            }
+            
+            if (hasChanged(oldPlace.getLongitude(), place.getLongitude()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getLongitude());
+                change.put("new", place.getLongitude());
+                detail.put("longitude", change);
+            }
+            
+            if (hasChanged(oldPlace.getLatitude(), place.getLatitude()))
+            {
+                JSONObject change = new JSONObject();
+                change.put("old", oldPlace.getLatitude());
+                change.put("new", place.getLatitude());
+                detail.put("latitude", change);
+            }
+            
+            // 只有当有字段变更时才记录审计日志
+            if (!detail.isEmpty())
+            {
+                auditLog.setOpDetail(detail.toJSONString());
+                auditLog.setOpTime(new Date());
+                auditLog.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+                
+                religionAuditLogService.insertReligionAuditLog(auditLog);
+            }
+        }
         
         return result;
     }
@@ -105,6 +352,114 @@ public class ReligionPlaceServiceImpl implements IReligionPlaceService
     {
         return religionPlaceMapper.deletePlaceById(placeId);
     }
+
+    /**
+     * 获取所有场所选项(用于下拉框)
+     * 
+     * @return 场所列表(仅包含 placeId 和 placeName)
+     */
+    @Override
+    public List<ReligionPlace> getPlaceOptions()
+    {
+        return religionPlaceMapper.selectPlaceList(new ReligionPlace());
+    }
+    
+    /**
+     * 判断字段值是否发生变化
+     * 规则:
+     * 1. 把 null、""、全空格 都认为是"空值"
+     * 2. 如果 old 和 new 都是空值 → 未变更
+     * 3. 如果 old 和 new 文本相同(去掉首尾空格后相等)→ 未变更
+     * 4. 只有 old 和 new 不相等时,才认为是"有变更"
+     * 
+     * @param oldVal 旧值
+     * @param newVal 新值
+     * @return true 表示有变更,false 表示未变更
+     */
+    private boolean hasChanged(Object oldVal, Object newVal)
+    {
+        String oldStr = oldVal == null ? "" : oldVal.toString().trim();
+        String newStr = newVal == null ? "" : newVal.toString().trim();
+        
+        // old 和 new 都为空:认为未变更
+        if (oldStr.isEmpty() && newStr.isEmpty())
+        {
+            return false;
+        }
+        
+        // 文本内容完全相同:认为未变更
+        return !oldStr.equals(newStr);
+    }
+    
+    /**
+     * 判断值是否非空(用于新增操作时过滤空字段)
+     * 
+     * @param value 值
+     * @return true 表示非空,false 表示为空
+     */
+    private boolean isNotEmpty(Object value)
+    {
+        if (value == null)
+        {
+            return false;
+        }
+        String str = value.toString().trim();
+        return !str.isEmpty();
+    }
+    
+    /**
+     * 将字符串字段放入 JSONObject(仅当非空时)
+     * 
+     * @param json JSONObject 对象
+     * @param key 字段 key
+     * @param value 字段值
+     */
+    private void putIfNotBlank(JSONObject json, String key, String value)
+    {
+        if (value != null && !value.trim().isEmpty())
+        {
+            json.put(key, value);
+        }
+    }
+    
+    /**
+     * 将非字符串字段放入 JSONObject(仅当非 null 时)
+     * 
+     * @param json JSONObject 对象
+     * @param key 字段 key
+     * @param value 字段值
+     */
+    private void putIfNotNull(JSONObject json, String key, Object value)
+    {
+        if (value != null)
+        {
+            // 如果是 Date 类型,格式化为字符串
+            if (value instanceof Date)
+            {
+                json.put(key, formatDate((Date) value));
+            }
+            else
+            {
+                json.put(key, value);
+            }
+        }
+    }
+    
+    /**
+     * 格式化日期为字符串(yyyy-MM-dd)
+     * 
+     * @param date 日期对象
+     * @return 格式化后的日期字符串
+     */
+    private String formatDate(Date date)
+    {
+        if (date == null)
+        {
+            return null;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        return sdf.format(date);
+    }
 }
 
 

+ 1 - 1
ruoyi-admin/src/main/resources/application.yml

@@ -1,7 +1,7 @@
 # 项目相关配置
 ruoyi:
   # 名称
-  name: RuoYi
+  name: 宗教管理信息系统
   # 版本
   version: 4.8.1
   # 版权年份

+ 93 - 0
ruoyi-admin/src/main/resources/mapper/religion/audit/ReligionAuditLogMapper.xml

@@ -0,0 +1,93 @@
+<?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="com.ruoyi.project.religion.audit.mapper.ReligionAuditLogMapper">
+    
+    <resultMap type="com.ruoyi.project.religion.audit.domain.BizAuditLog" id="BizAuditLogResult">
+        <id     property="logId"          column="log_id"          />
+        <result property="userId"         column="user_id"         />
+        <result property="userName"       column="user_name"       />
+        <result property="opType"         column="op_type"         />
+        <result property="resourceType"   column="resource_type"   />
+        <result property="resourceId"     column="resource_id"     />
+        <result property="opDetail"       column="op_detail"       />
+        <result property="opTime"         column="op_time"         />
+        <result property="ip"             column="ip"              />
+        <result property="extra"          column="extra"           />
+    </resultMap>
+
+    <sql id="selectAuditLogVo">
+        select log_id, user_id, user_name, op_type, resource_type, resource_id, 
+               op_detail, op_time, ip, extra
+        from religion_audit_log
+    </sql>
+
+    <select id="selectReligionAuditLogList" parameterType="com.ruoyi.project.religion.audit.domain.BizAuditLog" resultMap="BizAuditLogResult">
+        <include refid="selectAuditLogVo"/>
+        <where>  
+            <if test="userId != null and userId != ''">
+                AND user_id = #{userId}
+            </if>
+            <if test="userName != null and userName != ''">
+                AND user_name like concat('%', #{userName}, '%')
+            </if>
+            <if test="opType != null and opType != ''">
+                AND op_type = #{opType}
+            </if>
+            <if test="resourceType != null and resourceType != ''">
+                AND resource_type = #{resourceType}
+            </if>
+            <if test="resourceId != null and resourceId != ''">
+                AND resource_id = #{resourceId}
+            </if>
+            <if test="params.beginTime != null and params.beginTime != ''">
+                AND date_format(op_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+            </if>
+            <if test="params.endTime != null and params.endTime != ''">
+                AND date_format(op_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+            </if>
+        </where>
+        order by op_time desc
+    </select>
+    
+    <select id="selectReligionAuditLogById" parameterType="Long" resultMap="BizAuditLogResult">
+        <include refid="selectAuditLogVo"/>
+        where log_id = #{logId}
+    </select>
+        
+    <insert id="insertReligionAuditLog" parameterType="com.ruoyi.project.religion.audit.domain.BizAuditLog" useGeneratedKeys="true" keyProperty="logId">
+        insert into religion_audit_log
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="userId != null">user_id,</if>
+            <if test="userName != null">user_name,</if>
+            <if test="opType != null">op_type,</if>
+            <if test="resourceType != null">resource_type,</if>
+            <if test="resourceId != null">resource_id,</if>
+            <if test="opDetail != null">op_detail,</if>
+            <if test="opTime != null">op_time,</if>
+            <if test="ip != null">ip,</if>
+            <if test="extra != null">extra,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="userId != null">#{userId},</if>
+            <if test="userName != null">#{userName},</if>
+            <if test="opType != null">#{opType},</if>
+            <if test="resourceType != null">#{resourceType},</if>
+            <if test="resourceId != null">#{resourceId},</if>
+            <if test="opDetail != null">#{opDetail},</if>
+            <if test="opTime != null">#{opTime},</if>
+            <if test="ip != null">#{ip},</if>
+            <if test="extra != null">#{extra},</if>
+         </trim>
+    </insert>
+
+    <delete id="deleteReligionAuditLogByIds" parameterType="Long">
+        delete from religion_audit_log where log_id in 
+        <foreach item="logId" collection="array" open="(" separator="," close=")">
+            #{logId}
+        </foreach>
+    </delete>
+
+</mapper>
+

+ 112 - 0
ruoyi-admin/src/main/resources/mapper/religion/person/ReligionImportBatchMapper.xml

@@ -0,0 +1,112 @@
+<?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="com.ruoyi.project.religion.person.mapper.ReligionImportBatchMapper">
+    
+    <resultMap type="com.ruoyi.project.religion.person.domain.ReligionImportBatch" id="ReligionImportBatchResult">
+        <result property="batchId"    column="batch_id"    />
+        <result property="batchNo"    column="batch_no"    />
+        <result property="fileName"    column="file_name"    />
+        <result property="uploader"    column="uploader"    />
+        <result property="uploadTime"    column="upload_time"    />
+        <result property="lastProcessTime"    column="last_process_time"    />
+        <result property="totalRows"    column="total_rows"    />
+        <result property="validRows"    column="valid_rows"    />
+        <result property="errorRows"    column="error_rows"    />
+        <result property="conflictRows"    column="conflict_rows"    />
+        <result property="importedRows"    column="imported_rows"    />
+        <result property="abandonedRows"    column="abandoned_rows"    />
+        <result property="status"    column="status"    />
+        <result property="extra"    column="extra"    />
+    </resultMap>
+
+    <sql id="selectReligionImportBatchVo">
+        select batch_id, batch_no, file_name, uploader, upload_time, last_process_time, 
+               total_rows, valid_rows, error_rows, conflict_rows, imported_rows, abandoned_rows, 
+               status, extra
+        from religion_import_batch
+    </sql>
+
+    <select id="selectReligionImportBatchList" parameterType="com.ruoyi.project.religion.person.domain.ReligionImportBatch" resultMap="ReligionImportBatchResult">
+        <include refid="selectReligionImportBatchVo"/>
+        <where>  
+            <if test="batchNo != null  and batchNo != ''"> and batch_no = #{batchNo}</if>
+            <if test="fileName != null  and fileName != ''"> and file_name like concat('%', #{fileName}, '%')</if>
+            <if test="uploader != null  and uploader != ''"> and uploader = #{uploader}</if>
+            <if test="status != null  and status != ''"> and status = #{status}</if>
+        </where>
+        order by upload_time desc
+    </select>
+    
+    <select id="selectReligionImportBatchByBatchId" parameterType="Long" resultMap="ReligionImportBatchResult">
+        <include refid="selectReligionImportBatchVo"/>
+        where batch_id = #{batchId}
+    </select>
+        
+    <insert id="insertReligionImportBatch" parameterType="com.ruoyi.project.religion.person.domain.ReligionImportBatch" useGeneratedKeys="true" keyProperty="batchId">
+        insert into religion_import_batch
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="batchNo != null and batchNo != ''">batch_no,</if>
+            <if test="fileName != null and fileName != ''">file_name,</if>
+            <if test="uploader != null and uploader != ''">uploader,</if>
+            <if test="uploadTime != null">upload_time,</if>
+            <if test="lastProcessTime != null">last_process_time,</if>
+            <if test="totalRows != null">total_rows,</if>
+            <if test="validRows != null">valid_rows,</if>
+            <if test="errorRows != null">error_rows,</if>
+            <if test="conflictRows != null">conflict_rows,</if>
+            <if test="importedRows != null">imported_rows,</if>
+            <if test="abandonedRows != null">abandoned_rows,</if>
+            <if test="status != null and status != ''">status,</if>
+            <if test="extra != null">extra,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="batchNo != null and batchNo != ''">#{batchNo},</if>
+            <if test="fileName != null and fileName != ''">#{fileName},</if>
+            <if test="uploader != null and uploader != ''">#{uploader},</if>
+            <if test="uploadTime != null">#{uploadTime},</if>
+            <if test="lastProcessTime != null">#{lastProcessTime},</if>
+            <if test="totalRows != null">#{totalRows},</if>
+            <if test="validRows != null">#{validRows},</if>
+            <if test="errorRows != null">#{errorRows},</if>
+            <if test="conflictRows != null">#{conflictRows},</if>
+            <if test="importedRows != null">#{importedRows},</if>
+            <if test="abandonedRows != null">#{abandonedRows},</if>
+            <if test="status != null and status != ''">#{status},</if>
+            <if test="extra != null">#{extra},</if>
+         </trim>
+    </insert>
+
+    <update id="updateReligionImportBatch" parameterType="com.ruoyi.project.religion.person.domain.ReligionImportBatch">
+        update religion_import_batch
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="batchNo != null and batchNo != ''">batch_no = #{batchNo},</if>
+            <if test="fileName != null and fileName != ''">file_name = #{fileName},</if>
+            <if test="uploader != null and uploader != ''">uploader = #{uploader},</if>
+            <if test="uploadTime != null">upload_time = #{uploadTime},</if>
+            <if test="lastProcessTime != null">last_process_time = #{lastProcessTime},</if>
+            <if test="totalRows != null">total_rows = #{totalRows},</if>
+            <if test="validRows != null">valid_rows = #{validRows},</if>
+            <if test="errorRows != null">error_rows = #{errorRows},</if>
+            <if test="conflictRows != null">conflict_rows = #{conflictRows},</if>
+            <if test="importedRows != null">imported_rows = #{importedRows},</if>
+            <if test="abandonedRows != null">abandoned_rows = #{abandonedRows},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="extra != null">extra = #{extra},</if>
+        </trim>
+        where batch_id = #{batchId}
+    </update>
+
+    <delete id="deleteReligionImportBatchByBatchId" parameterType="Long">
+        delete from religion_import_batch where batch_id = #{batchId}
+    </delete>
+
+    <delete id="deleteReligionImportBatchByBatchIds" parameterType="Long">
+        delete from religion_import_batch where batch_id in 
+        <foreach item="batchId" collection="array" open="(" separator="," close=")">
+            #{batchId}
+        </foreach>
+    </delete>
+</mapper>
+

+ 131 - 0
ruoyi-admin/src/main/resources/mapper/religion/person/ReligionImportItemMapper.xml

@@ -0,0 +1,131 @@
+<?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="com.ruoyi.project.religion.person.mapper.ReligionImportItemMapper">
+    
+    <resultMap type="com.ruoyi.project.religion.person.domain.ReligionImportItem" id="ReligionImportItemResult">
+        <result property="itemId"    column="item_id"    />
+        <result property="batchId"    column="batch_id"    />
+        <result property="rowNo"    column="row_no"    />
+        <result property="jsonData"    column="json_data"    />
+        <result property="errorMsg"    column="error_msg"    />
+        <result property="conflictFlag"    column="conflict_flag"    />
+        <result property="status"    column="status"    />
+        <result property="action"    column="action"    />
+        <result property="actionBy"    column="action_by"    />
+        <result property="actionTime"    column="action_time"    />
+        <result property="processedFlag"    column="processed_flag"    />
+        <result property="createTime"    column="create_time"    />
+    </resultMap>
+
+    <sql id="selectReligionImportItemVo">
+        select item_id, batch_id, row_no, json_data, error_msg, conflict_flag, 
+               status, action, action_by, action_time, processed_flag, create_time
+        from religion_import_item
+    </sql>
+
+    <select id="selectReligionImportItemList" parameterType="com.ruoyi.project.religion.person.domain.ReligionImportItem" resultMap="ReligionImportItemResult">
+        <include refid="selectReligionImportItemVo"/>
+        <where>  
+            <if test="batchId != null">and batch_id = #{batchId}</if>
+            <if test="status != null and status != ''">and status = #{status}</if>
+            <if test="action != null and action != ''">and action = #{action}</if>
+            <if test="processedFlag != null">and processed_flag = #{processedFlag}</if>
+        </where>
+        order by row_no asc
+    </select>
+    
+    <select id="selectReligionImportItemByItemId" parameterType="Long" resultMap="ReligionImportItemResult">
+        <include refid="selectReligionImportItemVo"/>
+        where item_id = #{itemId}
+    </select>
+        
+    <insert id="insertReligionImportItem" parameterType="com.ruoyi.project.religion.person.domain.ReligionImportItem" useGeneratedKeys="true" keyProperty="itemId">
+        insert into religion_import_item
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="batchId != null">batch_id,</if>
+            <if test="rowNo != null">row_no,</if>
+            <if test="jsonData != null">json_data,</if>
+            <if test="errorMsg != null">error_msg,</if>
+            <if test="conflictFlag != null">conflict_flag,</if>
+            <if test="status != null and status != ''">status,</if>
+            <if test="action != null">action,</if>
+            <if test="actionBy != null">action_by,</if>
+            <if test="actionTime != null">action_time,</if>
+            <if test="processedFlag != null">processed_flag,</if>
+            <if test="createTime != null">create_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="batchId != null">#{batchId},</if>
+            <if test="rowNo != null">#{rowNo},</if>
+            <if test="jsonData != null">#{jsonData},</if>
+            <if test="errorMsg != null">#{errorMsg},</if>
+            <if test="conflictFlag != null">#{conflictFlag},</if>
+            <if test="status != null and status != ''">#{status},</if>
+            <if test="action != null">#{action},</if>
+            <if test="actionBy != null">#{actionBy},</if>
+            <if test="actionTime != null">#{actionTime},</if>
+            <if test="processedFlag != null">#{processedFlag},</if>
+            <if test="createTime != null">#{createTime},</if>
+         </trim>
+    </insert>
+
+    <insert id="batchInsertReligionImportItem" parameterType="java.util.List">
+        insert into religion_import_item(batch_id, row_no, json_data, error_msg, conflict_flag, 
+                                         status, action, action_by, action_time, processed_flag, create_time)
+        values
+        <foreach collection="list" item="item" separator=",">
+            (#{item.batchId}, #{item.rowNo}, #{item.jsonData}, #{item.errorMsg}, #{item.conflictFlag},
+             #{item.status}, #{item.action}, #{item.actionBy}, #{item.actionTime}, #{item.processedFlag}, #{item.createTime})
+        </foreach>
+    </insert>
+
+    <update id="updateReligionImportItem" parameterType="com.ruoyi.project.religion.person.domain.ReligionImportItem">
+        update religion_import_item
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="batchId != null">batch_id = #{batchId},</if>
+            <if test="rowNo != null">row_no = #{rowNo},</if>
+            <if test="jsonData != null">json_data = #{jsonData},</if>
+            <if test="errorMsg != null">error_msg = #{errorMsg},</if>
+            <if test="conflictFlag != null">conflict_flag = #{conflictFlag},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="action != null">action = #{action},</if>
+            <if test="actionBy != null">action_by = #{actionBy},</if>
+            <if test="actionTime != null">action_time = #{actionTime},</if>
+            <if test="processedFlag != null">processed_flag = #{processedFlag},</if>
+        </trim>
+        where item_id = #{itemId}
+    </update>
+
+    <delete id="deleteReligionImportItemByItemId" parameterType="Long">
+        delete from religion_import_item where item_id = #{itemId}
+    </delete>
+
+    <delete id="deleteReligionImportItemByItemIds" parameterType="Long">
+        delete from religion_import_item where item_id in 
+        <foreach item="itemId" collection="array" open="(" separator="," close=")">
+            #{itemId}
+        </foreach>
+    </delete>
+
+    <delete id="deleteReligionImportItemByBatchId" parameterType="Long">
+        delete from religion_import_item where batch_id = #{batchId}
+    </delete>
+
+    <select id="checkPersonExists" resultType="int">
+        select count(*) from religion_person 
+        where id_number = #{idNumber} and name = #{name}
+    </select>
+
+    <select id="checkPersonExistsByIdNumber" resultType="int">
+        select count(*) from religion_person 
+        where id_number = #{idNumber}
+    </select>
+
+    <select id="checkPersonExistsByName" resultType="int">
+        select count(*) from religion_person 
+        where name = #{name}
+    </select>
+</mapper>
+

+ 187 - 19
ruoyi-admin/src/main/resources/mapper/religion/person/ReligionPersonMapper.xml

@@ -7,9 +7,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <resultMap type="ReligionPerson" id="ReligionPersonResult">
         <result property="personId"    column="person_id"    />
         <result property="placeId"    column="place_id"    />
+        <result property="placeName"    column="place_name"    />
         <result property="name"    column="name"    />
         <result property="religionType"    column="religion_type"    />
         <result property="gender"    column="gender"    />
+        <result property="nation"    column="nation"    />
         <result property="idNumber"    column="id_number"    />
         <result property="birthday"    column="birthday"    />
         <result property="county"    column="county"    />
@@ -24,6 +26,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="orgName"    column="org_name"    />
         <result property="orgAddress"    column="org_address"    />
         <result property="status"    column="status"    />
+        <result property="countyCheckStatus"    column="county_check_status"    />
+        <result property="countyCheckBy"    column="county_check_by"    />
+        <result property="countyCheckTime"    column="county_check_time"    />
+        <result property="countyCheckComment"    column="county_check_comment"    />
         <result property="createBy"    column="create_by"    />
         <result property="createTime"    column="create_time"    />
         <result property="updateBy"    column="update_by"    />
@@ -31,30 +37,33 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectReligionPersonVo">
-        select person_id, place_id, name, religion_type, gender, id_number, birthday, county, address, political_status, edu_national, edu_religious, join_time, phone, domicile, native_place, org_name, org_address, status, create_by, create_time, update_by, update_time from religion_person
+        select p.person_id, p.place_id, pl.place_name, p.name, p.religion_type, p.gender, p.nation, p.id_number, p.birthday, p.county, p.address, p.political_status, p.edu_national, p.edu_religious, p.join_time, p.phone, p.domicile, p.native_place, p.org_name, p.org_address, p.status, p.county_check_status, p.county_check_by, p.county_check_time, p.county_check_comment, p.create_by, p.create_time, p.update_by, p.update_time 
+        from religion_person p
+        left join religion_place pl on p.place_id = pl.place_id
     </sql>
 
     <select id="selectReligionPersonList" parameterType="ReligionPerson" resultMap="ReligionPersonResult">
         <include refid="selectReligionPersonVo"/>
         <where>  
-            <if test="placeId != null "> and place_id = #{placeId}</if>
-            <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
-            <if test="religionType != null  and religionType != ''"> and religion_type = #{religionType}</if>
-            <if test="gender != null  and gender != ''"> and gender = #{gender}</if>
-            <if test="idNumber != null  and idNumber != ''"> and id_number = #{idNumber}</if>
-            <if test="birthday != null "> and birthday = #{birthday}</if>
-            <if test="county != null  and county != ''"> and county = #{county}</if>
-            <if test="address != null  and address != ''"> and address = #{address}</if>
-            <if test="politicalStatus != null  and politicalStatus != ''"> and political_status = #{politicalStatus}</if>
-            <if test="eduNational != null  and eduNational != ''"> and edu_national = #{eduNational}</if>
-            <if test="eduReligious != null  and eduReligious != ''"> and edu_religious = #{eduReligious}</if>
-            <if test="joinTime != null "> and join_time = #{joinTime}</if>
-            <if test="phone != null  and phone != ''"> and phone = #{phone}</if>
-            <if test="domicile != null  and domicile != ''"> and domicile = #{domicile}</if>
-            <if test="nativePlace != null  and nativePlace != ''"> and native_place = #{nativePlace}</if>
-            <if test="orgName != null  and orgName != ''"> and org_name like concat('%', #{orgName}, '%')</if>
-            <if test="orgAddress != null  and orgAddress != ''"> and org_address = #{orgAddress}</if>
-            <if test="status != null  and status != ''"> and status = #{status}</if>
+            <if test="placeId != null "> and p.place_id = #{placeId}</if>
+            <if test="name != null  and name != ''"> and p.name like concat('%', #{name}, '%')</if>
+            <if test="religionType != null  and religionType != ''"> and p.religion_type = #{religionType}</if>
+            <if test="gender != null  and gender != ''"> and p.gender = #{gender}</if>
+            <if test="nation != null  and nation != ''"> and p.nation = #{nation}</if>
+            <if test="idNumber != null  and idNumber != ''"> and p.id_number = #{idNumber}</if>
+            <if test="birthday != null "> and p.birthday = #{birthday}</if>
+            <if test="county != null  and county != ''"> and p.county = #{county}</if>
+            <if test="address != null  and address != ''"> and p.address = #{address}</if>
+            <if test="politicalStatus != null  and politicalStatus != ''"> and p.political_status = #{politicalStatus}</if>
+            <if test="eduNational != null  and eduNational != ''"> and p.edu_national = #{eduNational}</if>
+            <if test="eduReligious != null  and eduReligious != ''"> and p.edu_religious = #{eduReligious}</if>
+            <if test="joinTime != null "> and p.join_time = #{joinTime}</if>
+            <if test="phone != null  and phone != ''"> and p.phone = #{phone}</if>
+            <if test="domicile != null  and domicile != ''"> and p.domicile = #{domicile}</if>
+            <if test="nativePlace != null  and nativePlace != ''"> and p.native_place = #{nativePlace}</if>
+            <if test="orgName != null  and orgName != ''"> and p.org_name like concat('%', #{orgName}, '%')</if>
+            <if test="orgAddress != null  and orgAddress != ''"> and p.org_address = #{orgAddress}</if>
+            <if test="status != null  and status != ''"> and p.status = #{status}</if>
         </where>
     </select>
     
@@ -70,6 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="name != null and name != ''">name,</if>
             <if test="religionType != null and religionType != ''">religion_type,</if>
             <if test="gender != null">gender,</if>
+            <if test="nation != null">nation,</if>
             <if test="idNumber != null and idNumber != ''">id_number,</if>
             <if test="birthday != null">birthday,</if>
             <if test="county != null">county,</if>
@@ -84,6 +94,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orgName != null">org_name,</if>
             <if test="orgAddress != null">org_address,</if>
             <if test="status != null">status,</if>
+            <if test="countyCheckStatus != null">county_check_status,</if>
+            <if test="countyCheckBy != null">county_check_by,</if>
+            <if test="countyCheckTime != null">county_check_time,</if>
+            <if test="countyCheckComment != null">county_check_comment,</if>
             <if test="createBy != null">create_by,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
@@ -94,6 +108,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="name != null and name != ''">#{name},</if>
             <if test="religionType != null and religionType != ''">#{religionType},</if>
             <if test="gender != null">#{gender},</if>
+            <if test="nation != null">#{nation},</if>
             <if test="idNumber != null and idNumber != ''">#{idNumber},</if>
             <if test="birthday != null">#{birthday},</if>
             <if test="county != null">#{county},</if>
@@ -108,6 +123,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orgName != null">#{orgName},</if>
             <if test="orgAddress != null">#{orgAddress},</if>
             <if test="status != null">#{status},</if>
+            <if test="countyCheckStatus != null">#{countyCheckStatus},</if>
+            <if test="countyCheckBy != null">#{countyCheckBy},</if>
+            <if test="countyCheckTime != null">#{countyCheckTime},</if>
+            <if test="countyCheckComment != null">#{countyCheckComment},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
@@ -122,6 +141,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="name != null and name != ''">name = #{name},</if>
             <if test="religionType != null and religionType != ''">religion_type = #{religionType},</if>
             <if test="gender != null">gender = #{gender},</if>
+            <if test="nation != null">nation = #{nation},</if>
             <if test="idNumber != null and idNumber != ''">id_number = #{idNumber},</if>
             <if test="birthday != null">birthday = #{birthday},</if>
             <if test="county != null">county = #{county},</if>
@@ -136,6 +156,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orgName != null">org_name = #{orgName},</if>
             <if test="orgAddress != null">org_address = #{orgAddress},</if>
             <if test="status != null">status = #{status},</if>
+            <if test="countyCheckStatus != null">county_check_status = #{countyCheckStatus},</if>
+            <if test="countyCheckBy != null">county_check_by = #{countyCheckBy},</if>
+            <if test="countyCheckTime != null">county_check_time = #{countyCheckTime},</if>
+            <if test="countyCheckComment != null">county_check_comment = #{countyCheckComment},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
@@ -155,4 +179,148 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </delete>
 
+    <!-- 根据批量条件查询教职人员列表(用于批量维护预览) -->
+    <select id="selectByBatchCondition" parameterType="com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest" resultMap="ReligionPersonResult">
+        <include refid="selectReligionPersonVo"/>
+        <where>
+            <if test="name != null and name != ''">
+                and p.name like concat('%', #{name}, '%')
+            </if>
+            <if test="idNumber != null and idNumber != ''">
+                and p.id_number = #{idNumber}
+            </if>
+            <if test="placeId != null">
+                and p.place_id = #{placeId}
+            </if>
+            <if test="religionType != null and religionType != ''">
+                and p.religion_type = #{religionType}
+            </if>
+            <if test="gender != null and gender != ''">
+                and p.gender = #{gender}
+            </if>
+            <if test="nation != null and nation != ''">
+                and p.nation = #{nation}
+            </if>
+            <if test="county != null and county != ''">
+                and p.county = #{county}
+            </if>
+            <if test="status != null and status != ''">
+                and p.status = #{status}
+            </if>
+        </where>
+        order by p.person_id desc
+    </select>
+
+    <!-- 根据批量条件统计教职人员数量 -->
+    <select id="countByBatchCondition" parameterType="com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest" resultType="int">
+        select count(*) from religion_person p
+        <where>
+            <if test="name != null and name != ''">
+                and p.name like concat('%', #{name}, '%')
+            </if>
+            <if test="idNumber != null and idNumber != ''">
+                and p.id_number = #{idNumber}
+            </if>
+            <if test="placeId != null">
+                and p.place_id = #{placeId}
+            </if>
+            <if test="religionType != null and religionType != ''">
+                and p.religion_type = #{religionType}
+            </if>
+            <if test="gender != null and gender != ''">
+                and p.gender = #{gender}
+            </if>
+            <if test="nation != null and nation != ''">
+                and p.nation = #{nation}
+            </if>
+            <if test="county != null and county != ''">
+                and p.county = #{county}
+            </if>
+            <if test="status != null and status != ''">
+                and p.status = #{status}
+            </if>
+        </where>
+    </select>
+
+    <!-- 根据批量条件更新教职人员 -->
+    <update id="batchUpdateByCondition" parameterType="com.ruoyi.project.religion.person.domain.dto.ReligionPersonBatchUpdateByConditionRequest">
+        update religion_person
+        <set>
+            <if test="newPlaceId != null">
+                place_id = #{newPlaceId},
+            </if>
+            <if test="newReligionType != null and newReligionType != ''">
+                religion_type = #{newReligionType},
+            </if>
+            <if test="newGender != null and newGender != ''">
+                gender = #{newGender},
+            </if>
+            <if test="newNation != null and newNation != ''">
+                nation = #{newNation},
+            </if>
+            <if test="newBirthday != null">
+                birthday = #{newBirthday},
+            </if>
+            <if test="newCounty != null and newCounty != ''">
+                county = #{newCounty},
+            </if>
+            <if test="newAddress != null and newAddress != ''">
+                address = #{newAddress},
+            </if>
+            <if test="newPoliticalStatus != null and newPoliticalStatus != ''">
+                political_status = #{newPoliticalStatus},
+            </if>
+            <if test="newEduNational != null and newEduNational != ''">
+                edu_national = #{newEduNational},
+            </if>
+            <if test="newEduReligious != null and newEduReligious != ''">
+                edu_religious = #{newEduReligious},
+            </if>
+            <if test="newJoinTime != null">
+                join_time = #{newJoinTime},
+            </if>
+            <if test="newPhone != null and newPhone != ''">
+                phone = #{newPhone},
+            </if>
+            <if test="newDomicile != null and newDomicile != ''">
+                domicile = #{newDomicile},
+            </if>
+            <if test="newNativePlace != null and newNativePlace != ''">
+                native_place = #{newNativePlace},
+            </if>
+            <if test="newOrgName != null and newOrgName != ''">
+                org_name = #{newOrgName},
+            </if>
+            <if test="newOrgAddress != null and newOrgAddress != ''">
+                org_address = #{newOrgAddress},
+            </if>
+        </set>
+        <where>
+            <if test="name != null and name != ''">
+                and name like concat('%', #{name}, '%')
+            </if>
+            <if test="idNumber != null and idNumber != ''">
+                and id_number = #{idNumber}
+            </if>
+            <if test="placeId != null">
+                and place_id = #{placeId}
+            </if>
+            <if test="religionType != null and religionType != ''">
+                and religion_type = #{religionType}
+            </if>
+            <if test="gender != null and gender != ''">
+                and gender = #{gender}
+            </if>
+            <if test="nation != null and nation != ''">
+                and nation = #{nation}
+            </if>
+            <if test="county != null and county != ''">
+                and county = #{county}
+            </if>
+            <if test="status != null and status != ''">
+                and status = #{status}
+            </if>
+        </where>
+    </update>
+
 </mapper>

BIN
ruoyi-admin/src/main/resources/static/img/login-background.jpg


BIN
ruoyi-admin/src/main/resources/static/img/login-background1.jpg


+ 4 - 4
ruoyi-admin/src/main/resources/templates/index.html

@@ -4,7 +4,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta name="renderer" content="webkit">
-    <title>若依系统首页</title>
+    <title>宗教管理信息系统</title>
     <!-- 避免IE使用兼容模式 -->
  	<meta http-equiv="X-UA-Compatible" content="IE=edge">
     <link th:href="@{favicon.ico}" rel="shortcut icon"/>
@@ -26,7 +26,7 @@
         </div>
         <a th:href="@{/index}">
             <li class="logo hidden-xs">
-                <span class="logo-lg">RuoYi</span>
+                <span class="logo-lg">宗教管理信息系统</span>
             </li>
          </a>
         <div class="sidebar-collapse">
@@ -196,7 +196,7 @@
                     </a>
                 </div>
                 <ul class="nav navbar-top-links navbar-right welcome-message">
-                    <li><a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="开发文档" href="http://doc.ruoyi.vip/ruoyi" target="_blank"><i class="fa fa-question-circle"></i> 文档</a></li>
+                    <!--<li><a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="开发文档" href="http://doc.ruoyi.vip/ruoyi" target="_blank"><i class="fa fa-question-circle"></i> 文档</a></li>-->
                     <li><a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="锁定屏幕" href="javascript:;" id="lockScreen"><i class="fa fa-lock"></i> 锁屏</a></li>
 	                <li><a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="全屏显示" href="javascript:;" id="fullScreen"><i class="fa fa-arrows-alt"></i> 全屏</a></li>
                     <li class="dropdown user-menu">
@@ -254,7 +254,7 @@
         </div>
 
         <div th:if="${footer}" class="footer">
-            <div class="pull-right">© [[${copyrightYear}]] RuoYi Copyright </div>
+            <div class="pull-right">© [[${copyrightYear}]]  Copyright </div>
         </div>
     </div>
     <!--右侧部分结束-->

+ 10 - 10
ruoyi-admin/src/main/resources/templates/login.html

@@ -3,7 +3,7 @@
 <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
-    <title>登录若依系统</title>
+    <title>登录宗教管理信息系统</title>
     <meta name="description" content="若依后台管理框架">
     <link href="../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/>
     <link href="../static/css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet"/>
@@ -26,16 +26,16 @@
             <div class="col-sm-7">
                 <div class="signin-info">
                     <div class="logopanel m-b">
-                        <h1><img alt="[ 若依 ]" src="../static/ruoyi.png" th:src="@{/ruoyi.png}"></h1>
+                        <!--<h1><img alt="[ 若依 ]" src="../static/ruoyi.png" th:src="@{/ruoyi.png}"></h1>-->
                     </div>
                     <div class="m-b"></div>
-                    <h4>欢迎使用 <strong>若依 后台管理系统</strong></h4>
+                    <h2>欢迎使用 <strong>宗教管理信息系统</strong></h2>
                     <ul class="m-b">
-                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> SpringBoot</li>
-                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> Mybatis</li>
-                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> Shiro</li>
-                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> Thymeleaf</li>
-                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> Bootstrap</li>
+                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> 宗教信息录入</li>
+                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> 宗教信息导入</li>
+                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> 宗教信息校核 </li>
+                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> 宗教资料管理</li>
+                        <li><i class="fa fa-arrow-circle-o-right m-r-xs"></i> 全流程日志</li>
                     </ul>
                     <strong th:if="${isAllowRegister}">还没有账号? <a th:href="@{/register}">立即注册&raquo;</a></strong>
                 </div>
@@ -43,7 +43,7 @@
             <div class="col-sm-5">
                 <form id="signupForm" autocomplete="off">
                     <h4 class="no-margins">登录:</h4>
-                    <p class="m-t-md">你若不离不弃,我必生死相依</p>
+                    <p class="m-t-md">欢迎使用宗教管理信息系统</p>
                     <input type="text"     name="username" class="form-control uname"     placeholder="用户名" value="admin"    />
                     <input type="password" name="password" class="form-control pword"     placeholder="密码"   value="admin123" />
 					<div class="row m-t" th:if="${captchaEnabled==true}">
@@ -65,7 +65,7 @@
         </div>
         <div class="signup-footer">
             <div class="pull-left">
-                Copyright © 2018-2025 ruoyi.vip All Rights Reserved. <br>
+                Copyright © 2018-2025  All Rights Reserved. <br>
             </div>
         </div>
     </div>

+ 3 - 1804
ruoyi-admin/src/main/resources/templates/main.html

@@ -4,7 +4,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <!--360浏览器优先以webkit内核解析-->
-    <title>若依介绍</title>
+    <title>宗教管理信息系统</title>
     <link rel="shortcut icon" href="favicon.ico">
     <link href="../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/>
     <link href="../static/css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet"/>
@@ -12,1811 +12,10 @@
 </head>
 
 <body class="gray-bg">
-    <div class="row border-bottom white-bg dashboard-header">
-        <div class="col-sm-3">
-            <h2>Hello,Guest</h2>
-            <small>移动设备访问请扫描以下二维码:</small>
-            <br>
-            <br>
-            <img th:src="@{/img/qr_code.png}" width="100%" style="max-width:264px;">
-            <br>
-        </div>
-        <div class="col-sm-5">
-            <h2>若依后台管理框架</h2>
-            <p>一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统。,她可以用于所有的Web应用程序,如<b>网站管理后台</b>,<b>网站会员中心</b>,<b>CMS</b>,<b>CRM</b>,<b>OA</b>等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。</p>
-            <p>
-                <b>当前版本:</b><span>v[[${version}]]</span>
-            </p>
-            <p>
-                <span class="label label-warning">&yen;免费开源</span>
-            </p>
-            <br>
-            <p>
-                <a class="btn btn-success btn-outline" href="https://gitee.com/y_project/RuoYi" target="_blank">
-                    <i class="fa fa-cloud"> </i> 访问码云
-                </a>
-                <a class="btn btn-white btn-bitbucket" href="http://ruoyi.vip" target="_blank">
-                    <i class="fa fa-home"></i> 访问主页
-                </a>
-            </p>
-        </div>
-        <div class="col-sm-4">
-            <h4>技术选型:</h4>
-            <ol>
-                <li>核心框架:Spring Boot。</li>
-                <li>安全框架:Apache Shiro。</li>
-                <li>模板引擎:Thymeleaf。</li>
-                <li>持久层框架:MyBatis。</li>
-                <li>定时任务:Quartz。</li>
-                <li>数据库连接池:Druid。</li>
-                <li>工具类:Fastjson。</li>
-                <li>更多……</li>
-            </ol>
-        </div>
-
-    </div>
-    <div class="wrapper wrapper-content">
-        <div class="row">
-            <div class="col-sm-4">
-
-                <div class="ibox float-e-margins">
-                    <div class="ibox-title">
-                        <h5>联系信息</h5>
-
-                    </div>
-                    <div class="ibox-content">
-                        <p><i class="fa fa-send-o"></i> 官网:<a href="http://www.ruoyi.vip" target="_blank">http://www.ruoyi.vip</a>
-                        </p>
-                        <p><i class="fa fa-qq"></i> QQ群:<s>满1389287</s> <s>满1679294</s> <s>满1529866</s> <s>满1772718</s> <s>满1366522</s> <s>满1382251</s> <s>满1145125</s> <s>满86752435</s> <s>满134072510</s> <s>满210336300</s> <s>满339522636</s> <s>满130035985</s> <s>满143151071</s> <s>满158781320</s> <s>满201531282</s> <s>满101526938</s> <s>满264355400</s> <s>满298522656</s> <s>满139845794</s>  <s>满185760789</s> <s>满175104288</s> <s>满174942938</s> <s>满287843737</s> <s>满232896766</s> <s>满180208928</s> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=WqsGDxpGkqOPeWGOf3I32f_rXxdhqYNr&authKey=kvdF5df7PO9bzWxmixKhZN6ShsECBiuGUmmzTZBWVr2MVOfJ8%2F4oD0Gws0rbgYfz&noverify=0&group_code=140284548" target="_blank">140284548</a>
-                        </p>
-                        <p><i class="fa fa-weixin"></i> 微信:<a href="javascript:;">/ *若依</a>
-                        </p>
-                        <p><i class="fa fa-credit-card"></i> 支付宝:<a href="javascript:;" class="支付宝信息">/ *若依</a>
-                        </p>
-                    </div>
-                </div>
-            </div>
-            <div class="col-sm-4">
-                <div class="ibox float-e-margins">
-                    <div class="ibox-title">
-                        <h5>更新日志</h5>
-                    </div>
-                    <div class="ibox-content no-padding">
-                        <div class="panel-body">
-                            <div class="panel-group" id="version">
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v481">v4.8.1</a><code class="pull-right">2025.05.20</code>
-								   </h5>
-								</div>
-								<div id="v481" class="panel-collapse collapse in">
-									<div class="panel-body">
-									   <ol>
-									        <li>新增CSRF防护功能</li>
-									        <li>Excel导入导出支持多图片</li>
-									        <li>菜单管理支持批量保存排序</li>
-									        <li>用户分配角色页禁用不允许分配</li>
-									        <li>优化Tab页签跟随主题样式效果</li>
-									        <li>优化浅色主题下菜单右边框同步主题色</li>
-									        <li>优化空指针异常时日志无法记录错误信息问题</li>
-									        <li>新增表格参数(自定义radio/checkbox的name值)</li>
-									        <li>升级oshi到最新版本6.8.1</li>
-									        <li>升级tomcat到最新版本9.0.105</li>
-									        <li>升级commons.io到最新版本2.19.0</li>
-									        <li>升级bootstrap-table到最新版本1.24.1</li>
-									        <li>升级bootstrap-fileinput到最新版本5.5.4</li>
-									        <li>用户更新方法移除login_name更新字段</li>
-									        <li>修复定时任务参数值带括号时异常问题</li>
-									        <li>进入新增页面前方法校验数据权限</li>
-									        <li>进入授权角色&重置密码页校验数据权限</li>
-									        <li>优化Excel匹配数值型.0结尾</li>
-									        <li>优化HttpUtils加入请求类型传参</li>
-									        <li>优化管理员登录不设置权限permissions属性</li>
-									        <li>优化successCallback方法对于非特定表格类型无响应问题</li>
-									        <li>优化导出Excel日期格式双击离开后与设定的格式不一致问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v480">v4.8.0</a><code class="pull-right">2024.12.26</code>
-								   </h5>
-								</div>
-								<div id="v480" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>支持自定义显示Excel属性列</li>
-									        <li>表格默认转义HTML字符串</li>
-									        <li>新增列宽拖动长内容自适应显示示例</li>
-									        <li>Excel注解支持wrapText是否允许内容换行</li>
-									        <li>代码生成新增配置是否允许文件覆盖到本地</li>
-									        <li>升级oshi到最新版本6.6.5</li>
-									        <li>升级tomcat到最新版本9.0.96</li>
-									        <li>升级logback到最新版本1.2.13</li>
-									        <li>升级commons.io到最新版本2.16.1</li>
-									        <li>升级spring-framework到最新版本5.3.39</li>
-									        <li>升级jquery.validate到最新版本v1.21.0</li>
-									        <li>优化导入带标题文件关闭清理</li>
-									        <li>代码生成创建表屏蔽违规的字符</li>
-									        <li>修复主子表数据显示问题</li>
-									        <li>修复记住我请求头过大的问题</li>
-									        <li>修复角色禁用权限不失效问题</li>
-									        <li>修复类匿名注解访问失效问题</li>
-									        <li>修复导出子列表对象只能在最后的问题</li>
-									        <li>修复多选下拉框open导致页签空白问题</li>
-									        <li>优化身份证脱敏正则</li>
-									        <li>优化查询时间范围日期格式</li>
-									        <li>优化异步树表格折叠同步子状态</li>
-									        <li>优化时间控件清除按钮样式问题</li>
-									        <li>优化表格图片预览动态路径显示问题</li>
-									        <li>优化select2下拉框必填背景色无法清空问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v479">v4.7.9</a><code class="pull-right">2024.06.06</code>
-								   </h5>
-								</div>
-								<div id="v479" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>通知公告新增详细显示</li>
-									        <li>新增数据脱敏过滤注解</li>
-									        <li>新增表格示例(虚拟滚动)</li>
-									        <li>新增表格示例(全文检索)</li>
-									        <li>新增表格示例(保存状态)</li>
-									        <li>代码生成支持表单布局选项</li>
-									        <li>限制用户操作数据权限范围</li>
-									        <li>用户密码新增非法字符验证</li>
-									        <li>默认加载layer扩展皮肤</li>
-									        <li>未修改初始密码弹框提醒</li>
-									        <li>定时任务白名单配置范围缩小</li>
-									        <li>操作日志列表重置回第一页</li>
-									        <li>定时任务日志默认按时间排序</li>
-									        <li>Excel注解ColumnType类型新增文本</li>
-									        <li>Excel注解新增属性comboReadDict</li>
-									        <li>新增Anonymous匿名访问不鉴权注解</li>
-									        <li>升级oshi到最新版本6.6.1</li>
-									        <li>升级druid到最新版本1.2.23</li>
-									        <li>升级commons.io到最新版本2.13.0</li>
-									        <li>升级spring-framework到安全版本</li>
-									        <li>升级bootstrap-table到最新版本1.22.6</li>
-									        <li>修复重置日期时出现的异常问题</li>
-									        <li>修复页签关闭后存在的跳转问题</li>
-									        <li>修复tooltip单击复制文本不生效的问题</li>
-									        <li>更新缓存管理键名排序方式</li>
-									        <li>更新HttpUtils中的User-Agent</li>
-									        <li>优化自定义XSS注解匹配方式</li>
-									        <li>优化登录注册页面验证码验证</li>
-									        <li>优化数据权限自定义匹配方式</li>
-									        <li>优化高频率定时任务不执行问题</li>
-									        <li>优化树表格align属性在标题生效</li>
-									        <li>优化代码生成主子表关联查询方式</li>
-									        <li>优化导入Excel时设置dictType属性重复查缓存问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v478">v4.7.8</a><code class="pull-right">2023.11.23</code>
-								   </h5>
-								</div>
-								<div id="v478" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>用户列表新增抽屉效果详细信息</li>
-									        <li>操作日志列表新增IP地址查询</li>
-									        <li>定时任务新增页去除状态选项</li>
-									        <li>系统管理角色列表显示数据权限</li>
-									        <li>通用排序属性orderBy参数限制长度</li>
-									        <li>新增isScrollToTop页签切换滚动到顶部</li>
-									        <li>Excel自定义数据处理器增加单元格/工作簿对象</li>
-									        <li>新增表格参数(数据值为空时显示的内容undefinedText)</li>
-									        <li>升级oshi到最新版本6.4.7</li>
-									        <li>升级shiro到最新版本1.13.0</li>
-									        <li>升级druid到最新版本1.2.20</li>
-									        <li>升级pagehelper到最新版1.4.7</li>
-									        <li>升级spring-boot到最新版本2.5.15</li>
-									        <li>升级jquery到最新版v3.7.1</li>
-									        <li>升级layer到最新版本v3.7.0</li>
-									        <li>升级layui到最新版本v2.8.18</li>
-									        <li>升级x-editable到最新版本1.5.3</li>
-									        <li>修复自定义字典样式不生效的问题</li>
-									        <li>修复弹窗按钮启用禁用方法无效问题</li>
-									        <li>修复横向菜单关闭最后一个页签状态问题</li>
-									        <li>修复Excel导入数据临时文件无法删除问题</li>
-									        <li>修复表格行内编辑启用翻页记住选择无效问题</li>
-									        <li>修复Excels导入时无法获取到dictType字典值问题</li>
-									        <li>优化重置密码鼠标按下显示密码</li>
-									        <li>优化参数键值文本框改为文本域</li>
-									        <li>优化表格重置默认返回到第一页</li>
-									        <li>优化菜单管理类型为按钮状态可选</li>
-									        <li>优化数字金额大写转换精度丢失问题</li>
-									        <li>优化树表查询无数据时清除分页信息</li>
-									        <li>优化通用detail详细信息弹窗不显示按钮</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v477">v4.7.7</a><code class="pull-right">2023.04.14</code>
-								   </h5>
-								</div>
-								<div id="v477" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>操作日志新增消耗时间属性</li>
-									        <li>日志管理使用索引提升查询性能</li>
-									        <li>日志注解支持排除指定的请求参数</li>
-									        <li>新增监控页面图标显示</li>
-									        <li>新增支持登录IP黑名单限制</li>
-									        <li>更新fontawesome图标示例</li>
-									        <li>屏蔽定时任务bean违规的字符</li>
-									        <li>支持自定义隐藏属性列过滤子对象</li>
-									        <li>连接池Druid支持新的配置connectTimeout和socketTimeout</li>
-									        <li>升级jquery到最新版v3.6.3</li>
-									        <li>升级layui到最新版本2.7.6</li>
-									        <li>升级jasny-bootstrap到最新版4.0.0</li>
-									        <li>升级oshi到最新版本6.4.1</li>
-									        <li>升级druid到最新版本1.2.16</li>
-									        <li>修复异步表格树子项排序问题</li>
-									        <li>修复冻结列不支持IE浏览器的问题</li>
-									        <li>修复主子表使用suggest插件无法新增问题</li>
-									        <li>修复菜单栏快速点击导致展开折叠样式问题</li>
-									        <li>修复用户多角色数据权限可能出现权限抬升的情况</li>
-									        <li>修复异步加载表格树重置列表父节点展开异常问题</li>
-									        <li>修复页签属性refresh为undefined时页面被刷新问题</li>
-									        <li>移除apache/commons-fileupload依赖</li>
-									        <li>优化前端属性提醒说明</li>
-									        <li>优化用户导入更新时需获取用户编号问题</li>
-									        <li>优化主子表根据序号删除方法加入表格ID参数</li>
-									        <li>优化导出Excel时设置dictType属性重复查缓存问题</li>
-									        <li>优化在线用户服务缓存改为从Bean容器获取不使用自动装配</li>
-									        <li>优化表格示例行拖拽后列表底部总记录条数变成了undefined问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v476">v4.7.6</a><code class="pull-right">2022.12.16</code>
-								   </h5>
-								</div>
-								<div id="v476" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>定时任务违规的字符</li>
-									        <li>忽略不必要的属性数据返回</li>
-									        <li>导入更新用户数据前校验数据权限</li>
-									        <li>修改参数键名时移除前缓存配置</li>
-									        <li>修改用户登录账号进行重复验证</li>
-									        <li>兼容Excel下拉框内容过多无法显示</li>
-									        <li>升级oshi到最新版本6.4.0</li>
-									        <li>升级kaptcha到最新版2.3.3</li>
-									        <li>升级druid到最新版本1.2.15</li>
-									        <li>升级shiro到最新版本1.10.1</li>
-									        <li>升级pagehelper到最新版1.4.6</li>
-									        <li>升级bootstrap-fileinput到最新版本5.5.2</li>
-									        <li>修复sheet超出最大行数异常问题</li>
-									        <li>修复关闭父页签后提交无法跳转的问题</li>
-									        <li>修复操作日志类型多选导出不生效问题</li>
-									        <li>修复导出包含空子列表数据异常的问题</li>
-									        <li>优化树形表格层级显示</li>
-									        <li>优化SQL关键字检查防止注入</li>
-									        <li>优化用户管理重置时取消部门选择</li>
-									        <li>优化代码生成同步后字典值NULL问题</li>
-									        <li>优化导出对象的子列表为空会出现[]问题</li>
-									        <li>优化select2搜索下拉后校验必填样式问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v475">v4.7.5</a><code class="pull-right">2022.09.05</code>
-								   </h5>
-								</div>
-								<div id="v475" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>Excel支持导出对象的子列表方法</li>
-									        <li>数据逻辑删除不进行唯一验证</li>
-									        <li>优化多角色数据权限匹配规则</li>
-									        <li>新增主子表提交校验示例</li>
-									        <li>支持自定义隐藏Excel属性列</li>
-									        <li>Excel注解支持backgroundColor属性设置背景颜色</li>
-									        <li>菜单配置刷新时Tab页签切换时刷新</li>
-									        <li>增加对AjaxResult消息结果类型的判断</li>
-									        <li>新增示例(进度条)</li>
-									        <li>新增内容编码/解码方便插件集成使用</li>
-									        <li>升级jquery到最新版3.6.1</li>
-									        <li>升级layui到最新版本2.7.5</li>
-									        <li>升级shiro到最新版本1.9.1</li>
-									        <li>升级druid到最新版本1.2.11</li>
-									        <li>升级pagehelper到最新版1.4.3</li>
-									        <li>升级oshi到最新版本6.2.2</li>
-									        <li>修复树表onLoadSuccess不生效的问题</li>
-											<li>修复用户分配角色大于默认页数丢失问题</li>
-									        <li>定时任务支持执行父类方法</li>
-									        <li>自动设置切换多个树表格实例配置</li>
-									        <li>页签创建标题优先data-title属性</li>
-									        <li>优化任务过期不执行调度</li>
-									        <li>优化横向菜单下激活菜单样式</li>
-									        <li>优化按钮打开窗口后按回车反复弹出</li>
-									        <li>优化excel/scale属性导出单元格数值类型</li>
-									        <li>优化druid开启wall过滤器出现的异常问题</li>
-									        <li>优化多个相同角色数据导致权限SQL重复问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v474">v4.7.4</a><code class="pull-right">2022.06.01</code>
-								   </h5>
-								</div>
-								<div id="v474" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>用户头像上传图片格式限制</li>
-									        <li>Excel注解支持color属性设置字体颜色</li>
-									        <li>设置分页参数默认值</li>
-									        <li>主子表操作列新增单个删除</li>
-									        <li>定时任务检查Bean包名是否为白名单配置</li>
-									        <li>升级spring-boot到最新版本2.5.14</li>
-									        <li>升级shiro到最新版本1.9.0</li>
-									        <li>升级oshi到最新版本6.1.6</li>
-									        <li>升级fastjson到最新版1.2.83 安全修复版本</li>
-									        <li>文件上传兼容Weblogic环境</li>
-									        <li>新增清理分页的线程变量方法</li>
-									        <li>新增获取不带后缀文件名称方法</li>
-									        <li>用户缓存信息添加部门ancestors祖级列表</li>
-									        <li>自定义ShiroFilterFactoryBean防止中文请求被拦截</li>
-									        <li>字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)</li>
-									        <li>优化IP地址获取到多个的问题</li>
-									        <li>优化表格冻结列阴影效果显示</li>
-									        <li>优化菜单侧边栏滚动条尺寸及颜色</li>
-									        <li>优化显示顺序orderNum类型为整型</li>
-									        <li>优化接口使用泛型使其看到响应属性字段</li>
-									        <li>优化导出数据LocalDateTime类型无数据问题</li>
-									        <li>修复导入Excel时字典字段类型为Long转义为空问题</li>
-									        <li>优化导出excel单元格验证,包含变更为开头.防止正常内容被替换</li>
-									        <li>修复URL类型回退键被禁止问题</li>
-									        <li>修复表格客户端分页序号显示错误问题</li>
-									        <li>修复代码生成拖拽多次出现的排序不正确问题</li>
-									        <li>修复表格打印组件不识别多层对象属性值问题</li>
-									        <li>修复操作日志查询类型条件为0时会查到所有数据</li>
-									        <li>修复Excel注解prompt/combo同时使用不生效问题</li>
-									        <li>修复初始化多表格处理回调函数时获取的表格配置不一致问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v473">v4.7.3</a><code class="pull-right">2022.03.01</code>
-								   </h5>
-								</div>
-								<div id="v473" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>表格树支持分页/异步加载</li>
-									        <li>代码生成预览支持复制内容</li>
-									        <li>定时任务默认保存到内存中执行</li>
-									        <li>代码生成同步保留必填/类型选项</li>
-									        <li>页面若未匹配到字典标签则返回原字典值</li>
-									        <li>用户访问控制时校验数据权限,防止越权</li>
-									        <li>导出Excel时屏蔽公式,防止CSV注入风险</li>
-									        <li>升级spring-boot到最新版本2.5.10</li>
-									        <li>升级spring-boot-mybatis到最新版2.2.2</li>
-									        <li>升级pagehelper到最新版1.4.1</li>
-									        <li>升级oshi到最新版本6.1.2</li>
-									        <li>升级bootstrap-table到最新版本1.19.1</li>
-									        <li>服务监控新增运行参数信息显示</li>
-									        <li>定时任务目标字符串验证包名白名单</li>
-									        <li>文件上传接口新增原/新文件名返回参数</li>
-									        <li>定时任务屏蔽违规的字符</li>
-									        <li>分页数据新增分页参数合理化参数</li>
-									        <li>表格父子视图添加点击事件打开示例</li>
-									        <li>优化上传文件名称命名规则</li>
-									        <li>优化加载字典缓存数据</li>
-									        <li>优化任务队列满时任务拒绝策略</li>
-									        <li>优化IE11上传预览不显示的问题</li>
-									        <li>优化Excel格式化不同类型的日期对象</li>
-									        <li>优化国际化配置多余的zh请求问题</li>
-									        <li>优化新版Chrome浏览器回退出现的遮罩层</li>
-									        <li>修复EMAIL类型回退键被禁止问题</li>
-									        <li>修复Xss注解字段值为空时的异常问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v472">v4.7.2</a><code class="pull-right">2021.12.23</code>
-								   </h5>
-								</div>
-								<div id="v472" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>自定义xss校验注解实现</li>
-									        <li>进入修改页面方法添加权限标识</li>
-									        <li>代码生成创建按钮添加超级管理员权限</li>
-									        <li>代码生成创建表检查关键字,防止注入风险</li>
-									        <li>修复定时任务多参数逗号分隔的问题</li>
-									        <li>修复表格插件一起使用出现的声明报错问题</li>
-									        <li>修复代码生成主子表模板删除方法缺少事务</li>
-									        <li>升级oshi到最新版本v5.8.6</li>
-									        <li>升级velocity到最新版本2.3</li>
-									        <li>升级fastjson到最新版1.2.79</li>
-									        <li>升级log4j2到最新版2.17.0 防止漏洞风险</li>
-									        <li>升级thymeleaf到最新版3.0.14 阻止远程代码执行漏洞</li>
-									        <li>优化修改/授权角色实时生效</li>
-									        <li>修整tomcat配置参数已过期问题</li>
-									        <li>前端添加单独的二代身份证校验</li>
-									        <li>优化新增部门时验证用户所属部门</li>
-									        <li>优化查询用户的角色组&岗位组代码</li>
-									        <li>请求分页方法设置成通用方便灵活调用</li>
-									        <li>优化日期类型错误提示与图标重叠问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v471">v4.7.1</a><code class="pull-right">2021.11.10</code>
-								   </h5>
-								</div>
-								<div id="v471" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>新增是否开启页签功能</li>
-									        <li>代码生成的模块增加创建表功能</li>
-									        <li>Excel导入支持@Excels注解</li>
-									        <li>Excel注解支持导入导出标题信息</li>
-									        <li>Excel注解支持自定义数据处理器</li>
-									        <li>日志注解新增是否保存响应参数</li>
-									        <li>防重提交注解支持配置间隔时间/提示消息</li>
-									        <li>网页部分操作禁止使用后退键(Backspace)</li>
-									        <li>实例演示中增加多层窗口获取值</li>
-									        <li>弹出层openOptions增加动画属性</li>
-									        <li>升级spring-boot到最新版本2.5.6</li>
-									        <li>升级spring-boot-mybatis到最新版2.2.0</li>
-									        <li>升级pagehelper到最新版1.4.0</li>
-									        <li>升级oshi到最新版本v5.8.2</li>
-									        <li>升级druid到最新版1.2.8</li>
-									        <li>升级fastjson到最新版1.2.78</li>
-									        <li>升级thymeleaf-extras-shiro到最新版本v2.1.0</li>
-									        <li>升级bootstrap-fileinput到最新版本v5.2.4</li>
-									        <li>修改阿里云maven仓库地址为新版地址</li>
-									        <li>定时任务屏蔽违规字符</li>
-									        <li>增加sendGet无参请求方法</li>
-									        <li>代码生成去掉多余的排序字段</li>
-									        <li>优化启动脚本参数优化</li>
-									        <li>优化页签关闭右侧清除iframe元素</li>
-									        <li>优化多表格切换表单查询参数</li>
-									        <li>优化表格实例切换event不能为空</li>
-									        <li>优化mybatis全局默认的执行器</li>
-									        <li>优化导入Excel数据关闭时清理file</li>
-									        <li>优化Excel导入图片可能出现的异常</li>
-									        <li>优化记录登录信息,防止不必要的修改</li>
-									        <li>优化aop语法,使用spring自动注入注解</li>
-									        <li>修复无法被反转义问题</li>
-									        <li>修复拖拽行数据错位问题</li>
-									        <li>修复新窗口打开页面关闭弹窗报错</li>
-									        <li>修复富文本回退键被禁止&控制台报错问题</li>
-									        <li>修复自定义弹出层全屏参数无效问题</li>
-									        <li>修复树表代码生成短字段无法识别问题</li>
-									        <li>修复apple/webkit浏览器时间无法格式化</li>
-									        <li>修复后端主子表代码模板方法名生成错误问题</li>
-									        <li>修复swagger没有指定dataTypeClass导致启动出现warn日志</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v470">v4.7.0</a><code class="pull-right">2021.09.01</code>
-								   </h5>
-								</div>
-								<div id="v470" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>优化弹出层显示在顶层窗口</li>
-									        <li>定时任务支持在线生成cron表达式</li>
-									        <li>Excel注解支持Image图片导入</li>
-									        <li>支持配置是否开启记住我功能</li>
-									        <li>修改时检查用户数据权限范围</li>
-									        <li>表单重置开始/结束时间控件</li>
-									        <li>新增多图上传示例</li>
-									        <li>启用父部门状态排除顶级节点</li>
-									        <li>富文本默认dialogsInBody属性</li>
-									        <li>去除默认分页合理化参数</li>
-									        <li>顶部菜单跳转添加绝对路径</li>
-									        <li>升级oshi到最新版本v5.8.0</li>
-									        <li>升级shiro到最新版本v1.8.0</li>
-									        <li>升级commons.io到最新版本v2.11.0</li>
-									        <li>升级jquery到最新版v3.6.0</li>
-									        <li>升级icheck到最新版v1.0.3</li>
-									        <li>升级layer到最新版本v3.5.1</li>
-									        <li>升级layui到最新版本v2.6.8</li>
-									        <li>升级laydate到最新版本v5.3.1</li>
-									        <li>升级select2到最新版v4.0.13</li>
-									        <li>升级cropper到最新版本v1.5.12</li>
-									        <li>升级summernote到最新版本v0.8.18</li>
-									        <li>升级duallistbox到最新版本v3.0.9</li>
-									        <li>升级jquery.validate到最新版本v1.19.3</li>
-									        <li>升级bootstrap-suggest到最新版本v0.1.29</li>
-									        <li>升级bootstrap-select到最新版本v1.13.18</li>
-									        <li>升级bootstrap-fileinput到最新版本v5.2.3</li>
-									        <li>查询表格指定列值增加是否去重属性</li>
-									        <li>删除sourceMappingURL源映射</li>
-									        <li>去除多余的favicon.ico引入</li>
-									        <li>优化代码生成模板</li>
-									        <li>优化XSS跨站脚本过滤</li>
-									        <li>补充定时任务表字段注释</li>
-									        <li>定时任务屏蔽ldap远程调用</li>
-									        <li>定时任务屏蔽http(s)远程调用</li>
-									        <li>定时任务对检查异常进行事务回滚</li>
-									        <li>调度日志详细页添加关闭按钮</li>
-									        <li>优化异常打印输出信息</li>
-									        <li>优化移动端进入首页样式</li>
-									        <li>优化用户操作不能删除自己</li>
-									        <li>默认开始/结束时间绑定控件选择类型</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v462">v4.6.2</a><code class="pull-right">2021.07.01</code>
-								   </h5>
-								</div>
-								<div id="v462" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>优化参数&字典缓存操作</li>
-									        <li>新增表格参数(导出方式&导出文件类型)</li>
-									        <li>新增表格示例(自定义视图分页)</li>
-									        <li>新增示例(表格列拖拽)</li>
-									        <li>集成yuicompressor实现(CSS/JS压缩)</li>
-									        <li>新增表格参数(是否支持打印页面showPrint)</li>
-									        <li>支持bat脚本执行应用</li>
-									        <li>修复存在的SQL注入漏洞问题</li>
-									        <li>定时任务屏蔽rmi远程调用</li>
-									        <li>导出Excel文件支持数据流下载方式</li>
-									        <li>实例演示弹层组件增加相册层示例</li>
-									        <li>删除操作日志记录信息</li>
-									        <li>增加表格重置分页的参数</li>
-									        <li>限制超级管理员不允许操作</li>
-									        <li>树级结构更新子节点使用replaceFirst</li>
-									        <li>支持动态生成密匙,防止默认密钥泄露</li>
-									        <li>升级pagehelper到最新版1.3.1</li>
-									        <li>升级oshi到最新版本v5.7.4</li>
-									        <li>升级swagger到最新版本v3.0.0</li>
-									        <li>升级commons.io到最新版本v2.10.0</li>
-									        <li>升级commons.fileupload到最新版本v1.4</li>
-									        <li>升级bootstrap-table到最新版本v1.18.3</li>
-									        <li>升级druid到最新版本v1.2.6</li>
-									        <li>升级fastjson到最新版1.2.76</li>
-									        <li>升级layui到最新版本v2.6.6</li>
-									        <li>升级layer到最新版本v3.5.0</li>
-									        <li>升级laydate到最新版本v5.3.0</li>
-									        <li>优化表格树移动端&边框显示</li>
-									        <li>新增表格刷新options配置方法</li>
-									        <li>优化图片工具类读取文件,防止异常</li>
-									        <li>修复表格图片预览移动端宽高无效问题</li>
-									        <li>主子表通用操作封装处理增加文本域类型</li>
-									        <li>日志注解兼容获取json类型的参数</li>
-									        <li>修复表单向导插件有滚动条时底部工具栏无法固定问题</li>
-									        <li>修复导出角色数据范围翻译缺少仅本人</li>
-									        <li>修正Velocity模板初始字符集</li>
-									        <li>升级mybatis到最新版3.5.6 阻止远程代码执行漏洞</li>
-									        <li>优化代码生成导出模板名称</li>
-									        <li>修改个人中心密码长度提醒</li>
-									        <li>实例演示中弹出表格增加以回调形式回显到父窗体</li>
-									        <li>修复登录页面弹窗文字不显示的问题</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v461">v4.6.1</a><code class="pull-right">2021.04.12</code>
-								   </h5>
-								</div>
-								<div id="v461" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>新增IE浏览器版本过低提示页面</li>
-									        <li>新增详细信息tab页签方式</li>
-									        <li>新增解锁屏幕打开上次页签</li>
-									        <li>数据监控默认账户密码防止越权访问</li>
-									        <li>新增表格示例(导出选择列)</li>
-									        <li>个人信息添加手机&邮箱重复验证</li>
-									        <li>个人中心刷新后样式问题</li>
-									        <li>操作日志返回参数添加非空验证</li>
-									        <li>velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞</li>
-									        <li>子表模板默认日期格式化</li>
-									        <li>代码生成预览语言根据后缀名高亮显示</li>
-									        <li>代码生成主子表相同字段导致数据问题</li>
-									        <li>升级SpringBoot到最新版本2.2.13</li>
-									        <li>升级shiro到最新版1.7.1 阻止身份认证绕过漏洞</li>
-									        <li>升级bootstrapTable到最新版本v1.18.2</li>
-									        <li>升级bootstrapTable相关组件到最新版本v1.18.2</li>
-									        <li>升级fastjson到最新版1.2.75</li>
-									        <li>升级druid到最新版本v1.2.4</li>
-									        <li>升级oshi到最新版本v5.6.0</li>
-									        <li>修改ip字段长度防止ipv6地址长度不够</li>
-									        <li>搜索建议示例选择后隐藏列表</li>
-									        <li>主子表示例增加初始化数据</li>
-									        <li>优化Excel导入增加空行判断</li>
-									        <li>修复横向菜单无法打开页签问题</li>
-									        <li>修复导入数据为负浮点数时,导入结果会丢失精度问题</li>
-									        <li>优化更多操作按钮左侧移入内容闪现消失情况</li>
-									        <li>修复主子表提交中列隐藏后出现列偏移问题</li>
-									        <li>单据打印网页时通过hidden-print隐藏元素</li>
-									        <li>表格销毁清除记住选择数据</li>
-									        <li>增加表格动态列示例</li>
-									        <li>代码生成选择主子表关联元素必填</li>
-									        <li>tree根据Id和Name选中指定节点增加空判断</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v46">v4.6.0</a><code class="pull-right">2021.01.01</code>
-								   </h5>
-								</div>
-								<div id="v46" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>新增缓存监控管理</li>
-									        <li>新增锁定屏幕功能</li>
-									        <li>菜单新增是否刷新页面</li>
-									        <li>删除用户和角色解绑关联</li>
-									        <li>新增密码强度字符范围提示</li>
-									        <li>防止匿名访问进行过滤</li>
-									        <li>升级SpringBoot到最新版本2.2.12</li>
-									        <li>升级poi到最新版本4.1.2</li>
-									        <li>升级bitwalker到最新版本1.21</li>
-									        <li>升级bootstrap-fileinput到最新版本5.1.3</li>
-									        <li>升级bootstrapTable到最新版本v1.18.0</li>
-									        <li>升级bootstrapTable相关组件到最新版本v1.18.0</li>
-									        <li>升级oshi到最新版本v5.3.6</li>
-									        <li>新增示例(标签 & 提示)</li>
-									        <li>添加单据打印示例</li>
-									        <li>修改表格初始参数sortName默认值为undefined</li>
-									        <li>新增表格参数(自定义打印页面模板printPageBuilder)</li>
-									        <li>新增表格参数(是否显示行间隔色striped)</li>
-									        <li>新增表格参数(渲染完成后执行的事件onPostBody)</li>
-									        <li>Excel注解支持Image图片导出</li>
-									        <li>Excel支持注解align对齐方式</li>
-									        <li>Excel支持导入Boolean型数据</li>
-									        <li>主子表操作添加通用addColumn方法</li>
-									        <li>代码生成日期控件区分范围</li>
-									        <li>代码生成数据库文本类型生成表单文本域</li>
-									        <li>修复生成主子表外键名错误</li>
-									        <li>选项卡新增是否刷新属性</li>
-									        <li>修复树表格表头跟表格宽度不同步的问题</li>
-									        <li>表格树加载完成触发tooltip方法</li>
-									        <li>使用widthUnit定义树表格选项单位</li>
-									        <li>修复主子表editColumn序列问题</li>
-									        <li>修复添加全屏在无参数时没有替换url参数问题</li>
-									        <li>弹出层openOptions移动端自适应</li>
-									        <li>防止错误页返回主页出现嵌套问题</li>
-									        <li>设置回显数据字典验证防止空值</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v451">v4.5.1</a><code class="pull-right">2020.11.18</code>
-								   </h5>
-								</div>
-								<div id="v451" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>阻止任意文件下载漏洞</li>
-									        <li>升级shiro到最新版1.7.0 阻止权限绕过漏洞</li>
-									        <li>升级druid到最新版本v1.2.2</li>
-									        <li>新增表格行触发事件(onCheck、onUncheck、onCheckAll、onUncheckAll)</li>
-									        <li>修复多页签关闭非当前选项出现空白问题</li>
-									        <li>代码生成预览支持高亮显示</li>
-									        <li>mapperLocations配置支持分隔符</li>
-									        <li>权限信息调整</li>
-									        <li>个人中心头像和上传头像增加默认图片</li>
-									        <li>全局配置类保持和其他应用命名相同</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v45">v4.5.0</a><code class="pull-right">2020.10.20</code>
-								   </h5>
-								</div>
-								<div id="v45" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>新增菜单导航显示风格(default为左侧导航菜单,topnav为顶部导航菜单)</li>
-									        <li>菜单&数据权限新增(展开/折叠 全选/全不选 父子联动)</li>
-									        <li>账号密码支持自定义更新周期</li>
-									        <li>初始密码支持自定义修改策略</li>
-									        <li>新增校验用户修改新密码不能与旧密码相同</li>
-									        <li>添加检查密码范围支持的特殊字符包括:~!@#$%^&*()-=_+</li>
-									        <li>注册账号设置默认用户名称及密码最后更新时间</li>
-									        <li>去除用户手机邮箱部门必填验证</li>
-									        <li>新增日期格式化方法</li>
-									        <li>代码生成添加bit类型</li>
-									        <li>树结构加载添加callBack回调方法</li>
-									        <li>修复用户管理页面滚动返回顶部条失效</li>
-									        <li>修复代码生成模板文件上传组件缺少ctx的问题</li>
-									        <li>限制系统内置参数不允许删除</li>
-									        <li>新增表格列宽拖动插件</li>
-									        <li>新增Ajax局部刷新demo</li>
-									        <li>新增是否开启页脚功能</li>
-									        <li>新增表格参数(通过自定义函数设置标题样式headerStyle)</li>
-									        <li>新增表格参数(通过自定义函数设置页脚样式footerStyle)</li>
-									        <li>修复窗体大小改变后浮动提示框失效问题</li>
-									        <li>生成代码补充必填样式</li>
-									        <li>生成页面时不忽略remark属性</li>
-									        <li>字典数据列表页添加关闭按钮</li>
-									        <li>Excel注解支持自动统计数据总和</li>
-									        <li>升级springboot到2.1.17 提升安全性</li>
-									        <li>升级pagehelper到最新版1.3.0</li>
-									        <li>升级druid到最新版本v1.2.1</li>
-									        <li>升级fastjson到最新版1.2.74</li>
-									        <li>升级bootstrap-fileinput到最新版本5.1.2</li>
-									        <li>升级oshi到最新版本v5.2.5</li>
-									        <li>表单向导插件更换为jquery-smartwizard</li>
-									        <li>修改主子表提交示例代码防止渲染失效</li>
-									        <li>添加导入数据弹出窗体自定义宽高</li>
-									        <li>用户信息参数返回忽略掉密码字段</li>
-									        <li>优化关闭窗体添加index参数</li>
-									        <li>回显数据字典(字符串数组)增加空值判断</li>
-									        <li>修改前端密码长度校验和错误提示不符问题</li>
-									        <li>AjaxResult重写put方法,以方便链式调用</li>
-									        <li>增强验证码校验的语义,更易懂</li>
-									        <li>导入excel整形值校验优化</li>
-									        <li>Excel导出类型NUMERIC支持精度浮点类型</li>
-									        <li>导出Excel调整targetAttr获取值方法,防止get方法不规范</li>
-									        <li>输入框组验证错误后置图标提示颜色</li>
-									        <li>上传媒体类型添加视频格式</li>
-									        <li>数据权限判断参数类型</li>
-									        <li>修正数据库字符串类型nvarchar</li>
-									        <li>优化递归子节点</li>
-									        <li>修复多表格搜索formId无效</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v44">v4.4.0</a><code class="pull-right">2020.08.24</code>
-								   </h5>
-								</div>
-								<div id="v44" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>升级bootstrapTable到最新版本1.17.1</li>
-									        <li>升级shiro到最新版1.6.0 阻止权限绕过漏洞</li>
-									        <li>升级fastjson到最新版1.2.73</li>
-									        <li>代码生成支持同步数据库</li>
-									        <li>代码生成支持富文本控件</li>
-									        <li>用户密码支持自定义配置规则</li>
-									        <li>新增表格自动刷新插件</li>
-									        <li>新增表格打印配置插件</li>
-									        <li>更换图片裁剪工具为cropper</li>
-									        <li>Excel支持sort导出排序</li>
-									        <li>代码生成支持自定义路径</li>
-									        <li>代码生成支持选择上级菜单</li>
-									        <li>代码生成支持上传控件</li>
-									        <li>新增表格参数(自定义加载文本的字体大小loadingFontSize)</li>
-									        <li>Excel注解支持设置BigDecimal精度&舍入规则</li>
-									        <li>操作日志记录排除敏感属性字段</li>
-									        <li>修复不同浏览器附件下载中文名乱码的问题</li>
-									        <li>用户分配角色不允许选择超级管理员角色</li>
-									        <li>更换表格冻结列插件</li>
-									        <li>添加右侧冻结列示例</li>
-									        <li>升级表格行编辑&移动端适应插件</li>
-									        <li>修复更新表格插件后无法设置实例配置问题</li>
-									        <li>修复更新表格插件后导致的主子表错误</li>
-									        <li>修复页面存在多表格,回调函数res数据不正确问题</li>
-									        <li>强退&过期清理登录账号缓存会话</li>
-									        <li>表格树标题内容支持html语义化标签</li>
-									        <li>修复配置应用的访问路径首页页签重复问题</li>
-									        <li>优化openTab打开时滚动到当前页签</li>
-									        <li>表格请求方式method支持自定义配置</li>
-									        <li>菜单页签联动优化</li>
-									        <li>用户邮箱长度限制修改为50</li>
-									        <li>主子表示例添加日期格式案例</li>
-									        <li>修改表格行内编辑示例旧值参数</li>
-									        <li>操作日志查询方式调整</li>
-									        <li>唯一限制条件只返回单条数据</li>
-									        <li>修改Excel设置STRING单元格类型</li>
-									        <li>添加获取当前的环境配置方法</li>
-									        <li>截取返回参数长度,防止超出异常</li>
-									        <li>定时任务cron表达式验证</li>
-									        <li>拆分表格插件,按需引入</li>
-									        <li>多行文本框补齐必填错误提示背景</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v431">v4.3.1</a><code class="pull-right">2020.07.05</code>
-								   </h5>
-								</div>
-								<div id="v431" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>国家信息安全漏洞(请务必保持cipherKey密钥唯一性)</li>
-									        <li>升级shiro到最新版1.5.3 阻止权限绕过漏洞</li>
-									        <li>修改验证码在使用后清除,防止多次使用</li>
-									        <li>检查字符支持小数点&降级改成异常提醒</li>
-									        <li>openOptions函数中加入自定义maxmin属性</li>
-									        <li>支持openOptions方法最大化</li>
-									        <li>支持openOptions方法多个按钮回调</li>
-									        <li>新增isLinkage支持页签与菜单联动</li>
-									        <li>修改代码生成导入表结构出现异常页面不提醒问题</li>
-									        <li>优化用户头像发生错误,则显示一个默认头像</li>
-									        <li>Excel导出支持字典类型</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v43">v4.3.0</a><code class="pull-right">2020.06.22</code>
-								   </h5>
-								</div>
-								<div id="v43" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>代码生成模板支持主子表</li>
-									        <li>代码生成显示类型支持复选框</li>
-									        <li>前端表单样式修改成圆角</li>
-									        <li>新增回显数据字典(字符串数组)</li>
-									        <li>修复浏览器手动缩放比例后菜单无法自适应问题</li>
-									        <li>限制用户不允许选择系统管理员角色</li>
-									        <li>用户信息添加输入框组图标&鼠标按下显示密码</li>
-									        <li>升级fastjson到最新版1.2.70 修复高危安全漏洞</li>
-									        <li>升级Bootstrap版本到v3.3.7</li>
-									        <li>修复selectColumns方法获取子对象数据无效问题</li>
-									        <li>修改数据源类型优先级,先根据方法,再根据类</li>
-									        <li>修改上级部门(选择项排除本身和下级)</li>
-									        <li>首页菜单显示调整</li>
-									        <li>添加是否开启swagger配置</li>
-									        <li>新增示例(主子表提交)</li>
-									        <li>新增示例(多级联动下拉示例)</li>
-									        <li>新增示例(表格属性data数据加载)</li>
-									        <li>新增表格列参数(是否列选项可见ignore)</li>
-									        <li>新增表格参数(是否启用显示卡片视图cardView)</li>
-									        <li>新增表格参数(是否显示全屏按钮showFullscreen)</li>
-									        <li>新增表格参数(是否启用分页条无限循环的功能paginationLoop)</li>
-									        <li>新增表格参数(是否显示表头showHeader)</li>
-									        <li>表格添加显示/隐藏所有列方法 showAllColumns/hideAllColumns</li>
-									        <li>修复部分情况节点不展开问题</li>
-									        <li>修复关闭标签页后刷新还是上次地址问题</li>
-									        <li>修复选择菜单后刷新页面,菜单箭头显示不对问题</li>
-											<li>修复jquery表单序列化时复选框未选中不会序列化到对象中问题</li>
-											<li>Excel支持readConverterExp读取字符串组内容</li>
-									        <li>更换IP地址查询接口</li>
-									        <li>默认关闭获取ip地址</li>
-									        <li>操作处理ajaxSuccess判断修正</li>
-									        <li>HttpUtils.sendPost()方法,参数无需拼接参数到url</li>
-									        <li>通用http发送方法增加参数 contentType 编码类型</li>
-									        <li>HTML过滤器不替换&实体</li>
-									        <li>代码生成浮点型改用BigDecimal</li>
-									        <li>修复表单构建单选和多选框渲染问题</li>
-									        <li>代码生成模板调整,字段为String并且必填则加空串条件</li>
-									        <li>字典数据查询列表根据dictSort升序排序</li>
-									        <li>修复树表对imageView和tooltip方法无效问题</li>
-									        <li>修复Long类型比较相等问题调整</li>
-									        <li>示例demo页面清除html链接,防止点击后跳转出现404</li>
-									        <li>在线用户强退方法合并</li>
-									        <li>添加校验部门包含未停用的子部门</li>
-									        <li>取消回车自动提交表单</li>
-									        <li>'A','I','BUTTON' 标签忽略clickToSelect事件,防止点击操作按钮时选中</li>
-									        <li>邮箱显示截取部分字符串,防止低分辨率错位</li>
-									        <li>代码生成列属性根据sort排序</li>
-									        <li>修复更多操作部分浏览器不兼容情况</li>
-									        <li>图片预览事件属性修正</li>
-									        <li>修复冻结列排序样式无效问题</li>
-									        <li>修复context-path的情况下个人中心刷新导致样式问题</li>
-									        <li>全屏editFull打开适配表树</li>
-									        <li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v42">v4.2.0</a><code class="pull-right">2020.03.23</code>
-								   </h5>
-								</div>
-								<div id="v42" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>用户管理添加分配角色页面</li>
-									        <li>定时任务添加调度日志按钮</li>
-									        <li>新增是否开启用户注册功能</li>
-											<li>新增页面滚动显示返回顶部按钮</li>
-											<li>用户&角色&任务添加更多操作按钮</li>
-											<li>iframe框架页会话过期弹出超时提示</li>
-											<li>移动端登录不显示左侧菜单</li>
-											<li>侧边栏添加一套深蓝色主题</li>
-											<li>首页logo固定,不随菜单滚动</li>
-											<li>支持mode配置history(表示去掉地址栏的#)</li>
-											<li>任务分组字典翻译(调度日志详细)</li>
-											<li>字典管理添加缓存读取</li>
-											<li>字典数据列表标签显示样式</li>
-											<li>参数管理支持缓存操作</li>
-											<li>日期控件清空结束时间设置开始默认值为2099-12-31</li>
-											<li>表格树添加获取数据后响应回调处理</li>
-											<li>批量替换表前缀调整</li>
-											<li>支持表格导入模板的弹窗表单加入其它输入控件</li>
-											<li>表单重置刷新表格树</li>
-											<li>新增支持导出数据字段排序</li>
-											<li>新增表格参数(是否单选checkbox)</li>
-											<li>druid未授权不允许访问</li>
-											<li>表格树父节点兼容0,'0','',null</li>
-											<li>表单必填的项添加星号</li>
-											<li>修复select2不显示校验错误信息</li>
-											<li>添加自定义HTML过滤器</li>
-											<li>修复多数据源下开关关闭出现异常问题</li>
-											<li>修复翻页记住选择项数据问题</li>
-											<li>用户邮箱长度限制20</li>
-											<li>修改错误页面返回主页出现嵌套问题</li>
-											<li>表格浮动提示单双引号转义</li>
-											<li>支持配置四级菜单</li>
-											<li>升级shiro到最新版1.4.2 阻止rememberMe漏洞攻击</li>
-											<li>升级summernote到最新版本v0.8.12</li>
-											<li>导入Excel根据dateFormat属性格式处理</li>
-											<li>修复War部署无法正常shutdown,ehcache内存泄漏</li>
-											<li>修复代码生成短字段无法识别问题</li>
-											<li>修复serviceImpl模版,修改方法判断日期错误</li>
-											<li>代码生成模板增加导出功能日志记录</li>
-											<li>代码生成唯一编号调整为tableId</li>
-											<li>代码生成查询时忽略大小写</li>
-											<li>代码生成支持翻页记住选中</li>
-											<li>代码生成表注释未填写也允许导入</li>
-											<li>Global全局配置类修改为注解,防止多环境配置下读取问题</li>
-											<li>修复多表格情况下,firstLoad只对第一个表格生效</li>
-											<li>处理Maven打包出现警告问题</li>
-											<li>默认主题样式,防止网速慢情况下出现空白</li>
-											<li>修复文件上传多级目录识别问题</li>
-											<li>锚链接解码url,防止中文导致页面不能加载问题</li>
-											<li>修复右键Tab页刷新事件重复请求问题</li>
-											<li>角色禁用&菜单隐藏不查询权限</li>
-											<li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v41">v4.1.0</a><code class="pull-right">2019.10.22</code>
-								   </h5>
-								</div>
-								<div id="v41" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>支持多表格实例操作</li>
-									        <li>浮动提示方法tooltip支持弹窗</li>
-											<li>代码生成&字典数据支持模糊条件查询</li>
-											<li>增加页签全屏方法</li>
-											<li>增加清除表单验证错误信息方法</li>
-											<li>支持iframe局部刷新页面</li>
-											<li>支持在线切换主题</li>
-											<li>修改图片预览设置的高宽参数颠倒问题</li>
-											<li>操作日志新增解锁账户功能</li>
-											<li>管理员用户&角色不允许操作</li>
-											<li>去掉jsoup包调用自定义转义工具</li>
-											<li>添加时间轴示例</li>
-											<li>修复翻页记住选择时获取指定列值的问题</li>
-											<li>代码生成sql脚本添加导出按钮</li>
-											<li>添加表格父子视图示例</li>
-											<li>添加表格行内编辑示例</li>
-											<li>升级fastjson到最新版1.2.60 阻止漏洞攻击</li>
-											<li>升级echarts到最新版4.2.1</li>
-											<li>操作日志新增返回参数</li>
-											<li>支持mybatis通配符扫描任意多个包</li>
-											<li>权限验证多种情况处理</li>
-											<li>修复树形类型的代码生成的部分必要属性无法显示</li>
-											<li>修复非表格插件情况下重置出现异常</li>
-											<li>修复富文本编辑器有序列表冲突</li>
-											<li>代码生成表前缀配置支持多个</li>
-											<li>修复自动去除表前缀配置无效问题</li>
-											<li>菜单列表按钮数据可见不显示(权限标识控制)</li>
-											<li>修复设置会话超时时间无效问题</li>
-											<li>新增本地资源通用下载方法</li>
-											<li>操作日志记录新增请求方式</li>
-											<li>代码生成单选按钮属性重名修复</li>
-											<li>优化select2下拉框宽度不会随浏览器改变</li>
-											<li>修复代码生成树表异常</li>
-											<li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v40">v4.0.0</a><code class="pull-right">2019.08.08</code>
-								   </h5>
-								</div>
-								<div id="v40" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>代码生成支持预览、编辑,保存方案</li>
-									        <li>新增防止表单重复提交注解</li>
-											<li>新增后端校验(和前端保持一致)</li>
-											<li>新增同一个用户最大会话数控制</li>
-											<li>Excel导出子对象支持多个字段</li>
-											<li>定时任务支持静态调用和多参数</li>
-											<li>定时任务增加分组条件查询</li>
-											<li>字典类型增加任务分组数据</li>
-											<li>新增表格是否首次加载数据</li>
-											<li>新增parentTab选项卡可在同一页签打开</li>
-											<li>多数据源支持类注解(允许继承父类的注解)</li>
-											<li>部门及以下数据权限(调整为以下及所有子节点)</li>
-											<li>新增角色数据权限配(仅本人数据权限)</li>
-											<li>修改菜单权限显示问题</li>
-											<li>上传文件修改路径及返回名称</li>
-											<li>添加报表插件及示例</li>
-											<li>添加首页统计模板</li>
-											<li>添加表格拖拽示例</li>
-											<li>添加卡片列表示例</li>
-											<li>添加富文本编辑器示例</li>
-											<li>添加表格动态增删改查示例</li>
-											<li>添加用户页面岗位选择框提示</li>
-											<li>点击菜单操作添加背景高亮显示</li>
-											<li>表格树新增showSearch是否显示检索信息</li>
-											<li>解决表格列设置sortName无效问题</li>
-											<li>表格图片预览支持自定义设置宽高</li>
-											<li>添加表格列浮动提示(单击文本复制)</li>
-											<li>PC端收起菜单后支持浮动显示</li>
-											<li>详细操作样式调整</li>
-											<li>修改用户更新描述空串不更新问题</li>
-											<li>导入修改为模板渲染</li>
-											<li>修改菜单及部门排序规则</li>
-											<li>角色导出数据范围表达式翻译</li>
-											<li>添加summernote富文本字体大小</li>
-											<li>优化表格底部下边框防重叠&汇总像素问题</li>
-											<li>树表格支持属性多层级访问</li>
-											<li>修复IE浏览器用户管理界面右侧留白问题</li>
-											<li>重置按钮刷新表格</li>
-											<li>重置密码更新用户缓存</li>
-											<li>优化验证码属性参数</li>
-											<li>支持数据监控配置用户名和密码</li>
-											<li>文件上传修改按钮背景及加载动画</li>
-											<li>支持配置一级菜单href跳转</li>
-											<li>侧边栏添加一套浅色主题</li>
-											<li>树表格添加回调函数(校验异常状态)</li>
-											<li>用户个人中心适配手机端显示</li>
-											<li>Excel支持设置导出类型&更换样式</li>
-											<li>检查属性改变修改为克隆方式(防止热部署强转异常)</li>
-											<li>其他细节优化</li>
-										</ol>
-									</div>
-							    </div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v34">v3.4.0</a><code class="pull-right">2019.06.03</code>
-								   </h5>
-								</div>
-								<div id="v34" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-									        <li>新增实例演示菜单及demo</li>
-											<li>新增页签右键操作</li>
-											<li>菜单管理新增打开方式</li>
-											<li>新增点击某行触发的事件</li>
-											<li>新增双击某行触发的事件</li>
-											<li>新增单击某格触发的事件</li>
-											<li>新增双击某格触发的事件</li>
-											<li>新增是否启用显示细节视图</li>
-											<li>支持上传任意格式文件</li>
-											<li>修复角色权限注解失效问题</li>
-											<li>左侧的菜单栏宽度调整</li>
-											<li>新增响应完成后自定义回调函数</li>
-											<li>支持前端及其他模块直接获取用户信息</li>
-											<li>升级swagger到最新版2.9.2</li>
-											<li>升级jquery.slimscroll到最新版1.3.8</li>
-											<li>升级select2到最新版4.0.7</li>
-											<li>新增角色配置本部门数据权限</li>
-											<li>新增角色配置本部门及以下数据权限</li>
-											<li>优化底部操作防止跳到页面顶端</li>
-											<li>修改冻结列选框无效及样式问题</li>
-											<li>修复部门四层级修改祖级无效问题</li>
-											<li>更换开关切换按钮样式</li>
-											<li>新增select2-bootstrap美化下拉框</li>
-											<li>添加表格内图片预览方法</li>
-											<li>修复权限校验失败跳转页面路径错误</li>
-											<li>国际化资源文件调整</li>
-											<li>通知公告布局调整</li>
-											<li>删除页签操作功能</li>
-											<li>表格树新增查询指定列值</li>
-											<li>更改系统接口扫描方式及完善测试案例</li>
-											<li>表格列浮动提示及字典回显默认去背景</li>
-											<li>修复启用翻页记住前面的选择check没选中问题</li>
-											<li>去除监控页面底部的广告</li>
-											<li>日期控件功问题修复及data功能增强</li>
-											<li>新增角色权限可见性(前端直接调用)</li>
-											<li>新增获取当前登录用户方法(前端及子模块调用)</li>
-											<li>修复热部署重启导致菜单丢失问题</li>
-											<li>优化业务校验失败普通请求跳转页面</li>
-											<li>操作日志新增状态条件查询</li>
-											<li>操作类型支持多选条件查询</li>
-											<li>通知公告防止滚动触底回弹优化</li>
-											<li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							 </div>
-                             <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v33">v3.3.0</a><code class="pull-right">2019.04.01</code>
-								   </h5>
-								</div>
-								<div id="v33" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-											<li>新增线程池统一管理</li>
-											<li>新增支持左右冻结列</li>
-											<li>新增表格字符超长浮动提示</li>
-											<li>升级datepicker拓展并汉化</li>
-											<li>升级druid到最新版本v1.1.14</li>
-											<li>修复个人头像为图片服务器跨域问题</li>
-											<li>修改上传文件按日期存储</li>
-											<li>新增表格客户端分页选项</li>
-											<li>新增表格的高度参数</li>
-											<li>新增表格销毁方法</li>
-											<li>新增表格下拉按钮切换方法</li>
-											<li>新增表格分页跳转到指定页码</li>
-											<li>新增表格启用点击选中行参数</li>
-											<li>修复表格数据重新加载未触发部分按钮禁用</li>
-											<li>使用jsonview展示操作日志参数</li>
-											<li>新增方法(addTab、editTab)</li>
-											<li>修改用户管理界面为Tab打开方式</li>
-											<li>表单验证代码优化</li>
-											<li>修复@Excel注解 prompt 属性使用报错</li>
-											<li>修复combo属性Excel兼容性问题</li>
-											<li>新增@Excel导入导出支持父类字段</li>
-											<li>修复关闭最后选项卡无法激活滚动问题</li>
-											<li>增加日期控件显示类型及回显格式扩展选项</li>
-											<li>修复定时任务执行失败后入库状态为成功状态</li>
-											<li>支持定时任务并发开关控制</li>
-											<li>优化权限校验失败普通请求跳转页面</li>
-											<li>捕获线程池执行任务抛出的异常</li>
-											<li>修复IE浏览器导出功能报错</li>
-											<li>新增角色管理分配用户功能</li>
-											<li>新增表格翻页记住前面的选择</li>
-											<li>调整用户个人中心页面</li>
-											<li>修复界面存在的一些安全问题</li>
-											<li>其他细节优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v32">v3.2.0</a><code class="pull-right">2019.01.18</code>
-								   </h5>
-								</div>
-								<div id="v32" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-											<li>部门修改时不允许选择最后节点</li>
-											<li>修复部门菜单排序字段无效</li>
-											<li>修复光驱磁盘导致服务监控异常</li>
-											<li>登录界面去除check插件</li>
-											<li>验证码文本字符间距修正</li>
-											<li>升级SpringBoot到最新版本2.1.1</li>
-											<li>升级MYSQL驱动</li>
-											<li>修正登录必填项位置偏移</li>
-											<li>Session会话检查优化</li>
-											<li>Excel注解支持多级获取</li>
-											<li>新增序列号生成方法</li>
-											<li>修复WAR部署tomcat退出线程异常</li>
-											<li>全屏操作增加默认确认/关闭</li>
-											<li>修复个人信息可能导致漏洞</li>
-											<li>字典数据根据下拉选择新增类型</li>
-											<li>升级Summernote到最新版本v0.8.11</li>
-											<li>新增用户数据导入</li>
-											<li>首页主题样式更换</li>
-											<li>layer扩展主题更换</li>
-											<li>用户管理移动端默认隐藏左侧布局</li>
-											<li>详细信息弹出层显示在顶层</li>
-											<li>表格支持切换状态(用户/角色/定时任务)</li>
-											<li>Druid数据源支持配置继承</li>
-											<li>修正部分iPhone手机端表格适配问题</li>
-											<li>新增防止重复提交表单方法</li>
-											<li>新增表格数据统计汇总方法</li>
-											<li>支持富文本上传图片文件</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v31">v3.1.0</a><code class="pull-right">2018.12.03</code>
-								   </h5>
-								</div>
-								<div id="v31" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-											<li>新增内网不获取IP地址</li>
-											<li>新增cron表达式有效校验</li>
-											<li>定时任务新增详细信息</li>
-											<li>定时任务默认策略修改(不触发立即执行)</li>
-											<li>定时任务显示下一个执行周期</li>
-											<li>支持前端任意日期格式处理</li>
-											<li>上传头像删除多余提交按钮</li>
-											<li>表格增加行间隔色配置项</li>
-											<li>表格增加转义HTML字符串配置项</li>
-											<li>表格增加显示/隐藏指定列</li>
-											<li>代码生成优化</li>
-											<li>操作日志参数格式化显示</li>
-											<li>页签新增新增全屏显示</li>
-											<li>新增一键打包部署</li>
-											<li>Excel注解新增多个参数</li>
-											<li>新增提交静默更新表格方法</li>
-											<li>新增服务监控菜单</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-							<div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v30">v3.0.0</a><code class="pull-right">2018.10.08</code>
-								   </h5>
-								</div>
-								<div id="v30" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-											<li>升级poi到最新版3.17</li>
-											<li>导出修改临时目录绝对路径</li>
-											<li>升级laydate到最新版5.0.9</li>
-											<li>升级SpringBoot到最新版本2.0.5</li>
-											<li>优化开始/结束时间校验限制</li>
-											<li>重置密码参数表中获取默认值</li>
-											<li>修复头像修改显示问题</li>
-											<li>新增数据权限过滤注解</li>
-											<li>新增表格检索折叠按钮</li>
-											<li>新增清空(登录、操作、调度)日志</li>
-											<li>固定按钮位置(提交/关闭)</li>
-											<li>部门/菜单支持(展开/折叠)</li>
-											<li>部分细节调整优化</li>
-											<li>项目采用分模块</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-							<div class="panel panel-default">
-								<div class="panel-heading">
-								   <h5 class="panel-title">
-									   <a data-toggle="collapse" data-parent="#version" href="#v24">v2.4.0</a><code class="pull-right">2018.09.03</code>
-								   </h5>
-								</div>
-								<div id="v24" class="panel-collapse collapse">
-									<div class="panel-body">
-									   <ol>
-											<li>支持部门多级查询</li>
-											<li>修复菜单状态查询无效</li>
-											<li>支持IP地址开关</li>
-											<li>支持XSS开关</li>
-											<li>记录日志异步处理</li>
-											<li>字典回显样式更改为下拉框</li>
-											<li>菜单类型必填校验</li>
-											<li>修复在线用户排序报错</li>
-											<li>增加重置按钮</li>
-											<li>支持注解导入数据</li>
-											<li>支持弹层外区域关闭</li>
-											<li>备注更换为文本区域</li>
-											<li>新增角色逻辑删除</li>
-											<li>新增部门逻辑删除</li>
-											<li>支持部门数据权限</li>
-											<li>管理员默认拥有所有授权</li>
-											<li>字典数据采用分页</li>
-											<li>部分细节调整优化</li>
-										</ol>
-									</div>
-								</div>
-							</div>
-                            <div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v23">v2.3.0</a><code class="pull-right">2018.08.06</code>
-									   </h5>
-									</div>
-									<div id="v23" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-										        <li>支持表格不分页开关控制</li>
-										        <li>修改字典类型同步修改字典数据</li>
-										        <li>代码生成新增修改后缀处理</li>
-										        <li>代码生成新增实体toString</li>
-										        <li>代码生成非字符串去除!=''</li>
-												<li>导出数据前加载遮罩层</li>
-												<li>部门删除校验条件修改</li>
-												<li>搜索查询下载优化</li>
-												<li>手机打开弹出层自适应</li>
-												<li>角色岗位禁用显示置灰</li>
-												<li>角色禁用不显示菜单</li>
-												<li>新增导出权限</li>
-												<li>角色权限唯一校验</li>
-												<li>岗位名称编码唯一校验</li>
-                                                <li>TreeTable优化</li>
-                                                <li>支持多数据源</li>
-												<li>其他细节优化</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-                                <div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v22">v2.2.0</a><code class="pull-right">2018.07.23</code>
-									   </h5>
-									</div>
-									<div id="v22" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-										        <li>修复批量生成代码异常问题</li>
-										        <li>修复定时器保存失败问题</li>
-										        <li>修复热部署转换问题</li>
-												<li>支持查询菜单管理,部门管理</li>
-												<li>大多数功能支持时间查询</li>
-												<li>自定义导出注解自动匹配column</li>
-												<li>新增任务执行策略</li>
-												<li>操作详细动态显示类型</li>
-												<li>支持动态回显字典数据</li>
-												<li>后台代码优化调整</li>
-												<li>其他细节优化</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-                                <div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v21">v2.1.0</a><code class="pull-right">2018.07.10</code>
-									   </h5>
-									</div>
-									<div id="v21" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-										        <li>新增登录超时提醒</li>
-										        <li>修复定时器热部署转换问题</li>
-										        <li>修复登录验证码校验无效问题</li>
-												<li>定时任务新增立即执行一次</li>
-												<li>存在字典数据不允许删除字典</li>
-												<li>字典数据支持按名称查询</li>
-												<li>代码生成增加日志注解&表格优化</li>
-												<li>修复用户逻辑删除后能登录问题</li>
-												<li>表格支持多字段动态排序</li>
-												<li>支持三级菜单显示</li>
-												<li>新增ry.sh启动程序脚本</li>
-												<li>其他细节优化</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-                            	<div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v20">v2.0.0</a><code class="pull-right">2018.07.02</code>
-									   </h5>
-									</div>
-									<div id="v20" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-										        <li>升级SpringBoot到最新版本2.0.3</li>
-										        <li>新增公告管理</li>
-												<li>表单校验示提体验优化</li>
-												<li>前端通用方法封装调整</li>
-												<li>前端去除js文件,合并到html</li>
-												<li>操作加载遮罩层</li>
-												<li>支持全屏模式操作</li>
-												<li>支持注解导出数据</li>
-												<li>系统支持多查询&下载</li>
-												<li>系统样式调整</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-                                <div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v16">v1.1.6</a><code class="pull-right">2018.06.04</code>
-									   </h5>
-									</div>
-									<div id="v16" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-												<li>新增用户列表部门列</li>
-												<li>新增登录地点</li>
-												<li>新增swagger</li>
-												<li>修复排序数字校验</li>
-												<li>优化头像上传文件类型限定为图片</li>
-												<li>新增XSS过滤</li>
-												<li>新增热部署提高开发效率</li>
-												<li>修复treegrid居中无效</li>
-												<li>角色多条件查询</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-                            	<div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v15">v1.1.5</a><code class="pull-right">2018.05.28</code>
-									   </h5>
-									</div>
-									<div id="v15" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-												<li>优化登录失败刷新验证码</li>
-												<li>新增用户登录地址时间</li>
-												<li>修复ajax超时退出问题</li>
-												<li>新增html调用数据字典(若依首创)</li>
-												<li>调整系统部分样式</li>
-												<li>新增用户逻辑删除</li>
-												<li>新增管理员不允许删除修改</li>
-												<li>升级bootstrapTable到最新版本1.12.1</li>
-												<li>升级layer到最新版本3.1.1</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-							    <div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v14">v1.1.4</a><code class="pull-right">2018.05.20</code>
-									   </h5>
-									</div>
-									<div id="v14" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-												<li>新增参数管理</li>
-												<li>修复头像上传bug</li>
-												<li>手机邮箱唯一校验</li>
-												<li>支持手机邮箱登录</li>
-												<li>代码生成优化</li>
-												<li>支持模糊查询</li>
-												<li>支持切换主题皮肤</li>
-												<li>修改权限即时生效</li>
-												<li>修复页签Tab关闭问题</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-									   <h5 class="panel-title">
-										   <a data-toggle="collapse" data-parent="#version" href="#v13">v1.1.3</a><code class="pull-right">2018.05.14</code>
-									   </h5>
-									</div>
-									<div id="v13" class="panel-collapse collapse">
-										<div class="panel-body">
-										   <ol>
-												<li>新增验证码(数组计算、字符验证)</li>
-												<li>新增cookie记住我</li>
-												<li>新增头像上传</li>
-												<li>用户名密码长度限制</li>
-												<li>通用字段提取</li>
-												<li>支持自定义条件查询</li>
-												<li>部门名称必填、时间格式调整</li>
-												<li>其他细节优化</li>
-											</ol>
-										</div>
-									</div>
-								</div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v12">v1.1.2</a><code class="pull-right">2018.05.07</code>
-										</h5>
-                                    </div>
-                                    <div id="v12" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增个人信息修改</li>
-												<li>菜单存在子菜单不允许删除</li>
-												<li>菜单分配角色不允许删除</li>
-												<li>角色分配人员不允许删除</li>
-												<li>岗位使用后不允许删除</li>
-												<li>保证用户的数据完整性加入事物</li>
-												<li>新增环境使用手册、数据建模</li>
-												<li>Thymeleaf升级到3.0</li>
-												<li>支持非ROOT部署</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v11">v1.1.1</a><code class="pull-right">2018.04.23</code>
-										</h5>
-                                    </div>
-                                    <div id="v11" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增表单构建器</li>
-												<li>代码生成优化</li>
-												<li>支持新增主部门</li>
-												<li>支持选择上级部门、上级菜单</li>
-												<li>新增字典管理单条删除</li>
-												<li>优化一些其他细节</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v10">v1.1.0</a><code class="pull-right">2018.04.20</code>
-										</h5>
-                                    </div>
-                                    <div id="v10" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>支持密码盐</li>
-												<li>支持新增主目录</li>
-												<li>支持批量生成代码</li>
-												<li>支持表格导出(csv、txt、doc、excel)</li>
-												<li>自动适应宽高模式窗体</li>
-												<li>重复校验(角色名、菜单名、部门名)</li>
-												<li>优化一些其他细节</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v09">v1.0.9</a><code class="pull-right">2018.04.14</code>
-										</h5>
-                                    </div>
-                                    <div id="v09" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增代码生成(生成包括 java、html、js、xml、sql)</li>
-												<li>新增按钮权限控制隐藏(若依首创)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v08">v1.0.8</a><code class="pull-right">2018.04.08</code>
-										</h5>
-                                    </div>
-                                    <div id="v08" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增定时任务(新增、修改、删除、查询、启动/暂停)</li>
-												<li>新增调度日志(查询、删除)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                            	<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v07">v1.0.7</a><code class="pull-right">2018.04.04</code>
-										</h5>
-                                    </div>
-                                    <div id="v07" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增岗位管理(新增、修改、删除、查询)</li>
-												<li>优化用户管理,菜单管理部分细节</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v06">v1.0.6</a><code class="pull-right">2018.03.15</code>
-										</h5>
-                                    </div>
-                                    <div id="v06" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增字典管理(新增、删除、修改、查询、数据选择)</li>
-												<li>新增用户密码重置</li>
-												<li>优化一些其他细节</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v05">v1.0.5</a><code class="pull-right">2018.03.12</code>
-										</h5>
-                                    </div>
-                                    <div id="v05" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增菜单管理(新增、删除、修改、查询、图标选择)</li>
-												<li>部门管理优化(添加责任人、联系电话、邮箱、修改者)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v04">v1.0.4</a><code class="pull-right">2018.03.11</code>
-										</h5>
-                                    </div>
-                                    <div id="v04" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增角色管理(新增、删除、修改、查询、菜单选择)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-								<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v03">v1.0.3</a><code class="pull-right">2018.03.08</code>
-										</h5>
-                                    </div>
-                                    <div id="v03" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增用户管理(新增、删除、修改、查询、部门选择)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                            	<div class="panel panel-default">
-									<div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v02">v1.0.2</a><code class="pull-right">2018.03.04</code>
-										</h5>
-                                    </div>
-                                    <div id="v02" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增部门管理 (新增、删除、修改、查询)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="panel panel-default">
-                                    <div class="panel-heading">
-                                        <h5 class="panel-title">
-											<a data-toggle="collapse" data-parent="#version" href="#v01">v1.0.1</a><code class="pull-right">2018.03.03</code>
-										</h5>
-                                    </div>
-                                    <div id="v01" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                            	<li>新增在线用户 (批量强退、单条强退、查询)</li>
-                                                <li>新增登录日志 (批量删除、查询)</li>
-												<li>新增操作日志 (批量删除、查询、详细)</li>
-												<li>新增数据监控 (监控DB池连接和SQL的执行)</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="panel panel-default">
-                                    <div class="panel-heading">
-                                        <h4 class="panel-title">
-                                            <a data-toggle="collapse" data-parent="#version" href="#v00">v1.0.0</a><code class="pull-right">2018.03.01</code>
-                                        </h4>
-                                    </div>
-                                    <div id="v00" class="panel-collapse collapse">
-                                        <div class="panel-body">
-                                            <ol>
-                                                <li>若依管理系统正式发布。</li>
-                                            </ol>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="col-sm-4">
-                <div class="ibox float-e-margins">
-                    <div class="ibox-title">
-                        <h5>捐赠</h5>
-                    </div>
-                    <div class="ibox-content">
-                        <div class="alert alert-warning">
-                            	请作者喝杯咖啡(点击图片放大)
-                        </div>
-                        <p id="pay-qrcode">
-                            <a href="javascript:;"><img th:src="@{/img/pay.png}" width="100%" alt="请使用手机支付宝或者微信扫码支付">
-                            </a>
-                        </p>
-
-                    </div>
-                </div>
-            </div>
-        </div>
+    <div style="display: flex; align-items: center; justify-content: center; height: 100vh;">
+        <h1 style="font-size: 32px; color: #676a6c; font-weight: 300;">欢迎使用宗教管理信息系统</h1>
     </div>
     <script th:src="@{/js/jquery.min.js}"></script>
     <script th:src="@{/js/bootstrap.min.js}"></script>
-    <script th:src="@{/ajax/libs/layer/layer.min.js}"></script>
-    <script type="text/javascript">
-	    $('#pay-qrcode').click(function(){
-	        var html=$(this).html();
-	        parent.layer.open({
-	            title: false,
-	            type: 1,
-	            closeBtn:false,
-	            shadeClose:true,
-	            area: ['600px', '360px'],
-	            content: html
-	        });
-	    });
-    </script>
 </body>
 </html>

+ 146 - 0
ruoyi-admin/src/main/resources/templates/religion/audit/audit.html

@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('业务审计日志列表')" />
+</head>
+<body class="gray-bg">
+    <div class="container-div">
+        <div class="row">
+            <div class="col-sm-12 search-collapse">
+                <form id="audit-form">
+                    <div class="select-list">
+                        <ul>
+                            <li>
+                                操作人:<input type="text" name="userName" placeholder="请输入操作人"/>
+                            </li>
+                            <li>
+                                操作类型:<select name="opType">
+                                    <option value="">所有</option>
+                                    <option value="新增">新增</option>
+                                    <option value="修改">修改</option>
+                                    <option value="删除">删除</option>
+                                    <option value="批量导入">批量导入</option>
+                                    <option value="批量修改">批量修改</option>
+                                    <option value="校核数据">校核数据</option>
+                                    <option value="BATCH_IMPORT_PERSON">教职人员批量导入</option>
+                                </select>
+                            </li>
+                            <li>
+                                系统模块:<input type="text" name="resourceType" placeholder="请输入系统模块"/>
+                            </li>
+                            <li class="select-time">
+                                <label>操作时间: </label>
+                                <input type="text" class="time-input" id="startTime" placeholder="开始时间" name="params[beginTime]"/>
+                                <span>-</span>
+                                <input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endTime]"/>
+                            </li>
+                            <li>
+                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
+                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
+                            </li>
+                        </ul>
+                    </div>
+                </form>
+            </div>
+            
+            <div class="btn-group-sm" id="toolbar" role="group">
+                <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="religion:audit:export">
+                    <i class="fa fa-download"></i> 导出
+                </a>
+                <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="religion:audit:remove">
+                    <i class="fa fa-remove"></i> 删除
+                </a>
+            </div>
+            
+            <div class="col-sm-12 select-table table-striped">
+                <table id="bootstrap-table"></table>
+            </div>
+        </div>
+    </div>
+    
+    <th:block th:include="include :: footer" />
+    <script th:inline="javascript">
+        var removeFlag = [[${@permission.hasPermi('religion:audit:remove')}]];
+        var prefix = ctx + "religion/audit";
+
+        $(function() {
+            var options = {
+                url: prefix + "/list",
+                removeUrl: prefix + "/remove",
+                exportUrl: prefix + "/export",
+                sortName: "opTime",
+                sortOrder: "desc",
+                modalName: "业务审计日志",
+                columns: [{
+                    checkbox: true
+                },
+                {
+                    field: 'logId',
+                    title: '日志ID',
+                    visible: false
+                },
+                {
+                    field: 'opTime',
+                    title: '操作时间',
+                    sortable: true,
+                    width: '180px'
+                },
+                {
+                    field: 'userName',
+                    title: '操作人',
+                    sortable: true
+                },
+                {
+                    field: 'opType',
+                    title: '操作类型',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        if (value === 'BATCH_IMPORT_PERSON') {
+                            return '教职人员批量导入';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'resourceType',
+                    title: '系统模块',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        if (value === 'person_import') {
+                            return '宗教教职人员信息导入';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'resourceId',
+                    title: '资源ID'
+                },
+                {
+                    field: 'ip',
+                    title: '操作IP',
+                    visible: false
+                },
+                {
+                    title: '操作',
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        var actions = [];
+                        actions.push('<a class="btn btn-info btn-xs" href="javascript:void(0)" onclick="viewDetail(\'' + row.logId + '\')"><i class="fa fa-info-circle"></i>详情</a> ');
+                        actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.logId + '\')"><i class="fa fa-remove"></i>删除</a>');
+                        return actions.join('');
+                    }
+                }]
+            };
+            $.table.init(options);
+        });
+        
+        /* 查看详情 */
+        function viewDetail(logId) {
+            var url = prefix + '/detail/' + logId;
+            $.modal.open("审计日志详情", url, '900', '600');
+        }
+    </script>
+</body>
+</html>
+

+ 263 - 0
ruoyi-admin/src/main/resources/templates/religion/audit/detail.html

@@ -0,0 +1,263 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('审计日志详情')" />
+    <style>
+        .detail-info-box {
+            background: #f9f9f9;
+            padding: 15px;
+            border-radius: 4px;
+            margin-bottom: 20px;
+        }
+        .detail-info-box .info-item {
+            margin-bottom: 10px;
+        }
+        .detail-info-box .info-item label {
+            font-weight: bold;
+            min-width: 120px;
+            display: inline-block;
+        }
+        .change-table {
+            margin-top: 15px;
+        }
+        .change-table th {
+            background: #f5f5f5;
+            font-weight: bold;
+        }
+        .before-value {
+            color: #999;
+            text-decoration: line-through;
+        }
+        .after-value {
+            color: #00a65a;
+            font-weight: bold;
+        }
+        .raw-json-box {
+            margin-top: 20px;
+            border: 1px solid #ddd;
+            border-radius: 4px;
+            padding: 10px;
+        }
+        .raw-json-box pre {
+            background: #f5f5f5;
+            padding: 10px;
+            border-radius: 4px;
+            max-height: 300px;
+            overflow-y: auto;
+            font-size: 12px;
+        }
+        .toggle-json-btn {
+            cursor: pointer;
+            color: #337ab7;
+            text-decoration: underline;
+        }
+    </style>
+</head>
+<body class="white-bg">
+    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
+        <!-- 基本信息区 -->
+        <div class="detail-info-box">
+            <h4 style="margin-top: 0; margin-bottom: 15px; border-bottom: 2px solid #337ab7; padding-bottom: 10px;">
+                <i class="fa fa-info-circle"></i> 基本信息
+            </h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="info-item">
+                        <label>日志ID:</label>
+                        <span th:text="${log.logId}"></span>
+                    </div>
+                    <div class="info-item">
+                        <label>操作类型:</label>
+                        <span th:if="${log.opType == 'BATCH_IMPORT_PERSON'}" style="font-weight: bold; color: #337ab7;">教职人员批量导入</span>
+                        <span th:unless="${log.opType == 'BATCH_IMPORT_PERSON'}" th:text="${log.opType}" style="font-weight: bold; color: #337ab7;"></span>
+                    </div>
+                    <div class="info-item">
+                        <label>操作人:</label>
+                        <span th:text="${log.userName}"></span>
+                    </div>
+                    <div class="info-item">
+                        <label>操作时间:</label>
+                        <span th:text="${#dates.format(log.opTime, 'yyyy-MM-dd HH:mm:ss')}"></span>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="info-item">
+                        <label>系统模块:</label>
+                        <span th:if="${log.resourceType == 'person_import'}">宗教教职人员信息导入</span>
+                        <span th:unless="${log.resourceType == 'person_import'}" th:text="${log.resourceType}"></span>
+                    </div>
+                    <div class="info-item">
+                        <label>资源ID:</label>
+                        <span th:text="${log.resourceId}"></span>
+                    </div>
+                    <div class="info-item">
+                        <label>操作IP:</label>
+                        <span th:text="${log.ip}"></span>
+                    </div>
+                </div>
+            </div>
+        </div>
+        
+        <!-- 批量导入概要信息(仅在批量导入操作时显示) -->
+        <div th:if="${isBatchImport}" class="panel panel-info">
+            <div class="panel-heading">
+                <h3 class="panel-title"><i class="fa fa-upload"></i> 导入概要信息</h3>
+            </div>
+            <div class="panel-body">
+                <div class="alert alert-info" style="margin-bottom: 15px;">
+                    <i class="fa fa-info-circle"></i> 本次为教职人员批量导入操作,不展示逐条字段变更。
+                </div>
+                <div id="import-summary-content"></div>
+            </div>
+        </div>
+        
+        <!-- 新增数据详情(仅在新增类操作时显示) -->
+        <div th:if="${isCreate && !isBatchImport}" class="panel panel-success">
+            <div class="panel-heading">
+                <h3 class="panel-title"><i class="fa fa-plus-circle"></i> 新增数据详情</h3>
+            </div>
+            <div class="panel-body">
+                <table class="table table-bordered table-striped change-table" th:if="${createList != null && !createList.isEmpty()}">
+                    <thead>
+                        <tr>
+                            <th width="60">序号</th>
+                            <th width="200">字段名称</th>
+                            <th width="150">字段Key</th>
+                            <th>字段值</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr th:each="item, iterStat : ${createList}">
+                            <td th:text="${iterStat.index + 1}">1</td>
+                            <td th:text="${item.fieldLabel}">场所名称</td>
+                            <td><code th:text="${item.fieldKey}">place_name</code></td>
+                            <td th:text="${item.value}">西安XX清真寺</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <div th:if="${createList == null || createList.isEmpty()}" class="alert alert-info">
+                    <i class="fa fa-info-circle"></i> 本次新增未记录字段明细
+                </div>
+            </div>
+        </div>
+        
+        <!-- 字段变更明细(仅在修改类操作时显示) -->
+        <div th:if="${!isCreate && !isBatchImport}" class="panel panel-warning">
+            <div class="panel-heading">
+                <h3 class="panel-title"><i class="fa fa-edit"></i> 字段变更明细</h3>
+            </div>
+            <div class="panel-body">
+                <table class="table table-bordered table-striped change-table" th:if="${diffList != null && !diffList.isEmpty()}">
+                    <thead>
+                        <tr>
+                            <th width="60">序号</th>
+                            <!-- 批量修改时显示姓名列 -->
+                            <th width="120" th:if="${diffList[0].personName != null}">姓名</th>
+                            <th width="180">字段名称</th>
+                            <th width="130">字段Key</th>
+                            <th>修改前</th>
+                            <th>修改后</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr th:each="item, iterStat : ${diffList}">
+                            <td th:text="${iterStat.index + 1}">1</td>
+                            <!-- 批量修改时显示姓名 -->
+                            <td th:if="${item.personName != null}" th:text="${item.personName}">张三</td>
+                            <td th:text="${item.fieldLabel}">负责人姓名</td>
+                            <td><code th:text="${item.fieldKey}">charge_name</code></td>
+                            <td class="before-value" th:text="${item.beforeValue}">张三</td>
+                            <td class="after-value" th:text="${item.afterValue}">李四</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <div th:if="${diffList == null || diffList.isEmpty()}" class="alert alert-info">
+                    <i class="fa fa-info-circle"></i> 本次操作未检测到字段变更
+                </div>
+            </div>
+        </div>
+        
+        <!-- 原始 JSON 展示 -->
+        <div class="raw-json-box" th:if="${rawDetail != null && rawDetail != ''}">
+            <h5 style="margin-top: 0;">
+                <i class="fa fa-code"></i> 原始变更数据(opDetail JSON)
+                <span class="toggle-json-btn pull-right" onclick="toggleRawJson()">
+                    <i class="fa fa-chevron-down" id="json-toggle-icon"></i> 展开/折叠
+                </span>
+            </h5>
+            <pre id="raw-json-content" style="display: none;" th:text="${rawDetail}"></pre>
+        </div>
+        
+        <div th:if="${rawDetail == null || rawDetail == ''}" class="alert alert-warning" style="margin-top: 20px;">
+            <i class="fa fa-exclamation-triangle"></i> 本次操作未记录详细变更数据
+        </div>
+    </div>
+    
+    <th:block th:include="include :: footer" />
+    <script th:inline="javascript">
+        var isBatchImport = [[${isBatchImport}]];
+        var rawDetail = [[${rawDetail}]];
+        
+        $(function() {
+            // 如果是批量导入操作,解析并展示导入概要信息
+            if (isBatchImport && rawDetail) {
+                try {
+                    var importData = JSON.parse(rawDetail);
+                    var html = '<table class="table table-bordered table-striped">';
+                    html += '<tbody>';
+                    
+                    if (importData.batchNo) {
+                        html += '<tr><td width="200" style="font-weight: bold;"><i class="fa fa-tag"></i> 批次号</td><td>' + importData.batchNo + '</td></tr>';
+                    }
+                    if (importData.fileName) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-file-excel-o"></i> 导入文件名</td><td>' + importData.fileName + '</td></tr>';
+                    }
+                    if (importData.totalRows !== undefined) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-list"></i> 总记录数</td><td><span class="badge badge-primary">' + importData.totalRows + '</span></td></tr>';
+                    }
+                    if (importData.validRows !== undefined) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-check-circle"></i> 有效记录数</td><td><span class="badge badge-success">' + importData.validRows + '</span></td></tr>';
+                    }
+                    if (importData.errorRows !== undefined) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-times-circle"></i> 错误记录数</td><td><span class="badge badge-danger">' + importData.errorRows + '</span></td></tr>';
+                    }
+                    if (importData.conflictRows !== undefined) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-exclamation-triangle"></i> 冲突记录数</td><td><span class="badge badge-warning">' + importData.conflictRows + '</span></td></tr>';
+                    }
+                    if (importData.importedRows !== undefined) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-check"></i> 实际导入条数</td><td><span class="badge badge-info">' + importData.importedRows + '</span></td></tr>';
+                    }
+                    if (importData.abandonedRows !== undefined) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-trash"></i> 放弃导入条数</td><td><span class="badge badge-default">' + importData.abandonedRows + '</span></td></tr>';
+                    }
+                    if (importData.uploadTime) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-clock-o"></i> 上传时间</td><td>' + importData.uploadTime + '</td></tr>';
+                    }
+                    if (importData.lastProcessTime) {
+                        html += '<tr><td style="font-weight: bold;"><i class="fa fa-clock-o"></i> 最近处理时间</td><td>' + importData.lastProcessTime + '</td></tr>';
+                    }
+                    
+                    html += '</tbody></table>';
+                    $('#import-summary-content').html(html);
+                } catch (e) {
+                    // 解析失败,显示原始JSON
+                    $('#import-summary-content').html('<div class="alert alert-warning"><i class="fa fa-exclamation-triangle"></i> 无法解析导入概要信息</div><pre>' + rawDetail + '</pre>');
+                }
+            }
+        });
+        
+        function toggleRawJson() {
+            var content = document.getElementById('raw-json-content');
+            var icon = document.getElementById('json-toggle-icon');
+            if (content.style.display === 'none') {
+                content.style.display = 'block';
+                icon.className = 'fa fa-chevron-up';
+            } else {
+                content.style.display = 'none';
+                icon.className = 'fa fa-chevron-down';
+            }
+        }
+    </script>
+</body>
+</html>
+

+ 961 - 0
ruoyi-admin/src/main/resources/templates/religion/person/personImport.html

@@ -0,0 +1,961 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('教职人员信息导入')" />
+</head>
+<body class="gray-bg">
+    <div class="container-div">
+        <div class="row">
+            <div class="col-sm-12 search-collapse">
+                <form id="formId">
+                    <div class="select-list">
+                        <ul>
+                            <li>
+                                <label>批次号:</label>
+                                <input type="text" name="batchNo" placeholder="请输入批次号"/>
+                            </li>
+                            <li>
+                                <label>状态:</label>
+                                <select name="status" th:with="type=${@dict.getType('import_batch_status')}">
+                                    <option value="">所有</option>
+                                    <option value="PARSED">已解析</option>
+                                    <option value="PARTIAL">部分完成</option>
+                                    <option value="FINISHED">已完成</option>
+                                </select>
+                            </li>
+                            <li class="select-time">
+                                <label>上传时间:</label>
+                                <input type="text" class="time-input" id="startTime" placeholder="开始时间" name="params[beginTime]"/>
+                                <span>-</span>
+                                <input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endTime]"/>
+                            </li>
+                            <li>
+                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
+                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
+                            </li>
+                        </ul>
+                    </div>
+                </form>
+            </div>
+
+            <div class="btn-group-sm" id="toolbar" role="group">
+                <div class="alert alert-info" style="display: inline-block; margin-bottom: 0; margin-right: 20px; padding: 8px 15px;">
+                    <i class="fa fa-info-circle"></i> 可通过上传 Excel 批量导入教职人员信息
+                </div>
+                <a class="btn btn-success" onclick="openImportModal()" shiro:hasPermission="religion:religionPerson:import">
+                    <i class="fa fa-upload"></i> 上传并导入
+                </a>
+            </div>
+
+            <div class="col-sm-12 select-table table-striped">
+                <table id="batch-table"></table>
+            </div>
+        </div>
+    </div>
+
+    <!-- 导入预览弹框 -->
+    <div class="modal fade" id="personImportModal" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog" style="width: 90%; max-width: 1400px;">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <h4 class="modal-title">教职人员导入预览</h4>
+                </div>
+                <div class="modal-body">
+                    <!-- 文件上传区域 -->
+                    <div id="uploadArea">
+                        <div class="form-group">
+                            <label>选择Excel文件:</label>
+                            <input type="file" id="importFile" accept=".xls,.xlsx" class="form-control" style="width: 400px; display: inline-block;">
+                            <button type="button" class="btn btn-primary" onclick="uploadAndValidate()">
+                                <i class="fa fa-upload"></i> 上传并校验
+                            </button>
+                        </div>
+                        <div class="alert alert-info">
+                            <i class="fa fa-info-circle"></i> 
+                            请上传符合模板格式的Excel文件,系统将自动校验数据并标识错误和冲突记录。
+                        </div>
+                    </div>
+
+                    <!-- 批次统计区域 -->
+                    <div id="statsArea" style="display: none;">
+                        <div class="row">
+                            <div class="col-sm-2">
+                                <div class="panel panel-default">
+                                    <div class="panel-body text-center">
+                                        <h4 id="stat-total" class="text-primary">0</h4>
+                                        <p>总条数</p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-2">
+                                <div class="panel panel-default">
+                                    <div class="panel-body text-center">
+                                        <h4 id="stat-valid" class="text-success">0</h4>
+                                        <p>有效条数</p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-2">
+                                <div class="panel panel-default">
+                                    <div class="panel-body text-center">
+                                        <h4 id="stat-error" class="text-danger">0</h4>
+                                        <p>错误条数</p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-2">
+                                <div class="panel panel-default">
+                                    <div class="panel-body text-center">
+                                        <h4 id="stat-conflict" class="text-warning">0</h4>
+                                        <p>冲突条数</p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-2">
+                                <div class="panel panel-default">
+                                    <div class="panel-body text-center">
+                                        <h4 id="stat-imported" class="text-info">0</h4>
+                                        <p>已导入</p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-2">
+                                <div class="panel panel-default">
+                                    <div class="panel-body text-center">
+                                        <h4 id="stat-abandoned">0</h4>
+                                        <p>已放弃</p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+
+                    <!-- 预览表格区域 -->
+                    <div id="previewArea" style="display: none;">
+                        <div class="form-group">
+                            <label>状态筛选:</label>
+                            <select id="statusFilter" class="form-control" style="width: 200px; display: inline-block;">
+                                <option value="">全部</option>
+                                <option value="NORMAL">正常</option>
+                                <option value="ERROR">错误</option>
+                                <option value="CONFLICT">冲突</option>
+                            </select>
+                        </div>
+                        <table id="preview-table"></table>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <div class="pull-left text-muted" style="margin-top: 8px;">
+                        <i class="fa fa-info-circle"></i> 
+                        说明:导入策略仅作用于异常/冲突记录(ERROR/CONFLICT),正常记录将直接按规则导入系统。
+                    </div>
+                    <button type="button" class="btn btn-white" data-dismiss="modal">取消</button>
+                    <button type="button" id="btnCommitImport" class="btn btn-primary" onclick="commitImport()" style="display: none;">
+                        <i class="fa fa-check"></i> 提交导入
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!-- 手工编辑弹框 -->
+    <div class="modal fade" id="personImportEditModal" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog" style="width: 800px;">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <h4 class="modal-title">手工编辑导入数据</h4>
+                </div>
+                <div class="modal-body">
+                    <form id="editForm" class="form-horizontal">
+                        <input type="hidden" id="edit-itemId">
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label"><span class="text-danger">*</span> 所属场所名称:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-placeName" name="placeName">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label"><span class="text-danger">*</span> 姓名:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-name" name="name">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label"><span class="text-danger">*</span> 宗教类别:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-religionType" name="religionType">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">性别:</label>
+                                    <div class="col-sm-8">
+                                        <select class="form-control" id="edit-gender" name="gender">
+                                            <option value="">请选择</option>
+                                            <option value="男">男</option>
+                                            <option value="女">女</option>
+                                        </select>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">民族:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-nation" name="nation">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label"><span class="text-danger">*</span> 身份证号:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-idNumber" name="idNumber">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">出生日期:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-birthday" name="birthday">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">所属区县:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-county" name="county">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-12">
+                                <div class="form-group">
+                                    <label class="col-sm-2 control-label">家庭地址:</label>
+                                    <div class="col-sm-10">
+                                        <input type="text" class="form-control" id="edit-address" name="address">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">政治面貌:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-politicalStatus" name="politicalStatus">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">国民教育学历:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-eduNational" name="eduNational">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">宗教学历:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-eduReligious" name="eduReligious">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">入教/出家时间:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-joinTime" name="joinTime">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">联系电话:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-phone" name="phone">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">户籍所在地:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-domicile" name="domicile">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">籍贯:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-nativePlace" name="nativePlace">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-sm-6">
+                                <div class="form-group">
+                                    <label class="col-sm-4 control-label">所属宫观名称:</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-orgName" name="orgName">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="col-sm-12">
+                                <div class="form-group">
+                                    <label class="col-sm-2 control-label">所属宫观地址:</label>
+                                    <div class="col-sm-10">
+                                        <input type="text" class="form-control" id="edit-orgAddress" name="orgAddress">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-white" data-dismiss="modal">取消</button>
+                    <button type="button" class="btn btn-primary" onclick="saveAndRevalidate()">
+                        <i class="fa fa-save"></i> 保存并重新校验
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <th:block th:include="include :: footer" />
+    <th:block th:include="include :: datetimepicker-js" />
+    <script th:inline="javascript">
+        var ctx = [[@{/}]];
+        var prefix = ctx + "religion/person/import";
+
+        $(function() {
+            var options = {
+                id: "batch-table",
+                url: prefix + "/batch/list",
+                queryForm: "formId",
+                sortName: "uploadTime",
+                sortOrder: "desc",
+                modalName: "导入批次",
+                columns: [{
+                    checkbox: true
+                },
+                {
+                    field: 'batchId',
+                    title: '批次ID',
+                    visible: false
+                },
+                {
+                    field: 'batchNo',
+                    title: '批次号',
+                    sortable: true
+                },
+                {
+                    field: 'fileName',
+                    title: '导入文件名'
+                },
+                {
+                    field: 'uploader',
+                    title: '上传人'
+                },
+                {
+                    field: 'uploadTime',
+                    title: '上传时间',
+                    sortable: true
+                },
+                {
+                    field: 'totalRows',
+                    title: '总条数',
+                    sortable: true
+                },
+                {
+                    field: 'validRows',
+                    title: '有效条数',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        if (value > 0) {
+                            return '<span class="badge badge-success">' + value + '</span>';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'errorRows',
+                    title: '错误条数',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        if (value > 0) {
+                            return '<span class="badge badge-danger">' + value + '</span>';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'conflictRows',
+                    title: '冲突条数',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        if (value > 0) {
+                            return '<span class="badge badge-warning">' + value + '</span>';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'importedRows',
+                    title: '成功导入',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        if (value > 0) {
+                            return '<span class="badge badge-primary">' + value + '</span>';
+                        }
+                        return value || 0;
+                    }
+                },
+                {
+                    field: 'abandonedRows',
+                    title: '放弃条数',
+                    sortable: true,
+                    formatter: function(value, row, index) {
+                        return value || 0;
+                    }
+                },
+                {
+                    field: 'status',
+                    title: '状态',
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        if (value == 'PARSED') {
+                            return '<span class="badge badge-info">已解析</span>';
+                        } else if (value == 'PARTIAL') {
+                            return '<span class="badge badge-warning">部分完成</span>';
+                        } else if (value == 'FINISHED') {
+                            return '<span class="badge badge-success">已完成</span>';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    title: '操作',
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        var actions = [];
+                        actions.push('<a class="btn btn-success btn-xs" href="javascript:void(0)" onclick="viewBatchDetail(\'' + row.batchId + '\')"><i class="fa fa-eye"></i>查看详情</a>');
+                        return actions.join(' ');
+                    }
+                }]
+            };
+            $.table.init(options);
+
+            // 初始化时间选择器
+            $("#startTime").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true
+            });
+
+            $("#endTime").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true
+            });
+        });
+
+        // 全局变量
+        var currentBatchId = null;
+        var currentBatchStatus = null;
+        var previewTableInitialized = false;
+
+        /**
+         * 打开导入弹框
+         */
+        function openImportModal() {
+            // 重置弹框状态
+            resetImportModal();
+            // 显示上传区域
+            $("#uploadArea").show();
+            $("#statsArea").hide();
+            $("#previewArea").hide();
+            $("#btnCommitImport").hide();
+            // 打开弹框
+            $("#personImportModal").modal("show");
+        }
+
+        /**
+         * 查看批次详情
+         */
+        function viewBatchDetail(batchId) {
+            // 查询批次信息
+            $.ajax({
+                url: prefix + "/batch",
+                type: "get",
+                data: { batchId: batchId },
+                success: function(result) {
+                    if (result.code == web_status.SUCCESS) {
+                        var batch = result.data;
+                        
+                        // 先重置弹框状态,避免清掉后面刚设置的批次ID
+                        resetImportModal();
+                        
+                        // 再设置当前批次信息
+                        currentBatchId = batchId;
+                        currentBatchStatus = batch.status;
+                        
+                        // 隐藏上传区域
+                        $("#uploadArea").hide();
+                        
+                        // 显示统计信息
+                        showBatchStats(batch);
+                        
+                        // 初始化预览表格
+                        initPreviewTable(batchId, batch.status);
+                        
+                        // 如果批次已完成,禁用提交按钮
+                        if (batch.status == 'FINISHED') {
+                            $("#btnCommitImport").hide();
+                        } else {
+                            $("#btnCommitImport").show();
+                        }
+                        
+                        // 打开弹框
+                        $("#personImportModal").modal("show");
+                    } else {
+                        $.modal.alertError(result.msg);
+                    }
+                }
+            });
+        }
+
+        /**
+         * 重置导入弹框
+         */
+        function resetImportModal() {
+            currentBatchId = null;
+            currentBatchStatus = null;
+            $("#importFile").val("");
+            $("#statusFilter").val("");
+            if (previewTableInitialized) {
+                $("#preview-table").bootstrapTable('destroy');
+                previewTableInitialized = false;
+            }
+        }
+
+        /**
+         * 上传并校验
+         */
+        function uploadAndValidate() {
+            var fileInput = document.getElementById("importFile");
+            if (!fileInput.files || fileInput.files.length == 0) {
+                $.modal.alertWarning("请选择要上传的Excel文件");
+                return;
+            }
+
+            var file = fileInput.files[0];
+            var fileName = file.name;
+            if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
+                $.modal.alertWarning("只支持Excel文件格式(.xls或.xlsx)");
+                return;
+            }
+
+            // 创建FormData
+            var formData = new FormData();
+            formData.append("file", file);
+
+            // 先关闭对话框,再显示 loading
+            $("#personImportModal").modal("hide");
+            $.modal.loading("正在上传并解析文件,请稍候...");
+
+            // 上传文件
+            $.ajax({
+                url: prefix + "/upload",
+                type: "post",
+                data: formData,
+                processData: false,
+                contentType: false,
+                success: function(result) {
+                    $.modal.closeLoading();
+                    if (result.code == web_status.SUCCESS) {
+                        var data = result.data;
+                        currentBatchId = data.batchId;
+                        currentBatchStatus = 'PARSED';
+                        
+                        // 隐藏上传区域
+                        $("#uploadArea").hide();
+                        
+                        // 显示统计信息
+                        showBatchStats(data);
+                        
+                        // 初始化预览表格
+                        initPreviewTable(data.batchId, 'PARSED');
+                        
+                        // 显示提交按钮
+                        $("#btnCommitImport").show();
+                        
+                        // 重新打开对话框显示预览结果
+                        $("#personImportModal").modal("show");
+                        
+                        $.modal.msgSuccess("文件上传成功,共解析 " + data.totalRows + " 条记录");
+                    } else {
+                        $.modal.alertError(result.msg);
+                    }
+                },
+                error: function() {
+                    $.modal.closeLoading();
+                    $.modal.alertError("上传失败,请稍后重试");
+                }
+            });
+        }
+
+        /**
+         * 显示批次统计信息
+         */
+        function showBatchStats(data) {
+            $("#stat-total").text(data.totalRows || 0);
+            $("#stat-valid").text(data.validRows || 0);
+            $("#stat-error").text(data.errorRows || 0);
+            $("#stat-conflict").text(data.conflictRows || 0);
+            $("#stat-imported").text(data.importedRows || 0);
+            $("#stat-abandoned").text(data.abandonedRows || 0);
+            $("#statsArea").show();
+        }
+
+        /**
+         * 初始化预览表格
+         */
+        function initPreviewTable(batchId, batchStatus) {
+            var isFinished = (batchStatus == 'FINISHED');
+            
+            var options = {
+                url: prefix + "/items",
+                method: 'get',
+                queryParams: function(params) {
+                    // 转换 bootstrap-table 的分页参数为若依框架的分页参数
+                    var pageNum = params.offset / params.limit + 1;
+                    var pageSize = params.limit;
+                    
+                    // 返回查询参数,这些参数会作为 URL 参数传递
+                    var queryParams = {
+                        batchId: batchId,
+                        pageNum: pageNum,
+                        pageSize: pageSize
+                    };
+                    
+                    // 如果有状态筛选,添加 status 参数
+                    var statusVal = $("#statusFilter").val();
+                    if (statusVal) {
+                        queryParams.status = statusVal;
+                    }
+                    
+                    return queryParams;
+                },
+                pagination: true,
+                sidePagination: "server",
+                pageSize: 10,
+                pageList: [10, 20, 50, 100],
+                columns: [{
+                    field: 'rowNo',
+                    title: '行号',
+                    width: 60
+                },
+                {
+                    field: 'placeName',
+                    title: '所属场所名称',
+                    width: 150,
+                    formatter: function(value, row, index) {
+                        if (row.placeId) {
+                            return value + ' <span class="text-success"><i class="fa fa-check-circle"></i></span>';
+                        } else if (row.status == 'ERROR' && row.errorMsg && row.errorMsg.indexOf('场所') >= 0) {
+                            return value + ' <span class="text-danger"><i class="fa fa-times-circle"></i></span>';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'name',
+                    title: '姓名',
+                    width: 100
+                },
+                {
+                    field: 'religionType',
+                    title: '宗教类别',
+                    width: 100
+                },
+                {
+                    field: 'gender',
+                    title: '性别',
+                    width: 60
+                },
+                {
+                    field: 'idNumber',
+                    title: '身份证号',
+                    width: 150
+                },
+                {
+                    field: 'status',
+                    title: '状态',
+                    width: 80,
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        if (value == 'NORMAL') {
+                            return '<span class="badge badge-success">正常</span>';
+                        } else if (value == 'ERROR') {
+                            return '<span class="badge badge-danger">错误</span>';
+                        } else if (value == 'CONFLICT') {
+                            return '<span class="badge badge-warning">冲突</span>';
+                        }
+                        return value;
+                    }
+                },
+                {
+                    field: 'action',
+                    title: '处理动作',
+                    width: 100,
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        if (value == 'IMPORT') {
+                            return '<span class="label label-primary">导入</span>';
+                        } else if (value == 'OVERWRITE') {
+                            return '<span class="label label-warning">覆盖</span>';
+                        } else if (value == 'KEEP') {
+                            return '<span class="label label-info">保留</span>';
+                        } else if (value == 'ABANDON') {
+                            return '<span class="label label-default">放弃</span>';
+                        } else if (value == 'MANUAL') {
+                            return '<span class="label label-danger">待处理</span>';
+                        }
+                        return '-';
+                    }
+                },
+                {
+                    field: 'errorMsg',
+                    title: '错误/冲突信息',
+                    width: 200,
+                    formatter: function(value, row, index) {
+                        if (value) {
+                            return '<span class="text-danger">' + value + '</span>';
+                        }
+                        return '-';
+                    }
+                },
+                {
+                    title: '操作',
+                    align: 'center',
+                    width: 320,
+                    formatter: function(value, row, index) {
+                        if (row.status == 'NORMAL') {
+                            return '<span class="text-muted">无需操作</span>';
+                        }
+                        
+                        if (isFinished) {
+                            return '<span class="text-muted">已完成</span>';
+                        }
+                        
+                        var actions = [];
+                        actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" onclick="setAction(\'' + row.itemId + '\', \'OVERWRITE\')"><i class="fa fa-exchange"></i> 覆盖</a>');
+                        actions.push('<a class="btn btn-info btn-xs" href="javascript:void(0)" onclick="setAction(\'' + row.itemId + '\', \'KEEP\')"><i class="fa fa-shield"></i> 保留</a>');
+                        actions.push('<a class="btn btn-primary btn-xs" href="javascript:void(0)" onclick="openEditModal(\'' + row.itemId + '\')"><i class="fa fa-edit"></i> 手工</a>');
+                        actions.push('<a class="btn btn-default btn-xs" href="javascript:void(0)" onclick="setAction(\'' + row.itemId + '\', \'ABANDON\')"><i class="fa fa-trash"></i> 放弃</a>');
+                        return actions.join(' ');
+                    }
+                }]
+            };
+
+            if (previewTableInitialized) {
+                $("#preview-table").bootstrapTable('destroy');
+            }
+            $("#preview-table").bootstrapTable(options);
+            previewTableInitialized = true;
+            $("#previewArea").show();
+
+            // 绑定状态筛选事件
+            $("#statusFilter").off("change").on("change", function() {
+                $("#preview-table").bootstrapTable('refresh');
+            });
+        }
+
+        /**
+         * 设置处理动作
+         */
+        function setAction(itemId, action) {
+            var actionText = {
+                'OVERWRITE': '覆盖现有数据',
+                'KEEP': '保留现有数据',
+                'ABANDON': '放弃导入'
+            };
+
+            $.modal.confirm("确定要将此记录设置为【" + actionText[action] + "】吗?", function() {
+                $.ajax({
+                    url: prefix + "/item/" + itemId + "/action",
+                    type: "post",
+                    contentType: "application/json",
+                    data: JSON.stringify({ action: action }),
+                    success: function(result) {
+                        if (result.code == web_status.SUCCESS) {
+                            $.modal.msgSuccess("设置成功");
+                            $("#preview-table").bootstrapTable('refresh');
+                        } else {
+                            $.modal.alertError(result.msg);
+                        }
+                    }
+                });
+            });
+        }
+
+        /**
+         * 打开编辑弹框
+         */
+        function openEditModal(itemId) {
+            // 从表格中获取当前行数据
+            var row = $("#preview-table").bootstrapTable('getRowByUniqueId', itemId);
+            if (!row) {
+                // 如果没有uniqueId,尝试遍历查找
+                var allRows = $("#preview-table").bootstrapTable('getData');
+                for (var i = 0; i < allRows.length; i++) {
+                    if (allRows[i].itemId == itemId) {
+                        row = allRows[i];
+                        break;
+                    }
+                }
+            }
+
+            if (!row) {
+                $.modal.alertError("无法获取行数据");
+                return;
+            }
+
+            // 填充表单
+            $("#edit-itemId").val(itemId);
+            $("#edit-placeName").val(row.placeName || "");
+            $("#edit-name").val(row.name || "");
+            $("#edit-religionType").val(row.religionType || "");
+            $("#edit-gender").val(row.gender || "");
+            $("#edit-nation").val(row.nation || "");
+            $("#edit-idNumber").val(row.idNumber || "");
+            $("#edit-birthday").val(row.birthday || "");
+            $("#edit-county").val(row.county || "");
+            $("#edit-address").val(row.address || "");
+            $("#edit-politicalStatus").val(row.politicalStatus || "");
+            $("#edit-eduNational").val(row.eduNational || "");
+            $("#edit-eduReligious").val(row.eduReligious || "");
+            $("#edit-joinTime").val(row.joinTime || "");
+            $("#edit-phone").val(row.phone || "");
+            $("#edit-domicile").val(row.domicile || "");
+            $("#edit-nativePlace").val(row.nativePlace || "");
+            $("#edit-orgName").val(row.orgName || "");
+            $("#edit-orgAddress").val(row.orgAddress || "");
+
+            // 打开编辑弹框
+            $("#personImportEditModal").modal("show");
+        }
+
+        /**
+         * 保存并重新校验
+         */
+        function saveAndRevalidate() {
+            var itemId = $("#edit-itemId").val();
+            
+            // 收集表单数据
+            var editData = {
+                placeName: $("#edit-placeName").val(),
+                name: $("#edit-name").val(),
+                religionType: $("#edit-religionType").val(),
+                gender: $("#edit-gender").val(),
+                nation: $("#edit-nation").val(),
+                idNumber: $("#edit-idNumber").val(),
+                birthday: $("#edit-birthday").val(),
+                county: $("#edit-county").val(),
+                address: $("#edit-address").val(),
+                politicalStatus: $("#edit-politicalStatus").val(),
+                eduNational: $("#edit-eduNational").val(),
+                eduReligious: $("#edit-eduReligious").val(),
+                joinTime: $("#edit-joinTime").val(),
+                phone: $("#edit-phone").val(),
+                domicile: $("#edit-domicile").val(),
+                nativePlace: $("#edit-nativePlace").val(),
+                orgName: $("#edit-orgName").val(),
+                orgAddress: $("#edit-orgAddress").val()
+            };
+
+            $.ajax({
+                url: prefix + "/item/" + itemId + "/edit",
+                type: "post",
+                contentType: "application/json",
+                data: JSON.stringify(editData),
+                success: function(result) {
+                    if (result.code == web_status.SUCCESS) {
+                        $.modal.msgSuccess("保存成功");
+                        $("#personImportEditModal").modal("hide");
+                        $("#preview-table").bootstrapTable('refresh');
+                    } else {
+                        $.modal.alertError(result.msg);
+                    }
+                }
+            });
+        }
+
+        /**
+         * 提交导入
+         */
+        function commitImport() {
+            if (!currentBatchId) {
+                $.modal.alertError("批次ID不存在");
+                return;
+            }
+
+            $.modal.confirm("确定提交导入?异常/冲突记录将按所选处理方式执行,正常记录将直接导入。", function() {
+                // 先关闭详情对话框,再显示 loading
+                $("#personImportModal").modal("hide");
+                $.modal.loading("正在导入数据,请稍候...");
+                
+                $.ajax({
+                    url: prefix + "/batch/" + currentBatchId + "/commit",
+                    type: "post",
+                    success: function(result) {
+                        $.modal.closeLoading();
+                        if (result.code == web_status.SUCCESS) {
+                            var data = result.data;
+                            $.modal.msgSuccess("导入成功!共导入 " + data.importedCount + " 条记录,放弃 " + data.abandonedCount + " 条记录");
+                            // 刷新主页面批次列表
+                            $("#batch-table").bootstrapTable('refresh');
+                        } else {
+                            $.modal.alertError(result.msg);
+                        }
+                    },
+                    error: function() {
+                        $.modal.closeLoading();
+                        $.modal.alertError("导入失败,请稍后重试");
+                    }
+                });
+            });
+        }
+    </script>
+</body>
+</html>
+

+ 63 - 0
ruoyi-admin/src/main/resources/templates/religion/place/place.html

@@ -109,6 +109,69 @@
                     title: '详细地址',
                     visible: false
                 },
+                {
+                    field: 'placeArea',
+                    title: '占地面积(㎡)',
+                    visible: false
+                },
+                {
+                    field: 'buildingArea',
+                    title: '建筑面积(㎡)',
+                    visible: false
+                },
+                {
+                    field: 'approveOrgan',
+                    title: '批准设立机关',
+                    visible: false
+                },
+                {
+                    field: 'approveTime',
+                    title: '批准设立时间',
+                    visible: false
+                },
+                {
+                    field: 'regOrgan',
+                    title: '登记机关',
+                    visible: false
+                },
+                {
+                    field: 'regNo',
+                    title: '登记证号',
+                    visible: false
+                },
+                {
+                    field: 'regTime',
+                    title: '登记时间',
+                    visible: false
+                },
+                {
+                    field: 'legalQualified',
+                    title: '是否具有法人资格',
+                    visible: false,
+                    formatter: function(value, row, index) {
+                        return value == 1 ? '是' : '否';
+                    }
+                },
+                {
+                    field: 'legalTime',
+                    title: '法人资格取得时间',
+                    visible: false
+                },
+                {
+                    field: 'creditCode',
+                    title: '统一社会信用代码',
+                    visible: false
+                },
+                {
+                    field: 'longitude',
+                    title: '经度',
+                    visible: false
+                },
+                {
+                    field: 'latitude',
+                    title: '纬度',
+                    visible: false
+                },
                 {
                     field: 'createTime',
                     title: '创建时间',

+ 207 - 94
ruoyi-admin/src/main/resources/templates/religion/religionPerson/add.html

@@ -7,151 +7,264 @@
 <body class="white-bg">
 <div class="wrapper wrapper-content animated fadeInRight ibox-content">
     <form class="form-horizontal m" id="form-religionPerson-add">
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label is-required">所属场所ID:</label>
-                <div class="col-sm-8">
-                    <input name="placeId" class="form-control" type="text" required>
+        <!-- 基本信息 -->
+        <h4 class="form-header h4">基本信息</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">姓名:</label>
+                    <div class="col-sm-8">
+                        <input name="name" class="form-control" type="text" required maxlength="128" placeholder="请输入姓名">
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label is-required">姓名:</label>
-                <div class="col-sm-8">
-                    <input name="name" class="form-control" type="text" required>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">身份证号:</label>
+                    <div class="col-sm-8">
+                        <input name="idNumber" class="form-control" type="text" required maxlength="32" placeholder="请输入身份证号">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">性别:</label>
-                <div class="col-sm-8">
-                    <input name="gender" class="form-control" type="text">
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">宗教类别:</label>
+                    <div class="col-sm-8">
+                        <select name="religionType" class="form-control" required th:with="type=${@dict.getType('religion_type')}">
+                            <option value="">请选择宗教类别</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label is-required">身份证号:</label>
-                <div class="col-sm-8">
-                    <input name="idNumber" class="form-control" type="text" required>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">性别:</label>
+                    <div class="col-sm-8">
+                        <select name="gender" class="form-control">
+                            <option value="">请选择性别</option>
+                            <option value="男">男</option>
+                            <option value="女">女</option>
+                        </select>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">出生日期:</label>
-                <div class="col-sm-8">
-                    <div class="input-group date">
-                        <input name="birthday" class="form-control" placeholder="yyyy-MM-dd" type="text">
-                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">民族:</label>
+                    <div class="col-sm-8">
+                        <select name="nation" class="form-control" th:with="type=${@dict.getType('sys_nation')}">
+                            <option value="">请选择民族</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                        </select>
                     </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">所属区县:</label>
-                <div class="col-sm-8">
-                    <input name="county" class="form-control" type="text">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">出生日期:</label>
+                    <div class="col-sm-8">
+                        <div class="input-group date">
+                            <input name="birthday" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                            <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                        </div>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">家庭地址/联系地址:</label>
-                <div class="col-sm-8">
-                    <textarea name="address" class="form-control"></textarea>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">联系电话:</label>
+                    <div class="col-sm-8">
+                        <input name="phone" class="form-control" type="text" maxlength="32" placeholder="请输入联系电话">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">国民教育学历:</label>
-                <div class="col-sm-8">
-                    <input name="eduNational" class="form-control" type="text">
+
+        <!-- 所属信息 -->
+        <h4 class="form-header h4">所属信息</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">所属场所:</label>
+                    <div class="col-sm-8">
+                        <select name="placeId" class="form-control" required>
+                            <option value="">请选择所属场所</option>
+                            <option th:each="place : ${placeList}" th:value="${place.placeId}" th:text="${place.placeName}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">宗教学历:</label>
-                <div class="col-sm-8">
-                    <input name="eduReligious" class="form-control" type="text">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">所属区县:</label>
+                    <div class="col-sm-8">
+                        <select name="county" class="form-control" th:with="type=${@dict.getType('region_xa_district')}">
+                            <option value="">请选择区县</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">入教/出家时间:</label>
-                <div class="col-sm-8">
-                    <div class="input-group date">
-                        <input name="joinTime" class="form-control" placeholder="yyyy-MM-dd" type="text">
-                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">家庭地址:</label>
+                    <div class="col-sm-10">
+                        <textarea name="address" class="form-control" maxlength="512" placeholder="请输入家庭地址/联系地址"></textarea>
                     </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">联系电话:</label>
-                <div class="col-sm-8">
-                    <input name="phone" class="form-control" type="text">
+
+        <!-- 政治与学历 -->
+        <h4 class="form-header h4">政治与学历</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">政治面貌:</label>
+                    <div class="col-sm-8">
+                        <select name="politicalStatus" class="form-control" th:with="type=${@dict.getType('political_status')}">
+                            <option value="">请选择政治面貌</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">户籍所在地:</label>
-                <div class="col-sm-8">
-                    <input name="domicile" class="form-control" type="text">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">国民教育学历:</label>
+                    <div class="col-sm-8">
+                        <select name="eduNational" class="form-control" th:with="type=${@dict.getType('edu_national')}">
+                            <option value="">请选择学历</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">籍贯:</label>
-                <div class="col-sm-8">
-                    <input name="nativePlace" class="form-control" type="text">
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">宗教学历:</label>
+                    <div class="col-sm-8">
+                        <select name="eduReligious" class="form-control" th:with="type=${@dict.getType('edu_religious')}">
+                            <option value="">请选择宗教学历</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                        </select>
+                    </div>
+                </div>
+            </div>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">入教/出家时间:</label>
+                    <div class="col-sm-8">
+                        <div class="input-group date">
+                            <input name="joinTime" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                            <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">所属宫观名称:</label>
-                <div class="col-sm-8">
-                    <input name="orgName" class="form-control" type="text">
+
+        <!-- 户籍信息 -->
+        <h4 class="form-header h4">户籍信息</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">户籍所在地:</label>
+                    <div class="col-sm-8">
+                        <input name="domicile" class="form-control" type="text" maxlength="128" placeholder="请输入户籍所在地">
+                    </div>
+                </div>
+            </div>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">籍贯:</label>
+                    <div class="col-sm-8">
+                        <input name="nativePlace" class="form-control" type="text" maxlength="128" placeholder="请输入籍贯">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">所属宫观地址:</label>
-                <div class="col-sm-8">
-                    <textarea name="orgAddress" class="form-control"></textarea>
+
+        <!-- 宫观信息(留档) -->
+        <h4 class="form-header h4">宫观信息(留档)</h4>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">所属宫观名称:</label>
+                    <div class="col-sm-10">
+                        <input name="orgName" class="form-control" type="text" maxlength="256" placeholder="请输入所属宫观名称(Excel原始值)">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报:</label>
-                <div class="col-sm-8">
-                    <div class="radio-box" th:each="dict : ${@dict.getType('religion_person_status')}">
-                        <input type="radio" th:id="${'status_' + dict.dictCode}" name="status" th:value="${dict.dictValue}" th:checked="${dict.default}">
-                        <label th:for="${'status_' + dict.dictCode}" th:text="${dict.dictLabel}"></label>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">所属宫观地址:</label>
+                    <div class="col-sm-10">
+                        <textarea name="orgAddress" class="form-control" maxlength="512" placeholder="请输入所属宫观地址(Excel原始值)"></textarea>
                     </div>
                 </div>
             </div>
         </div>
+
     </form>
 </div>
 <th:block th:include="include :: footer" />
 <th:block th:include="include :: datetimepicker-js" />
 <script th:inline="javascript">
-    var prefix = ctx + "religion/religionPerson"
+    var prefix = ctx + "religion/religionPerson";
+    
     $("#form-religionPerson-add").validate({
-        focusCleanup: true
+        focusCleanup: true,
+        rules: {
+            name: {
+                required: true,
+                minlength: 2,
+                maxlength: 128
+            },
+            idNumber: {
+                required: true,
+                minlength: 15,
+                maxlength: 32
+            },
+            religionType: {
+                required: true
+            },
+            placeId: {
+                required: true
+            },
+            phone: {
+                isPhone: true
+            }
+        },
+        messages: {
+            name: {
+                required: "请输入姓名",
+                minlength: "姓名长度不能少于2个字符",
+                maxlength: "姓名长度不能超过128个字符"
+            },
+            idNumber: {
+                required: "请输入身份证号",
+                minlength: "身份证号长度不正确",
+                maxlength: "身份证号长度不正确"
+            },
+            religionType: {
+                required: "请选择宗教类别"
+            },
+            placeId: {
+                required: "请选择所属场所"
+            }
+        }
     });
 
     function submitHandler() {

+ 207 - 93
ruoyi-admin/src/main/resources/templates/religion/religionPerson/edit.html

@@ -8,151 +8,265 @@
 <div class="wrapper wrapper-content animated fadeInRight ibox-content">
     <form class="form-horizontal m" id="form-religionPerson-edit" th:object="${religionPerson}">
         <input name="personId" th:field="*{personId}" type="hidden">
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label is-required">所属场所ID:</label>
-                <div class="col-sm-8">
-                    <input name="placeId" th:field="*{placeId}" class="form-control" type="text" required>
+        
+        <!-- 基本信息 -->
+        <h4 class="form-header h4">基本信息</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">姓名:</label>
+                    <div class="col-sm-8">
+                        <input name="name" th:field="*{name}" class="form-control" type="text" required maxlength="128" placeholder="请输入姓名">
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label is-required">姓名:</label>
-                <div class="col-sm-8">
-                    <input name="name" th:field="*{name}" class="form-control" type="text" required>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">身份证号:</label>
+                    <div class="col-sm-8">
+                        <input name="idNumber" th:field="*{idNumber}" class="form-control" type="text" required maxlength="32" placeholder="请输入身份证号">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">性别:</label>
-                <div class="col-sm-8">
-                    <input name="gender" th:field="*{gender}" class="form-control" type="text">
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">宗教类别:</label>
+                    <div class="col-sm-8">
+                        <select name="religionType" class="form-control" required th:with="type=${@dict.getType('religion_type')}">
+                            <option value="">请选择宗教类别</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{religionType}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label is-required">身份证号:</label>
-                <div class="col-sm-8">
-                    <input name="idNumber" th:field="*{idNumber}" class="form-control" type="text" required>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">性别:</label>
+                    <div class="col-sm-8">
+                        <select name="gender" class="form-control" th:with="value=${religionPerson.gender}">
+                            <option value="">请选择性别</option>
+                            <option value="男" th:selected="${value == '男'}">男</option>
+                            <option value="女" th:selected="${value == '女'}">女</option>
+                        </select>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">出生日期:</label>
-                <div class="col-sm-8">
-                    <div class="input-group date">
-                        <input name="birthday" th:value="${#dates.format(religionPerson.birthday, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
-                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">民族:</label>
+                    <div class="col-sm-8">
+                        <select name="nation" class="form-control" th:with="type=${@dict.getType('sys_nation')}">
+                            <option value="">请选择民族</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:selected="${dict.dictValue == religionPerson.nation}"></option>
+                        </select>
                     </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">所属区县:</label>
-                <div class="col-sm-8">
-                    <input name="county" th:field="*{county}" class="form-control" type="text">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">出生日期:</label>
+                    <div class="col-sm-8">
+                        <div class="input-group date">
+                            <input name="birthday" th:value="${#dates.format(religionPerson.birthday, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                            <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                        </div>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">家庭地址/联系地址:</label>
-                <div class="col-sm-8">
-                    <textarea name="address" class="form-control">[[*{address}]]</textarea>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">联系电话:</label>
+                    <div class="col-sm-8">
+                        <input name="phone" th:field="*{phone}" class="form-control" type="text" maxlength="32" placeholder="请输入联系电话">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">国民教育学历:</label>
-                <div class="col-sm-8">
-                    <input name="eduNational" th:field="*{eduNational}" class="form-control" type="text">
+
+        <!-- 所属信息 -->
+        <h4 class="form-header h4">所属信息</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label is-required">所属场所:</label>
+                    <div class="col-sm-8">
+                        <select name="placeId" class="form-control" required>
+                            <option value="">请选择所属场所</option>
+                            <option th:each="place : ${placeList}" th:value="${place.placeId}" th:text="${place.placeName}" th:selected="${place.placeId == religionPerson.placeId}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">宗教学历:</label>
-                <div class="col-sm-8">
-                    <input name="eduReligious" th:field="*{eduReligious}" class="form-control" type="text">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">所属区县:</label>
+                    <div class="col-sm-8">
+                        <select name="county" class="form-control" th:with="type=${@dict.getType('region_xa_district')}">
+                            <option value="">请选择区县</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:selected="${dict.dictValue == religionPerson.county}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">入教/出家时间:</label>
-                <div class="col-sm-8">
-                    <div class="input-group date">
-                        <input name="joinTime" th:value="${#dates.format(religionPerson.joinTime, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
-                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">家庭地址:</label>
+                    <div class="col-sm-10">
+                        <textarea name="address" th:field="*{address}" class="form-control" maxlength="512" placeholder="请输入家庭地址/联系地址"></textarea>
                     </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">联系电话:</label>
-                <div class="col-sm-8">
-                    <input name="phone" th:field="*{phone}" class="form-control" type="text">
+
+        <!-- 政治与学历 -->
+        <h4 class="form-header h4">政治与学历</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">政治面貌:</label>
+                    <div class="col-sm-8">
+                        <select name="politicalStatus" class="form-control" th:with="type=${@dict.getType('political_status')}">
+                            <option value="">请选择政治面貌</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{politicalStatus}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">户籍所在地:</label>
-                <div class="col-sm-8">
-                    <input name="domicile" th:field="*{domicile}" class="form-control" type="text">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">国民教育学历:</label>
+                    <div class="col-sm-8">
+                        <select name="eduNational" class="form-control" th:with="type=${@dict.getType('edu_national')}">
+                            <option value="">请选择学历</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{eduNational}"></option>
+                        </select>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">籍贯:</label>
-                <div class="col-sm-8">
-                    <input name="nativePlace" th:field="*{nativePlace}" class="form-control" type="text">
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">宗教学历:</label>
+                    <div class="col-sm-8">
+                        <select name="eduReligious" class="form-control" th:with="type=${@dict.getType('edu_religious')}">
+                            <option value="">请选择宗教学历</option>
+                            <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{eduReligious}"></option>
+                        </select>
+                    </div>
+                </div>
+            </div>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">入教/出家时间:</label>
+                    <div class="col-sm-8">
+                        <div class="input-group date">
+                            <input name="joinTime" th:value="${#dates.format(religionPerson.joinTime, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                            <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">所属宫观名称:</label>
-                <div class="col-sm-8">
-                    <input name="orgName" th:field="*{orgName}" class="form-control" type="text">
+
+        <!-- 户籍信息 -->
+        <h4 class="form-header h4">户籍信息</h4>
+        <div class="row">
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">户籍所在地:</label>
+                    <div class="col-sm-8">
+                        <input name="domicile" th:field="*{domicile}" class="form-control" type="text" maxlength="128" placeholder="请输入户籍所在地">
+                    </div>
+                </div>
+            </div>
+            <div class="col-sm-6">
+                <div class="form-group">
+                    <label class="col-sm-4 control-label">籍贯:</label>
+                    <div class="col-sm-8">
+                        <input name="nativePlace" th:field="*{nativePlace}" class="form-control" type="text" maxlength="128" placeholder="请输入籍贯">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">所属宫观地址:</label>
-                <div class="col-sm-8">
-                    <textarea name="orgAddress" class="form-control">[[*{orgAddress}]]</textarea>
+
+        <!-- 宫观信息(留档) -->
+        <h4 class="form-header h4">宫观信息(留档)</h4>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">所属宫观名称:</label>
+                    <div class="col-sm-10">
+                        <input name="orgName" th:field="*{orgName}" class="form-control" type="text" maxlength="256" placeholder="请输入所属宫观名称(Excel原始值)">
+                    </div>
                 </div>
             </div>
         </div>
-        <div class="col-xs-12">
-            <div class="form-group">
-                <label class="col-sm-3 control-label">状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报:</label>
-                <div class="col-sm-8">
-                    <div class="radio-box" th:each="dict : ${@dict.getType('religion_person_status')}">
-                        <input type="radio" th:id="${'status_' + dict.dictCode}" name="status" th:value="${dict.dictValue}" th:field="*{status}">
-                        <label th:for="${'status_' + dict.dictCode}" th:text="${dict.dictLabel}"></label>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">所属宫观地址:</label>
+                    <div class="col-sm-10">
+                        <textarea name="orgAddress" th:field="*{orgAddress}" class="form-control" maxlength="512" placeholder="请输入所属宫观地址(Excel原始值)"></textarea>
                     </div>
                 </div>
             </div>
         </div>
+
     </form>
 </div>
 <th:block th:include="include :: footer" />
 <th:block th:include="include :: datetimepicker-js" />
 <script th:inline="javascript">
     var prefix = ctx + "religion/religionPerson";
+    
     $("#form-religionPerson-edit").validate({
-        focusCleanup: true
+        focusCleanup: true,
+        rules: {
+            name: {
+                required: true,
+                minlength: 2,
+                maxlength: 128
+            },
+            idNumber: {
+                required: true,
+                minlength: 15,
+                maxlength: 32
+            },
+            religionType: {
+                required: true
+            },
+            placeId: {
+                required: true
+            },
+            phone: {
+                isPhone: true
+            }
+        },
+        messages: {
+            name: {
+                required: "请输入姓名",
+                minlength: "姓名长度不能少于2个字符",
+                maxlength: "姓名长度不能超过128个字符"
+            },
+            idNumber: {
+                required: "请输入身份证号",
+                minlength: "身份证号长度不正确",
+                maxlength: "身份证号长度不正确"
+            },
+            religionType: {
+                required: "请选择宗教类别"
+            },
+            placeId: {
+                required: "请选择所属场所"
+            }
+        }
     });
 
     function submitHandler() {

+ 532 - 0
ruoyi-admin/src/main/resources/templates/religion/religionPerson/personCheck.html

@@ -0,0 +1,532 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('区县校核工作台(教职人员)')" />
+</head>
+<body class="gray-bg">
+<div class="container-div">
+    <div class="row">
+        <div class="col-sm-12 search-collapse">
+            <form id="formId">
+                <div class="select-list">
+                    <ul>
+                        <li>
+                            <label>宗教类别:</label>
+                            <select name="religionType" th:with="type=${@dict.getType('religion_type')}">
+                                <option value="">所有</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                            </select>
+                        </li>
+                        <li>
+                            <label>所属场所:</label>
+                            <select id="placeId" name="placeId" class="form-control">
+                                <option value="">所有场所</option>
+                                <option th:each="place : ${placeList}" th:value="${place.placeId}" th:text="${place.placeName}"></option>
+                            </select>
+                        </li>
+                        <li>
+                            <label>姓名:</label>
+                            <input type="text" name="name" placeholder="请输入姓名"/>
+                        </li>
+                        <li>
+                            <label>身份证号:</label>
+                            <input type="text" name="idNumber" placeholder="请输入身份证号"/>
+                        </li>
+                        <li>
+                            <label>状态:</label>
+                            <select name="status">
+                                <option value="TO_CHECK" selected>待校核</option>
+                                <option value="CHECKED">已校核待上报</option>
+                            </select>
+                        </li>
+                        <li>
+                            <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
+                            <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
+                        </li>
+                    </ul>
+                </div>
+            </form>
+        </div>
+
+        <div class="btn-group-sm" id="toolbar" role="group">
+            <a class="btn btn-success multiple disabled" onclick="batchPass()" shiro:hasPermission="religion:religionPerson:check">
+                <i class="fa fa-check"></i> 批量通过校核
+            </a>
+        </div>
+        
+        <div class="col-sm-12 select-table table-striped">
+            <table id="bootstrap-table"></table>
+        </div>
+    </div>
+</div>
+
+<!-- 单条校核弹框 -->
+<div class="modal fade" id="checkModal" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog" style="width: 900px;">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+                <h4 class="modal-title">教职人员校核</h4>
+            </div>
+            <div class="modal-body" style="max-height: 600px; overflow-y: auto;">
+                <!-- 人员详情 -->
+                <div class="panel panel-info">
+                    <div class="panel-heading">
+                        <h3 class="panel-title"><i class="fa fa-user"></i> 人员详情</h3>
+                    </div>
+                    <div class="panel-body">
+                        <form class="form-horizontal">
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">姓名:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_name"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">性别:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_gender"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">民族:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_nation"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">宗教类别:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_religionType"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">身份证号:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_idNumber"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <!-- 空列,保持布局对称 -->
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">所属场所:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_placeName"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">所属区县:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_county"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">政治面貌:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_politicalStatus"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">联系电话:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_phone"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">国民教育学历:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_eduNational"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">宗教学历:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_eduReligious"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">家庭地址:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_address"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <!-- 空列,保持布局对称 -->
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">户籍所在地:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_domicile"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">籍贯:</label>
+                                        <div class="col-sm-7">
+                                            <p class="form-control-static" id="detail_nativePlace"></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+
+                <!-- 校核操作区 -->
+                <div class="panel panel-warning">
+                    <div class="panel-heading">
+                        <h3 class="panel-title"><i class="fa fa-check-square-o"></i> 校核操作</h3>
+                    </div>
+                    <div class="panel-body">
+                        <form class="form-horizontal">
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-5 control-label text-right">校核结果:</label>
+                                        <div class="col-sm-7">
+                                            <label class="radio-inline">
+                                                <input type="radio" name="checkAction" value="PASS" checked> 通过
+                                            </label>
+                                            <label class="radio-inline">
+                                                <input type="radio" name="checkAction" value="REJECT"> 驳回
+                                            </label>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <!-- 空列,保持布局对称 -->
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-12">
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label text-right" style="padding-left: 15px;">校核意见:</label>
+                                        <div class="col-sm-10">
+                                            <textarea id="checkComment" class="form-control" rows="3" placeholder="请输入校核意见(驳回时必填)"></textarea>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-primary" onclick="submitCheck()">提交</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<th:block th:include="include :: footer" />
+<script th:inline="javascript">
+    var prefix = ctx + "religion/person/check";
+    var checkFlag = [[${@permission.hasPermi('religion:religionPerson:check')}]];
+    var currentPersonId = null;
+
+    $(function() {
+        var options = {
+            url: prefix + "/list",
+            createUrl: prefix + "/add",
+            updateUrl: prefix + "/edit/{id}",
+            removeUrl: prefix + "/remove",
+            exportUrl: prefix + "/export",
+            modalName: "教职人员",
+            uniqueId: "personId",
+            showColumns: true,
+            showRefresh: true,
+            showToggle: true,
+            columns: [{
+                checkbox: true
+            },
+            {
+                field: 'personId',
+                title: '人员ID',
+                visible: false
+            },
+            {
+                field: 'name',
+                title: '姓名',
+                sortable: true
+            },
+            {
+                field: 'gender',
+                title: '性别',
+                sortable: true,
+                visible: false
+            },
+            {
+                field: 'nation',
+                title: '民族',
+                sortable: true,
+                visible: false
+            },
+            {
+                field: 'religionType',
+                title: '宗教类别',
+                sortable: true
+            },
+            {
+                field: 'idNumber',
+                title: '身份证号',
+                sortable: true
+            },
+            {
+                field: 'county',
+                title: '所属区县',
+                sortable: true
+            },
+            {
+                field: 'placeName',
+                title: '所属场所',
+                sortable: true
+            },
+            {
+                field: 'address',
+                title: '家庭地址',
+                visible: false
+            },
+            {
+                field: 'politicalStatus',
+                title: '政治面貌',
+                visible: false
+            },
+            {
+                field: 'eduNational',
+                title: '国民教育学历',
+                visible: false
+            },
+            {
+                field: 'eduReligious',
+                title: '宗教学历',
+                visible: false
+            },
+            {
+                field: 'joinTime',
+                title: '入教时间',
+                visible: false
+            },
+            {
+                field: 'phone',
+                title: '联系电话',
+                visible: false
+            },
+            {
+                field: 'domicile',
+                title: '户籍所在地',
+                visible: false
+            },
+            {
+                field: 'nativePlace',
+                title: '籍贯',
+                visible: false
+            },
+            {
+                field: 'status',
+                title: '状态',
+                sortable: true,
+                formatter: function(value, row, index) {
+                    if (value == 'TO_CHECK') {
+                        return '<span class="badge badge-warning">待校核</span>';
+                    } else if (value == 'CHECKED') {
+                        return '<span class="badge badge-success">已校核待上报</span>';
+                    } else if (value == 'REPORTED') {
+                        return '<span class="badge badge-info">已上报</span>';
+                    }
+                    return value;
+                }
+            },
+            {
+                field: 'countyCheckBy',
+                title: '校核人',
+                sortable: true,
+                visible: false
+            },
+            {
+                field: 'countyCheckTime',
+                title: '校核时间',
+                sortable: true,
+                visible: false
+            },
+            {
+                title: '操作',
+                align: 'center',
+                formatter: function(value, row, index) {
+                    var actions = [];
+                    if (row.status == 'TO_CHECK') {
+                        actions.push('<a class="btn btn-primary btn-xs ' + checkFlag + '" href="javascript:void(0)" onclick="openCheckModal(\'' + row.personId + '\')"><i class="fa fa-check-square-o"></i>校核</a> ');
+                    }
+                    return actions.join('');
+                }
+            }]
+        };
+        $.table.init(options);
+    });
+
+    /* 打开校核弹框 */
+    function openCheckModal(personId) {
+        currentPersonId = personId;
+        
+        // 从表格中获取所有数据,查找对应的行
+        var tableData = $("#bootstrap-table").bootstrapTable('getData');
+        var row = null;
+        for (var i = 0; i < tableData.length; i++) {
+            if (tableData[i].personId == personId) {
+                row = tableData[i];
+                break;
+            }
+        }
+        
+        if (row) {
+            // 填充详情
+            $("#detail_name").text(row.name || '-');
+            $("#detail_gender").text(row.gender || '-');
+            $("#detail_nation").text(row.nation || '-');
+            $("#detail_religionType").text(row.religionType || '-');
+            $("#detail_idNumber").text(row.idNumber || '-');
+            $("#detail_placeName").text(row.placeName || '-');
+            $("#detail_county").text(row.county || '-');
+            $("#detail_politicalStatus").text(row.politicalStatus || '-');
+            $("#detail_phone").text(row.phone || '-');
+            $("#detail_eduNational").text(row.eduNational || '-');
+            $("#detail_eduReligious").text(row.eduReligious || '-');
+            $("#detail_address").text(row.address || '-');
+            $("#detail_domicile").text(row.domicile || '-');
+            $("#detail_nativePlace").text(row.nativePlace || '-');
+            
+            // 重置校核表单
+            $("input[name='checkAction'][value='PASS']").prop('checked', true);
+            $("#checkComment").val('');
+            
+            // 打开弹框
+            $("#checkModal").modal('show');
+        } else {
+            $.modal.alertWarning("未找到该人员信息");
+        }
+    }
+
+    /* 提交校核 */
+    function submitCheck() {
+        var action = $("input[name='checkAction']:checked").val();
+        var comment = $("#checkComment").val().trim();
+        
+        // 如果是驳回,校核意见必填
+        if (action == 'REJECT' && comment == '') {
+            $.modal.alertWarning("驳回时必须填写驳回原因");
+            return;
+        }
+        
+        var url = action == 'PASS' ? prefix + "/pass" : prefix + "/reject";
+        var data = {
+            personId: currentPersonId,
+            comment: comment
+        };
+        
+        // 先关闭对话框,再显示 loading
+        $("#checkModal").modal('hide');
+        $.modal.loading("正在提交...");
+        
+        $.ajax({
+            type: "POST",
+            url: url,
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(result) {
+                $.modal.closeLoading();
+                if (result.code == web_status.SUCCESS) {
+                    $.modal.msgSuccess(result.msg);
+                    $.table.refresh();
+                } else {
+                    $.modal.alertError(result.msg);
+                }
+            },
+            error: function() {
+                $.modal.closeLoading();
+                $.modal.alertError("操作失败");
+            }
+        });
+    }
+
+    /* 批量通过校核 */
+    function batchPass() {
+        var rows = $.table.selectColumns("personId");
+        if (rows.length == 0) {
+            $.modal.alertWarning("请至少选择一条记录");
+            return;
+        }
+        
+        $.modal.confirm("确定对选中的 " + rows.length + " 条记录执行通过区县校核?", function() {
+            var data = {
+                personIds: rows.join(","),
+                comment: "" // 可选,可以弹出输入框让用户填写统一备注
+            };
+            
+            $.modal.loading("正在处理...");
+            $.ajax({
+                type: "POST",
+                url: prefix + "/batchPass",
+                contentType: "application/json",
+                data: JSON.stringify(data),
+                success: function(result) {
+                    $.modal.closeLoading();
+                    if (result.code == web_status.SUCCESS) {
+                        $.modal.msgSuccess(result.msg);
+                        $.table.refresh();
+                    } else {
+                        $.modal.alertError(result.msg);
+                    }
+                },
+                error: function() {
+                    $.modal.closeLoading();
+                    $.modal.alertError("操作失败");
+                }
+            });
+        });
+    }
+</script>
+</body>
+</html>
+

+ 770 - 55
ruoyi-admin/src/main/resources/templates/religion/religionPerson/religionPerson.html

@@ -2,6 +2,7 @@
 <html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
 <head>
     <th:block th:include="include :: header('教职人员列表')" />
+    <th:block th:include="include :: datetimepicker-css" />
 </head>
 <body class="gray-bg">
 <div class="container-div">
@@ -11,60 +12,48 @@
                 <div class="select-list">
                     <ul>
                         <li>
-                            <label>所属场所ID:</label>
-                            <input type="text" name="placeId"/>
+                            <label>所属场所:</label>
+                            <select id="placeId" name="placeId" class="form-control">
+                                <option value="">所有场所</option>
+                                <option th:each="place : ${placeList}" th:value="${place.placeId}" th:text="${place.placeName}"></option>
+                            </select>
                         </li>
                         <li>
                             <label>姓名:</label>
-                            <input type="text" name="name"/>
-                        </li>
-                        <li>
-                            <label>性别:</label>
-                            <input type="text" name="gender"/>
+                            <input type="text" name="name" placeholder="请输入姓名"/>
                         </li>
                         <li>
                             <label>身份证号:</label>
-                            <input type="text" name="idNumber"/>
+                            <input type="text" name="idNumber" placeholder="请输入身份证号"/>
                         </li>
                         <li>
-                            <label>出生日期:</label>
-                            <input type="text" class="time-input" placeholder="请选择出生日期" name="birthday"/>
+                            <label>宗教类别:</label>
+                            <select name="religionType" th:with="type=${@dict.getType('religion_type')}">
+                                <option value="">所有</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                            </select>
                         </li>
                         <li>
                             <label>所属区县:</label>
-                            <input type="text" name="county"/>
-                        </li>
-                        <li>
-                            <label>国民教育学历:</label>
-                            <input type="text" name="eduNational"/>
-                        </li>
-                        <li>
-                            <label>宗教学历:</label>
-                            <input type="text" name="eduReligious"/>
+                            <select name="county" class="form-control" th:with="type=${@dict.getType('region_xa_district')}">
+                                <option value="">所有区县</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                            </select>
                         </li>
                         <li>
-                            <label>入教/出家时间:</label>
-                            <input type="text" class="time-input" placeholder="请选择入教/出家时间" name="joinTime"/>
+                            <label>民族:</label>
+                            <select name="nation" class="form-control" th:with="type=${@dict.getType('sys_nation')}">
+                                <option value="">所有民族</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                            </select>
                         </li>
                         <li>
                             <label>联系电话:</label>
-                            <input type="text" name="phone"/>
-                        </li>
-                        <li>
-                            <label>户籍所在地:</label>
-                            <input type="text" name="domicile"/>
-                        </li>
-                        <li>
-                            <label>籍贯:</label>
-                            <input type="text" name="nativePlace"/>
+                            <input type="text" name="phone" placeholder="请输入联系电话"/>
                         </li>
                         <li>
-                            <label>所属宫观名称:</label>
-                            <input type="text" name="orgName"/>
-                        </li>
-                        <li>
-                            <label>状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报:</label>
-                            <select name="status" th:with="type=${@dict.getType('religion_person_status')}">
+                            <label>状态:</label>
+                            <select name="status" th:with="type=${@dict.getType('person_status')}">
                                 <option value="">所有</option>
                                 <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
                             </select>
@@ -85,6 +74,9 @@
             <a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="religion:religionPerson:edit">
                 <i class="fa fa-edit"></i> 修改
             </a>
+            <a class="btn btn-info" onclick="openBatchUpdateModal()" shiro:hasPermission="religion:religionPerson:edit">
+                <i class="fa fa-edit"></i> 批量维护
+            </a>
             <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="religion:religionPerson:remove">
                 <i class="fa fa-remove"></i> 删除
             </a>
@@ -97,11 +89,413 @@
         </div>
     </div>
 </div>
+
+<!-- 批量维护模态框 -->
+<div class="modal fade" id="personBatchModal" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog" style="width: 1000px;">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+                <h4 class="modal-title">批量维护教职人员</h4>
+            </div>
+            <div class="modal-body" style="max-height: 650px; overflow-y: auto;">
+                <!-- 条件筛选区 -->
+                <div class="panel panel-default">
+                    <div class="panel-heading">
+                        <h3 class="panel-title">条件筛选(指定批量更新范围)</h3>
+                    </div>
+                    <div class="panel-body">
+                        <form id="batchConditionForm" class="form-horizontal">
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">姓名:</label>
+                                        <div class="col-sm-8">
+                                            <input type="text" id="cond_name" class="form-control" placeholder="请输入姓名">
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">身份证号:</label>
+                                        <div class="col-sm-8">
+                                            <input type="text" id="cond_idNumber" class="form-control" placeholder="请输入身份证号">
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">所属场所:</label>
+                                        <div class="col-sm-8">
+                                            <select id="cond_placeId" class="form-control">
+                                                <option value="">所有场所</option>
+                                                <option th:each="place : ${placeList}" th:value="${place.placeId}" th:text="${place.placeName}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">宗教类别:</label>
+                                        <div class="col-sm-8">
+                                            <select id="cond_religionType" class="form-control" th:with="type=${@dict.getType('religion_type')}">
+                                                <option value="">所有</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">性别:</label>
+                                        <div class="col-sm-8">
+                                            <select id="cond_gender" class="form-control">
+                                                <option value="">所有</option>
+                                                <option value="男">男</option>
+                                                <option value="女">女</option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">民族:</label>
+                                        <div class="col-sm-8">
+                                            <select id="cond_nation" class="form-control" th:with="type=${@dict.getType('sys_nation')}">
+                                                <option value="">所有民族</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">所属区县:</label>
+                                        <div class="col-sm-8">
+                                            <select id="cond_county" class="form-control" th:with="type=${@dict.getType('region_xa_district')}">
+                                                <option value="">所有区县</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">状态:</label>
+                                        <div class="col-sm-8">
+                                            <select id="cond_status" class="form-control" th:with="type=${@dict.getType('person_status')}">
+                                                <option value="">所有</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </form>
+                        <div class="row">
+                            <div class="col-sm-12 text-right">
+                                <button type="button" class="btn btn-sm btn-info" onclick="queryPreview()">
+                                    <i class="fa fa-search"></i> 查询
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <!-- 查询结果预览区 -->
+                <div class="panel panel-default" id="previewPanel" style="display: none;">
+                    <div class="panel-heading">
+                        <h3 class="panel-title">查询结果预览</h3>
+                    </div>
+                    <div class="panel-body">
+                        <div class="alert alert-info" style="margin-bottom: 10px;">
+                            <i class="fa fa-info-circle"></i> 共 <span id="batchTotalCount" style="font-weight: bold; color: #d9534f;">0</span> 条记录,将作为本次批量修改的对象
+                        </div>
+                        <div>
+                            <table id="previewTable"></table>
+                        </div>
+                    </div>
+                </div>
+
+                <!-- 批量更新字段区 -->
+                <div class="panel panel-default">
+                    <div class="panel-heading">
+                        <h3 class="panel-title">批量更新字段(勾选需要修改的字段)</h3>
+                    </div>
+                    <div class="panel-body">
+                        <form id="batchUpdateForm" class="form-horizontal">
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newPlaceId"> 所属场所:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_placeId" class="form-control" disabled>
+                                                <option value="">请选择所属场所</option>
+                                                <option th:each="place : ${placeList}" th:value="${place.placeId}" th:text="${place.placeName}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newReligionType"> 宗教类别:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_religionType" class="form-control" disabled th:with="type=${@dict.getType('religion_type')}">
+                                                <option value="">请选择宗教类别</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newGender"> 性别:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_gender" class="form-control" disabled>
+                                                <option value="">请选择性别</option>
+                                                <option value="男">男</option>
+                                                <option value="女">女</option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newNation"> 民族:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_nation" class="form-control" disabled th:with="type=${@dict.getType('sys_nation')}">
+                                                <option value="">请选择民族</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newBirthday"> 出生日期:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <div class="input-group date">
+                                                <input id="new_birthday" class="form-control" placeholder="yyyy-MM-dd" type="text" disabled>
+                                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newCounty"> 所属区县:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_county" class="form-control" disabled th:with="type=${@dict.getType('region_xa_district')}">
+                                                <option value="">请选择区县</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newAddress"> 家庭地址:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <input id="new_address" class="form-control" type="text" maxlength="512" placeholder="请输入家庭地址" disabled>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newPoliticalStatus"> 政治面貌:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_politicalStatus" class="form-control" disabled th:with="type=${@dict.getType('political_status')}">
+                                                <option value="">请选择政治面貌</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newEduNational"> 国民教育学历:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_eduNational" class="form-control" disabled th:with="type=${@dict.getType('edu_national')}">
+                                                <option value="">请选择学历</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newEduReligious"> 宗教学历:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <select id="new_eduReligious" class="form-control" disabled th:with="type=${@dict.getType('edu_religious')}">
+                                                <option value="">请选择宗教学历</option>
+                                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newJoinTime"> 入教/出家时间:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <div class="input-group date">
+                                                <input id="new_joinTime" class="form-control" placeholder="yyyy-MM-dd" type="text" disabled>
+                                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newPhone"> 联系电话:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <input id="new_phone" class="form-control" type="text" maxlength="32" placeholder="请输入联系电话" disabled>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newDomicile"> 户籍所在地:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <input id="new_domicile" class="form-control" type="text" maxlength="128" placeholder="请输入户籍所在地" disabled>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newNativePlace"> 籍贯:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <input id="new_nativePlace" class="form-control" type="text" maxlength="128" placeholder="请输入籍贯" disabled>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newOrgName"> 所属宫观名称:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <input id="new_orgName" class="form-control" type="text" maxlength="256" placeholder="请输入所属宫观名称" disabled>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-sm-6">
+                                    <div class="form-group">
+                                        <label class="col-sm-4 control-label">
+                                            <input type="checkbox" id="chk_newOrgAddress"> 所属宫观地址:
+                                        </label>
+                                        <div class="col-sm-8">
+                                            <textarea id="new_orgAddress" class="form-control" maxlength="512" placeholder="请输入所属宫观地址" disabled></textarea>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-white" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-primary" onclick="executeBatchUpdate()">执行批量更新</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 批量更新预览模态框 -->
+<div class="modal fade" id="personBatchPreviewModal" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog" style="width: 80%; max-width: 1200px;">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                <h4 class="modal-title">批量更新预览</h4>
+            </div>
+            <div class="modal-body">
+                <div class="alert alert-info">
+                    <i class="fa fa-info-circle"></i> 
+                    将影响 <strong id="previewTotalPersons">0</strong> 名人员,共 <strong id="previewTotalChanges">0</strong> 条字段变更。
+                    请仔细核对以下变更内容,确认无误后点击"确认执行"按钮。
+                </div>
+                <div class="table-responsive">
+                    <table id="previewChangeTable" class="table table-striped table-bordered table-hover">
+                        <thead>
+                            <tr>
+                                <th>姓名</th>
+                                <th>所属场所</th>
+                                <th>字段名称</th>
+                                <th>修改前</th>
+                                <th>修改后</th>
+                            </tr>
+                        </thead>
+                        <tbody id="previewChangeTableBody">
+                            <!-- 动态填充 -->
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-white" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-primary" id="btnConfirmBatchUpdate">确认执行</button>
+            </div>
+        </div>
+    </div>
+</div>
+
 <th:block th:include="include :: footer" />
+<th:block th:include="include :: datetimepicker-js" />
 <script th:inline="javascript">
     var editFlag = [[${@permission.hasPermi('religion:religionPerson:edit')}]];
     var removeFlag = [[${@permission.hasPermi('religion:religionPerson:remove')}]];
-    var statusDatas = [[${@dict.getType('religion_person_status')}]];
+    var religionTypeDatas = [[${@dict.getType('religion_type')}]];
+    var nationDatas = [[${@dict.getType('sys_nation')}]];
+    var statusDatas = [[${@dict.getType('person_status')}]];
     var prefix = ctx + "religion/religionPerson";
 
     $(function() {
@@ -112,6 +506,7 @@
             removeUrl: prefix + "/remove",
             exportUrl: prefix + "/export",
             modalName: "教职人员",
+            showColumns: true,
             columns: [{
                 checkbox: true
             },
@@ -121,28 +516,44 @@
                     visible: false
                 },
                 {
-                    field: 'placeId',
-                    title: '所属场所ID'
+                field: 'placeName',
+                title: '所属场所'
                 },
                 {
                     field: 'name',
-                    title: '姓名'
+                title: '姓名',
+                sortable: true
                 },
                 {
                     field: 'religionType',
-                    title: '宗教类别'
+                title: '宗教类别',
+                formatter: function(value, row, index) {
+                    return $.table.selectDictLabel(religionTypeDatas, value);
+                }
                 },
                 {
                     field: 'gender',
                     title: '性别'
                 },
+            {
+                field: 'nation',
+                title: '民族',
+                formatter: function(value, row, index) {
+                    return $.table.selectDictLabel(nationDatas, value);
+                }
+            },
                 {
                     field: 'idNumber',
                     title: '身份证号'
                 },
                 {
                     field: 'birthday',
-                    title: '出生日期'
+                title: '出生日期',
+                visible: false
+            },
+            {
+                field: 'phone',
+                title: '联系电话'
                 },
                 {
                     field: 'county',
@@ -150,7 +561,8 @@
                 },
                 {
                     field: 'address',
-                    title: '家庭地址/联系地址'
+                title: '家庭地址',
+                visible: false
                 },
                 {
                     field: 'politicalStatus',
@@ -158,39 +570,42 @@
                 },
                 {
                     field: 'eduNational',
-                    title: '国民教育学历'
+                title: '国民教育学历',
+                visible: false
                 },
                 {
                     field: 'eduReligious',
-                    title: '宗教学历'
+                title: '宗教学历',
+                visible: false
                 },
                 {
                     field: 'joinTime',
-                    title: '入教/出家时间'
-                },
-                {
-                    field: 'phone',
-                    title: '联系电话'
+                title: '入教/出家时间',
+                visible: false
                 },
                 {
                     field: 'domicile',
-                    title: '户籍所在地'
+                title: '户籍所在地',
+                visible: false
                 },
                 {
                     field: 'nativePlace',
-                    title: '籍贯'
+                title: '籍贯',
+                visible: false
                 },
                 {
                     field: 'orgName',
-                    title: '所属宫观名称'
+                title: '所属宫观名称',
+                visible: false
                 },
                 {
                     field: 'orgAddress',
-                    title: '所属宫观地址'
+                title: '所属宫观地址',
+                visible: false
                 },
                 {
                     field: 'status',
-                    title: '状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报',
+                title: '状态',
                     formatter: function(value, row, index) {
                         return $.table.selectDictLabel(statusDatas, value);
                     }
@@ -207,7 +622,307 @@
                 }]
         };
         $.table.init(options);
+
+        // 初始化日期控件
+        $("#new_birthday, #new_joinTime").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+
+        // 复选框控制字段启用/禁用 - 使用事件委托
+        $(document).on("change", "#batchUpdateForm input[type='checkbox']", function() {
+            var checkboxId = $(this).attr("id");
+            // chk_newReligionType -> ReligionType -> religionType
+            var fieldName = checkboxId.replace("chk_new", "");
+            var fieldId = "new_" + fieldName.charAt(0).toLowerCase() + fieldName.slice(1);
+            var isChecked = $(this).is(":checked");
+            
+            var $field = $("#" + fieldId);
+            console.log("复选框变化:", checkboxId, "->", fieldId, "启用:", isChecked, "找到元素:", $field.length);
+            
+            if ($field.length > 0) {
+                $field.prop("disabled", !isChecked);
+                console.log("设置后disabled状态:", $field.prop("disabled"));
+                
+                // 如果是日期选择器,需要特殊处理
+                if (fieldId === "new_birthday" || fieldId === "new_joinTime") {
+                    if (isChecked) {
+                        $field.datetimepicker('enable');
+                    } else {
+                        $field.datetimepicker('disable');
+                    }
+                }
+            } else {
+                console.error("未找到元素:", fieldId);
+            }
+        });
+        
+        // 确认批量更新按钮点击事件
+        $("#btnConfirmBatchUpdate").on("click", function() {
+            if (!window.batchUpdateRequestData) {
+                $.modal.alertError("请求数据丢失,请重新操作");
+                return;
+            }
+            
+            $.modal.confirm("确定要执行批量更新吗?", function() {
+                $.ajax({
+                    url: prefix + "/batchUpdateByCondition",
+                    type: "post",
+                    contentType: "application/json",
+                    data: JSON.stringify(window.batchUpdateRequestData),
+                    success: function(result) {
+                        if (result.code == web_status.SUCCESS) {
+                            var count = result.data || 0;
+                            $.modal.msgSuccess("批量更新成功,共修改 " + count + " 条记录");
+                            $("#personBatchPreviewModal").modal("hide");
+                            $("#personBatchModal").modal("hide");
+                            $.table.refresh();
+                            // 清空保存的请求数据
+                            window.batchUpdateRequestData = null;
+                        } else {
+                            $.modal.alertError(result.msg);
+                        }
+                    },
+                    error: function() {
+                        $.modal.alertError("批量更新失败,请稍后重试");
+                    }
+                });
+            });
+        });
     });
+
+    // 打开批量维护模态框
+    function openBatchUpdateModal() {
+        // 重置表单
+        $("#batchConditionForm")[0].reset();
+        $("#batchUpdateForm")[0].reset();
+        $("#batchUpdateForm input[type='checkbox']").prop("checked", false);
+        // 只禁用输入框、下拉框和文本域,不禁用复选框
+        $("#batchUpdateForm input[type='text'], #batchUpdateForm input[type='date'], #batchUpdateForm select, #batchUpdateForm textarea").prop("disabled", true);
+        
+        // 隐藏预览区并重置
+        $("#previewPanel").hide();
+        $("#batchTotalCount").text("0");
+        if ($("#previewTable").data('bootstrap.table')) {
+            $("#previewTable").bootstrapTable('destroy');
+        }
+        
+        // 显示模态框
+        $("#personBatchModal").modal("show");
+    }
+
+    // 查询预览
+    function queryPreview() {
+        var conditions = getConditions();
+        
+        // 初始化预览表格
+        var previewOptions = {
+            url: prefix + "/batchPreviewList",
+            method: 'post',
+            contentType: "application/json",
+            ajax: function(request) {
+                var queryParams = $.extend({}, conditions);
+                queryParams.pageNum = request.data.offset / request.data.limit + 1;
+                queryParams.pageSize = request.data.limit;
+                
+                $.ajax({
+                    type: "POST",
+                    url: request.url,
+                    contentType: "application/json",
+                    data: JSON.stringify(queryParams),
+                    dataType: "json",
+                    success: function(res) {
+                        request.success({
+                            total: res.total,
+                            rows: res.rows
+                        });
+                    },
+                    error: function(res) {
+                        request.error(res);
+                    }
+                });
+            },
+            pagination: true,
+            sidePagination: "server",
+            pageSize: 20,
+            pageList: [10, 20, 50],
+            showRefresh: false,
+            showToggle: false,
+            showColumns: false,
+            clickToSelect: false,
+            columns: [{
+                field: 'name',
+                title: '姓名'
+            },
+            {
+                field: 'placeName',
+                title: '所属场所'
+            },
+            {
+                field: 'religionType',
+                title: '宗教类别',
+                formatter: function(value, row, index) {
+                    return getDictLabel(religionTypeDatas, value);
+                }
+            },
+            {
+                field: 'gender',
+                title: '性别'
+            },
+            {
+                field: 'nation',
+                title: '民族',
+                formatter: function(value, row, index) {
+                    return getDictLabel(nationDatas, value);
+                }
+            },
+            {
+                field: 'county',
+                title: '所属区县'
+            },
+            {
+                field: 'status',
+                title: '状态',
+                formatter: function(value, row, index) {
+                    return getDictLabel(statusDatas, value);
+                }
+            }],
+            onLoadSuccess: function(data) {
+                $("#batchTotalCount").text(data.total || 0);
+                $("#previewPanel").show();
+            },
+            onLoadError: function() {
+                $.modal.alertError("查询失败,请稍后重试");
+            }
+        };
+
+        // 销毁旧表格并初始化新表格
+        if ($("#previewTable").data('bootstrap.table')) {
+            $("#previewTable").bootstrapTable('destroy');
+        }
+        $("#previewTable").bootstrapTable(previewOptions);
+    }
+
+    // 执行批量更新(改为预览)
+    function executeBatchUpdate() {
+        // 检查是否至少选择了一个字段
+        var checkedCount = $("#batchUpdateForm input[type='checkbox']:checked").length;
+        if (checkedCount == 0) {
+            $.modal.alertWarning("请至少选择一个要修改的字段");
+            return;
+        }
+
+        // 获取条件
+        var data = getConditions();
+
+        // 收集勾选字段的值
+        $("#batchUpdateForm input[type='checkbox']:checked").each(function() {
+            var checkboxId = $(this).attr("id");
+            // chk_newReligionType -> ReligionType
+            var fieldName = checkboxId.replace("chk_new", "");
+            // 构造输入框ID: new_religionType (首字母小写)
+            var fieldId = "new_" + fieldName.charAt(0).toLowerCase() + fieldName.slice(1);
+            var fieldValue = $("#" + fieldId).val();
+            // 构造后端字段名: newReligionType (带new前缀,首字母大写)
+            var backendFieldName = "new" + fieldName;
+            if (fieldValue !== null && fieldValue !== "") {
+                data[backendFieldName] = fieldValue;
+            }
+        });
+
+        // 调用预览接口
+        $.ajax({
+            url: prefix + "/batchUpdatePreview",
+            type: "post",
+            contentType: "application/json",
+            data: JSON.stringify(data),
+            success: function(result) {
+                if (result.code == web_status.SUCCESS) {
+                    var previewData = result.data;
+                    
+                    // 检查是否有实际变更
+                    if (!previewData || previewData.totalPersons == 0 || !previewData.rows || previewData.rows.length == 0) {
+                        $.modal.alertWarning("无实际变更,请检查筛选条件和修改字段");
+                        return;
+                    }
+                    
+                    // 保存请求数据供确认时使用
+                    window.batchUpdateRequestData = data;
+                    
+                    // 填充预览数据
+                    showBatchUpdatePreview(previewData);
+                } else {
+                    $.modal.alertError(result.msg);
+                }
+            },
+            error: function() {
+                $.modal.alertError("预览失败,请稍后重试");
+            }
+        });
+    }
+    
+    // 显示批量更新预览
+    function showBatchUpdatePreview(previewData) {
+        // 更新统计信息
+        $("#previewTotalPersons").text(previewData.totalPersons);
+        $("#previewTotalChanges").text(previewData.totalChanges);
+        
+        // 清空并填充表格
+        var tbody = $("#previewChangeTableBody");
+        tbody.empty();
+        
+        if (previewData.rows && previewData.rows.length > 0) {
+            $.each(previewData.rows, function(index, row) {
+                var tr = $("<tr>");
+                tr.append($("<td>").text(row.name || ""));
+                tr.append($("<td>").text(row.placeName || ""));
+                tr.append($("<td>").text(row.fieldLabel || ""));
+                tr.append($("<td>").text(row.oldValue || ""));
+                tr.append($("<td>").html("<strong>" + (row.newValue || "") + "</strong>"));
+                tbody.append(tr);
+            });
+        }
+        
+        // 关闭批量维护模态框,打开预览模态框
+        $("#personBatchModal").modal("hide");
+        $("#personBatchPreviewModal").modal("show");
+    }
+
+    // 获取筛选条件
+    function getConditions() {
+        var conditions = {};
+        var name = $.trim($("#cond_name").val());
+        var idNumber = $.trim($("#cond_idNumber").val());
+        var placeId = $("#cond_placeId").val();
+        var religionType = $("#cond_religionType").val();
+        var gender = $("#cond_gender").val();
+        var nation = $("#cond_nation").val();
+        var county = $("#cond_county").val();
+        var status = $("#cond_status").val();
+
+        if (name && name != "") conditions.name = name;
+        if (idNumber && idNumber != "") conditions.idNumber = idNumber;
+        if (placeId && placeId != "") conditions.placeId = placeId;
+        if (religionType && religionType != "") conditions.religionType = religionType;
+        if (gender && gender != "") conditions.gender = gender;
+        if (nation && nation != "") conditions.nation = nation;
+        if (county && county != "") conditions.county = county;
+        if (status && status != "") conditions.status = status;
+
+        return conditions;
+    }
+
+    // 获取字典标签
+    function getDictLabel(datas, value) {
+        if (!value || !datas) return "";
+        for (var i = 0; i < datas.length; i++) {
+            if (datas[i].dictValue == value) {
+                return datas[i].dictLabel;
+            }
+        }
+        return value;
+    }
 </script>
 </body>
 </html>