Просмотр исходного кода

保存当前所有工作内容

Lin Qilong 1 неделя назад
Родитель
Сommit
5183bab30a
50 измененных файлов с 6128 добавлено и 0 удалено
  1. 142 0
      docs/dameng_network_issue_solution.md
  2. 86 0
      docs/menu_config.sql
  3. 45 0
      docs/河湖管理系统.txt
  4. 85 0
      gw-common/src/main/java/com/goldenwater/common/utils/FieldConvertUtils.java
  5. 84 0
      gw-framework/src/main/java/com/goldenwater/framework/handler/ClobTypeHandler.java
  6. 9 0
      gw-ui/src/api/hzz/adtree.js
  7. 14 0
      gw-ui/src/api/hzz/assess.js
  8. 8 0
      gw-ui/src/api/hzz/eventclear.js
  9. 23 0
      gw-ui/src/api/hzz/riverproject.js
  10. 56 0
      gw-ui/src/api/hzz/rule.js
  11. 5 0
      gw-ui/src/api/hzz/shoreline.js
  12. 11 0
      gw-ui/src/api/hzz/supervision.js
  13. 10 0
      gw-ui/src/api/hzz/workplan.js
  14. 14 0
      gw-ui/src/api/hzz/wps.js
  15. BIN
      gw-ui/src/assets/thlogo.png
  16. BIN
      gw-ui/src/assets/top_bg.png
  17. 37 0
      gw-ui/src/views/hlgl/IframeView.vue
  18. 102 0
      gw-ui/src/views/hlgl/assess/index.vue
  19. 138 0
      gw-ui/src/views/hlgl/bankline/index.vue
  20. 66 0
      gw-ui/src/views/hlgl/eventclear/index.vue
  21. 292 0
      gw-ui/src/views/hlgl/gzfa/index.vue
  22. 36 0
      gw-ui/src/views/hlgl/khwj/index.vue
  23. 78 0
      gw-ui/src/views/hlgl/onemap/components/FeaturePopup.vue
  24. 114 0
      gw-ui/src/views/hlgl/onemap/components/InfoPanel.vue
  25. 125 0
      gw-ui/src/views/hlgl/onemap/components/LayerMenu.vue
  26. 132 0
      gw-ui/src/views/hlgl/onemap/config/layerMenu.js
  27. 101 0
      gw-ui/src/views/hlgl/onemap/config/layers.js
  28. 152 0
      gw-ui/src/views/hlgl/onemap/index.vue
  29. 251 0
      gw-ui/src/views/hlgl/onemap/mapContent.vue
  30. 114 0
      gw-ui/src/views/hlgl/riverproject/index.vue
  31. 73 0
      gw-ui/src/views/hlgl/shoreline/index.vue
  32. 104 0
      gw-ui/src/views/hlgl/supervision/index.vue
  33. 49 0
      gw-ui/src/views/hlgl/syaxgl/index.vue
  34. 101 0
      gw-ui/src/views/hlgl/wps/index.vue
  35. 6 0
      package-lock.json
  36. BIN
      sql/InsertUsers.class
  37. 61 0
      sql/InsertUsers.java
  38. 27 0
      sql/fix_critical.sql
  39. 12 0
      sql/fix_quotes.py
  40. 645 0
      sql/gw.sql
  41. 645 0
      sql/gw_dameng_temp.sql
  42. 645 0
      sql/gw_dm.sql
  43. 645 0
      sql/gw_mysql_backup.sql
  44. 27 0
      sql/insert_seed.sql
  45. 71 0
      sql/menu_hzz.sql
  46. 174 0
      sql/quartz.sql
  47. 219 0
      sql/quartz_dm.sql
  48. 174 0
      sql/quartz_mysql_backup.sql
  49. 5 0
      sql/run_import.sql
  50. 115 0
      sql/seed_data_dameng.sql

+ 142 - 0
docs/dameng_network_issue_solution.md

@@ -0,0 +1,142 @@
+# 达梦数据库网络通信异常解决方案
+
+## 问题描述
+
+应用启动时出现以下错误:
+```
+Caused by: dm.jdbc.driver.DMException: 网络通信异常
+Caused by: java.io.EOFException
+```
+
+错误发生在 `SysDictTypeServiceImpl.init()` 方法初始化字典缓存时。
+
+## 已实施的解决方案
+
+### 1. 优化 Druid 连接池配置
+
+**修改文件:** `gw-admin/src/main/resources/application-druid.yml`
+
+**关键调整:**
+- ✅ 增加连接超时时间:30s → 60s
+- ✅ 增加网络超时时间:60s → 120s
+- ✅ 缩短空闲连接检测间隔:60s → 30s(更频繁地检测失效连接)
+- ✅ 缩短连接最小生存时间:300s → 180s
+- ✅ 缩短连接最大生存时间:900s → 600s
+- ✅ 启用获取连接时测试:testOnBorrow: true(确保获取的连接有效)
+- ✅ 启用泄漏连接回收:removeAbandoned: true
+- ✅ 设置泄漏回收超时:300秒
+- ✅ 开启泄漏日志:logAbandoned: true
+
+### 2. 添加启动重试机制
+
+**修改文件:** `gw-system/src/main/java/com/goldenwater/system/service/impl/SysDictTypeServiceImpl.java`
+
+**功能说明:**
+- 在应用启动时,如果数据库连接失败,会自动重试最多3次
+- 每次重试间隔递增:第一次2秒,第二次4秒,第三次6秒
+- 避免因启动瞬间网络波动导致的应用启动失败
+
+### 3. 增强异常处理
+
+**修改内容:**
+- 在 `loadingDictCache()` 方法中添加 try-catch 保护
+- 提供更清晰的错误信息
+- 确保空值检查,避免 NPE
+
+## 其他建议方案
+
+### 方案A:检查网络连接
+
+1. **验证数据库服务状态**
+   ```bash
+   # 测试数据库服务器连通性
+   ping 39.98.38.2
+   
+   # 测试数据库端口连通性
+   telnet 39.98.38.2 30236
+   ```
+
+2. **检查防火墙规则**
+   - 确保防火墙没有阻止 30236 端口
+   - 检查是否有中间网络设备(如负载均衡器)断开空闲连接
+
+3. **检查数据库服务器负载**
+   - CPU、内存使用率是否正常
+   - 连接数是否达到上限
+
+### 方案B:更新达梦数据库驱动
+
+如果当前使用的达梦驱动版本较老,建议升级到最新稳定版:
+
+1. 查看当前驱动版本(在 pom.xml 中)
+2. 从达梦官网下载最新驱动
+3. 替换项目中的 dm-jdbc 驱动包
+
+### 方案C:调整数据库服务器配置
+
+联系 DBA 检查达梦数据库服务器配置:
+- `MAX_SESSIONS` - 最大会话数
+- `CONNECT_TIMEOUT` - 连接超时设置
+- `KEEP_ALIVE` - 保持活动连接配置
+
+### 方案D:使用连接保活机制
+
+如果问题持续存在,可以考虑:
+
+1. **在 JDBC URL 中添加参数:**
+   ```yaml
+   url: jdbc:dm://39.98.38.2:30236?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&schema=TBA_HZZ&connectTimeout=60000&socketTimeout=120000
+   ```
+
+2. **启用 TCP KeepAlive:**
+   在操作系统层面启用 TCP KeepAlive 以防止防火墙断开空闲连接
+
+## 监控和诊断
+
+### 1. 启用 Druid 监控
+
+访问 Druid 监控页面:`http://localhost:8080/druid/`
+- 用户名:goldenwater
+- 密码:123456
+
+查看:
+- 连接池活跃连接数
+- 连接泄漏情况
+- SQL 执行统计
+- 慢查询记录
+
+### 2. 查看应用日志
+
+关注以下日志信息:
+- 连接池初始化日志
+- 连接获取/释放日志
+- 数据库异常堆栈
+
+### 3. 网络抓包分析(必要时)
+
+如果问题依然存在,可以使用 Wireshark 或 tcpdump 进行网络抓包,分析 TCP 连接断开的具体原因。
+
+## 快速验证
+
+重启应用后,观察:
+1. ✅ 应用是否能正常启动
+2. ✅ 控制台是否有 "加载字典缓存失败" 的错误
+3. ✅ 访问系统功能是否正常
+4. ✅ Druid 监控页面显示的连接池状态是否正常
+
+## 预防措施
+
+1. **定期检查数据库连接状态**
+2. **监控连接池使用情况**
+3. **设置合理的超时和重试策略**
+4. **保持数据库驱动为最新稳定版本**
+5. **定期维护数据库服务器和网络设备**
+
+## 联系支持
+
+如果以上方案都无法解决问题,请提供:
+- 完整的应用启动日志
+- Druid 监控截图
+- 数据库服务器版本信息
+- 网络拓扑结构
+- 问题发生的时间和频率

+ 86 - 0
docs/menu_config.sql

@@ -0,0 +1,86 @@
+-- ============================================================
+-- 河湖管理系统菜单配置 SQL
+-- 适用数据库: 达梦DM / Oracle
+-- 说明: 需要先有"系统管理"等RuoYi基础菜单
+-- 使用方式: 登录系统 → 系统管理 → 菜单管理 → 或直接执行SQL
+-- ============================================================
+
+-- ==================== 一级菜单:河湖管理 ====================
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('河湖管理', 0, 1, 'hzz', NULL, 1, 0, 'M', '0', '0', NULL, 'tree', 'admin', SYSDATE, 'admin', SYSDATE, '河湖管理系统顶级菜单');
+
+-- 达梦获取刚插入的 menu_id 可用 IDENT_CURRENT('sys_menu')
+-- 以下SQL假设河湖管理menu_id=1000(请根据实际自增值修改)
+
+-- ==================== 模块1:水域岸线管理 ====================
+-- 菜单
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('水域岸线管理', 1000, 1, 'bankline', 'hzz/bankline/index', 1, 0, 'C', '0', '0', 'hzz:bankline:list', 'form', 'admin', SYSDATE, 'admin', SYSDATE, '河湖长制工作制度查询展示');
+
+-- 模块1的按钮权限
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('制度文件查询', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:bankline:list') t), 1, '', NULL, 1, 0, 'F', '0', '0', 'hzz:rule:file:list', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('工作动态查询', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:bankline:list') t), 2, '', NULL, 1, 0, 'F', '0', '0', 'hzz:rule:article:list', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+
+-- ==================== 模块2:水域岸线(涉河项目+岸线统计) ====================
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('涉河项目查询', 1000, 2, 'riverproject', 'hzz/riverproject/index', 1, 0, 'C', '0', '0', 'hzz:riverproject:list', 'build', 'admin', SYSDATE, 'admin', SYSDATE, '涉河项目查询与管理');
+
+-- 涉河项目按钮权限
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('涉河项目新增', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:riverproject:list') t), 1, '', NULL, 1, 0, 'F', '0', '0', 'hzz:riverproject:add', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('涉河项目修改', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:riverproject:list') t), 2, '', NULL, 1, 0, 'F', '0', '0', 'hzz:riverproject:edit', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('涉河项目删除', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:riverproject:list') t), 3, '', NULL, 1, 0, 'F', '0', '0', 'hzz:riverproject:remove', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+
+-- 岸线功能区统计
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('岸线功能区统计', 1000, 3, 'shoreline', 'hzz/shoreline/index', 1, 0, 'C', '0', '0', 'hzz:shoreline:list', 'chart', 'admin', SYSDATE, 'admin', SYSDATE, '岸线功能区长度统计');
+
+-- ==================== 模块3:考核评估 ====================
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('考核评估', 1000, 4, 'assess', 'hzz/assess/index', 1, 0, 'C', '0', '0', 'hzz:assess:list', 'edit', 'admin', SYSDATE, 'admin', SYSDATE, '考核评估:工作方案统计、河长湖长统计、公示牌统计、考核文件');
+
+-- 考核评估按钮权限
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('考核评估查询', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:assess:list') t), 1, '', NULL, 1, 0, 'F', '0', '0', 'hzz:assess:query', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+
+-- ==================== 模块4:督导检查 ====================
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('督导检查', 1000, 5, 'supervision', 'hzz/supervision/index', 1, 0, 'C', '0', '0', 'hzz:supervision:list', 'monitor', 'admin', SYSDATE, 'admin', SYSDATE, '督导检查列表');
+
+-- 督导检查按钮权限
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('督导审核', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:supervision:list') t), 1, '', NULL, 1, 0, 'F', '0', '0', 'hzz:supervision:audit', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+
+-- 涉河项目督查
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('涉河项目督查', 1000, 6, 'wps', 'hzz/wps/index', 1, 0, 'C', '0', '0', 'hzz:wps:list', 'list', 'admin', SYSDATE, 'admin', SYSDATE, '涉河项目督查查询与管理');
+
+-- 涉河项目督查按钮权限
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('督查新增', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:wps:list') t), 1, '', NULL, 1, 0, 'F', '0', '0', 'hzz:wps:add', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('督查修改', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:wps:list') t), 2, '', NULL, 1, 0, 'F', '0', '0', 'hzz:wps:edit', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('督查删除', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:wps:list') t), 3, '', NULL, 1, 0, 'F', '0', '0', 'hzz:wps:remove', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+
+-- 一湖两河清四乱
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('一湖两河清四乱', 1000, 7, 'eventclear', 'hzz/eventclear/index', 1, 0, 'C', '0', '0', 'hzz:eventclear:list', 'warning', 'admin', SYSDATE, 'admin', SYSDATE, '一湖两河清四乱信息查询');
+
+-- 清四乱按钮权限
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+VALUES ('清四乱查询', (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE perms = 'hzz:eventclear:list') t), 1, '', NULL, 1, 0, 'F', '0', '0', 'hzz:eventclear:query', '#', 'admin', SYSDATE, 'admin', SYSDATE, '');
+
+-- ============================================================
+-- 分配权限:给超级管理员角色分配所有河湖管理菜单权限
+-- ============================================================
+-- 假设超级管理员角色 role_id = 1
+-- INSERT INTO sys_role_menu (role_id, menu_id)
+-- SELECT 1, menu_id FROM sys_menu WHERE menu_name IN (
+--   '河湖管理', '水域岸线管理', '涉河项目查询', '岸线功能区统计',
+--   '考核评估', '督导检查', '涉河项目督查', '一湖两河清四乱'
+-- );

+ 45 - 0
docs/河湖管理系统.txt

@@ -0,0 +1,45 @@
+5.3河湖管理系统需求分析
+河湖管理系统作为太湖流域水资源监控与保护预警系统综合应用体系的关键子系统,核心承担流域河湖岸线管控、水域空间保护、河湖长制管理、涉河湖工程监管及河湖生态修复监督等核心业务职能,是保障太湖流域河湖健康生命、维护水域空间有序利用的核心信息化支撑载体。本次需求分析基于系统现有运行现状、国产化改造总体要求及太湖流域河湖管理业务实际需求,全面梳理当前存在的核心问题,明确改造方向与核心目标,细化技术、功能、数据等全维度需求,为系统国产化改造的设计、开发、测试及验收提供坚实依据,确保改造后系统适配国产化环境、支撑河湖管理精细化与智能化开展。
+5.3.1系统现状及核心问题
+结合太湖流域水资源监控与保护预警系统国产化改造实施方案调研情况,当前河湖管理系统已初步实现河湖基础信息展示、岸线利用审批备案、河湖长制基础台账管理等基础功能,但受技术架构滞后、业务迭代更新等因素影响,在国产化适配、功能支撑、数据管理、协同联动及安全防护等方面仍存在诸多短板,难以满足流域河湖精细化管理、安全化管控的实际需求,具体核心问题如下:
+国产化适配缺口明显,核心技术自主可控不足。系统当前前端采用EXT.JS、JSP等老旧非国产化适配技术,对麒麟、统信等国产化操作系统及国产化浏览器适配性差,存在页面渲染错乱、功能响应异常等问题;后端依赖Oracle数据库专有语法及国外中间件,数据库、中间件未完成国产化替代,操作系统部分依赖国外产品,核心技术存在“卡脖子”风险,不符合国产化安全管控与自主可控要求。
+数据管理不规范。未建立标准化的河湖管理专题数据库,河湖基础信息、岸线利用数据、涉河湖工程数据、河湖长制台账数据等分散存储,数据格式不统一、冗余度高。
+安全防护体系不完善,风险防控能力薄弱。涉河项目信息、河湖长制考核数据等敏感数据未实现加密存储与传输。权限管控颗粒度较粗,未按业务角色、管控场景实现精细化权限分配,存在越权访问风险。缺乏完善的安全审计日志与异常行为监测机制,安全漏洞扫描与应急响应能力不足,难以有效防范数据泄露、系统攻击等安全风险。
+5.3.2改造目标
+本次河湖管理系统国产化改造以“国产化替代、数据规范化、安全体系化”为核心目标,结合太湖流域河湖管理业务发展需求,全面提升系统的自主可控能力、业务支撑能力、协同联动能力及安全防护能力,具体目标如下:
+技术架构全面国产化:完成前端、后端、数据库、中间件及操作系统的国产化适配改造,彻底摆脱国外技术依赖,核心组件全部采用国产化产品,确保系统在国产化环境下稳定运行,年均可用率不低于99.2%。
+核心功能优化升级:优化现有基础功能,补充岸线动态监测、水域空间预警、涉河湖工程全流程监管等精细化功能,覆盖河湖管理全业务场景,提升功能响应效率,核心接口响应时间≤2秒。
+数据管理规范高效:建立标准化河湖管理专题数据库,完成历史数据清洗、整合与国产化迁移,统一数据格式与共享标准,提升数据质量与复用率,实现与关联系统数据实时或准实时同步。
+安全防护全面加固:构建符合三级等保要求的全方位安全防护体系,实现敏感数据加密、精细化权限管控,确保数据与系统安全。
+5.3.3核心需求
+基于系统现状问题及改造目标,结合太湖流域河湖管理核心业务场景(涉河项目管理、岸线管控、涉河项目督察、河湖管理督察等),梳理形成涵盖技术架构、功能、数据、安全多维度的核心需求,具体内容如下:
+5.3.3.1技术架构国产化需求
+技术架构改造需全面适配国产化软硬件环境,实现核心技术自主可控,具体需求包括:
+前端技术改造:采用Vue.js等现代国产化前端框架,替代原有EXT.JS、JSP等老旧技术及组件,优化页面渲染逻辑与交互设计,全面适配麒麟、统信等国产化操作系统及360安全浏览器(国产化版)、火狐(国产化版)等国产化浏览器,确保页面展示流畅、操作便捷,支持页面自适应调整。
+后端技术改造:基于Java开发语言,采用微服务架构拆分核心业务模块(如涉河项目管理、岸线管控、涉河项目督察等),实现模块独立部署、扩展与维护,单个模块故障不影响整体系统运行;替换国外中间件,采用Tomcat作为后台服务发布中间件、Nginx作为Web服务器,提升服务稳定性与性能。
+数据库国产化改造:建立标准化河湖管理专题数据库,采用达梦、人大金仓等国产化数据库替代原有Oracle数据库,完成数据库表结构重构、语法适配改造,优化数据访问逻辑,替代原有依赖国外数据库的专有技术(如dblink、存储过程),确保数据访问高效、稳定。
+部署环境适配:全面适配国产化服务器集群及国产化操作系统,完成测试环境与生产环境的部署配置、网络调试、权限分配及安全加固,严格遵循信息安全等级保护三级标准要求,保障系统部署环境安全合规。
+5.3.3.2核心功能需求
+围绕河湖管理系统四大核心业务模块,结合精细化管理需求,明确各模块核心功能需求如下:
+水域岸线管理模块需求:支持太湖流域河湖长制工作制度的查询与展示。
+水域岸线模块需求:支持太湖流域涉河项目查询(项目名称、联系人、项目性质、申请人)基础信息管理与地图可视化展示。岸线功能区数据统计,功能区岸线长度统计。
+考核评估模块:河长制工作方案统计、河长湖长数量统计、河长制办公室人员情况统计、河长公示牌统计、出台规定制度统计和考核相关文件展示。
+督导检查模块需求:支持太湖流域涉河项目督查信息(项目名称、督察情况、开始时间、结束时间等)查询。一湖两河清四乱信息(河段名称、事件类型、问题描述、当前情况、整改情况、检查情况等)查询。
+5.3.3.3数据需求
+数据需求聚焦数据规范、数据迁移、数据共享三大核心,确保数据全面、准确、规范、可用,具体需求如下:
+数据规范需求:建立统一的河湖管理数据标准体系,涵盖涉河湖工程基础信息、涉河湖督察信息等各类数据的格式、编码、存储、传输标准。
+数据迁移需求:采用数据同步软件(如Kettle)完成历史数据从原Oracle数据库向国产化数据库的迁移,优先迁移核心业务数据(河湖基础信息、岸线审批数据、河湖长巡河数据、涉河湖工程数据等);迁移过程中开展数据完整性、一致性校验,迁移后进行数据可用性测试,确保历史数据不丢失、不损坏,可正常查询与使用。
+5.3.3.4协同需求
+协同需求核心实现跨子系统及跨关联单位的高效协同,提升业务办理与应急处置效率,具体需求如下:
+与水资源管理系统、水资源保护系统协同共享数据,支撑河湖管理与水资源开发利用、水环境保护协同管控;与苏浙沪省级河湖管理系统建立双向数据交互机制,推送流域层面监管指令、预警信息等,接收区域河湖管理数据、处置反馈信息等;与太湖流域水环境监测中心数据管理平台、工程运行管理系统对接,实时获取水质监测数据、涉河湖工程运行数据,辅助开展河湖管理决策;与国家水资源监控能力建设项目流域分中心系统协同,按要求自动上报河湖管理核心数据,确保数据上报合规及时。
+5.3.3.5安全需求
+围绕数据安全、权限安全、运行安全三大核心,构建全方位安全防护体系,具体需求如下:
+数据安全需求:对涉河湖工程信息、涉河项目督查信息数据等敏感数据进行全流程加密存储与传输,采用国密算法与规范的密钥管理机制;建立数据定期备份机制,实现本地备份与异地灾备相结合,核心数据按日备份,历史数据定期归档,防止数据丢失;严格控制数据访问权限,敏感数据仅授权人员可访问、操作,实现数据操作全程追溯。
+权限安全需求:建立精细化用户权限管理机制,按业务角色、监管场景分配用户权限,实现“一人一权、权责对等”;支持用户权限新增、修改、删除、查询等操作,定期开展权限审计与清理,避免权限滥用;实现用户登录身份双重认证(账号密码+验证码),密码加密存储,支持登录日志记录与异常登录(多次密码错误、异地登录)预警。
+运行安全需求:配置网络防火墙、入侵检测系统等安全设备,监控与过滤入站、出站网络流量,限制非法端口与服务访问;定期开展系统安全审计与漏洞扫描,形成审计报告与漏洞修复方案,及时修复安全隐患;定期更新操作系统、服务器及应用程序安全补丁,建立安全应急响应机制,遭遇网络攻击或系统故障时可快速处置、恢复运行。
+5.3.4非功能需求
+除核心功能及安全需求外,系统还需满足性能、易用性、可维护性、可扩展性等非功能需求,具体如下:
+性能需求:系统核心接口响应时间≤2秒,页面加载时间≤3秒;支持并发用户数≥80人,多用户同时操作(数据查询、报表生成、审批办理等)无明显卡顿、延迟;大数据量处理(如年度河湖管理数据统计、历史巡河数据查询)时性能不降级,数据处理效率满足业务需求。
+易用性需求:前端界面设计简洁直观、布局合理,符合水利行业用户操作习惯;支持页面自适应调整,适配不同分辨率显示设备;提供操作指引、帮助文档与常见问题解答,降低用户学习成本;关键操作(如审批办理、问题上报、预警处置)提供流程提示,核心功能设置快捷操作入口,提升操作便捷性。
+可维护性需求:系统架构设计清晰,模块划分合理,支持单个模块独立维护、升级与故障排查;建立完善的日志管理机制,记录系统运行、用户操作、数据流转、故障信息等各类日志,日志留存时间满足合规要求;支持系统配置参数灵活调整,提供模块化的维护工具,降低维护成本。
+可扩展性需求:系统架构支持功能模块灵活新增与扩展,能够适配未来太湖流域河湖管理业务发展变化;数据库设计支持数据量持续增长,具备良好的扩容能力;支持与新增关联系统的对接与集成,提升系统扩展性。

+ 85 - 0
gw-common/src/main/java/com/goldenwater/common/utils/FieldConvertUtils.java

@@ -0,0 +1,85 @@
+package com.goldenwater.common.utils;
+
+import java.util.*;
+
+/**
+ * 字段名转换工具类
+ */
+public class FieldConvertUtils {
+
+    /**
+     * 将Map中的大写下划线字段转换为小写驼峰格式
+     * @param data 原始数据列表
+     * @return 转换后的数据列表
+     */
+    public static List<Map<String, Object>> convertToCamelCase(List<Map<String, Object>> data) {
+        if (data == null || data.isEmpty()) {
+            return data;
+        }
+        
+        List<Map<String, Object>> result = new ArrayList<>();
+        for (Map<String, Object> item : data) {
+            result.add(convertMapKeysToCamelCase(item));
+        }
+        return result;
+    }
+
+    /**
+     * 将单个Map中的键从大写下划线格式转换为小写驼峰格式
+     * @param map 原始Map
+     * @return 转换后的Map
+     */
+    private static Map<String, Object> convertMapKeysToCamelCase(Map<String, Object> map) {
+        if (map == null || map.isEmpty()) {
+            return map;
+        }
+        
+        Map<String, Object> converted = new LinkedHashMap<>();
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            
+            // 将大写下划线格式转换为小写驼峰格式
+            String camelKey = toCamelCase(key);
+            converted.put(camelKey, value);
+        }
+        return converted;
+    }
+
+    /**
+     * 将下划线命名转换为驼峰命名
+     * @param str 下划线命名的字符串
+     * @return 驼峰命名的字符串
+     */
+    private static String toCamelCase(String str) {
+        if (str == null || str.isEmpty()) {
+            return str;
+        }
+        
+        // 如果已经是小写开头,直接返回
+        if (Character.isLowerCase(str.charAt(0))) {
+            return str;
+        }
+        
+        // 转为小写
+        str = str.toLowerCase();
+        
+        StringBuilder sb = new StringBuilder(str.length());
+        boolean upperCase = false;
+        
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            
+            if (c == '_') {
+                upperCase = true;
+            } else if (upperCase) {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            } else {
+                sb.append(c);
+            }
+        }
+        
+        return sb.toString();
+    }
+}

+ 84 - 0
gw-framework/src/main/java/com/goldenwater/framework/handler/ClobTypeHandler.java

@@ -0,0 +1,84 @@
+package com.goldenwater.framework.handler;
+
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedTypes;
+
+/**
+ * MyBatis 自定义 TypeHandler:将数据库 CLOB / NCLOB 类型转为 String
+ * <p>
+ * 解决 DM 数据库返回 NClobProxyImpl 对象导致 Jackson 序列化深度超限的问题。
+ * 在 MyBatis 映射阶段就将 CLOB 读取为字符串,避免 JDBC 内部对象泄漏到业务层。
+ *
+ * @author goldenwater
+ */
+@MappedTypes({String.class})
+public class ClobTypeHandler extends BaseTypeHandler<String>
+{
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException
+    {
+        ps.setString(i, parameter);
+    }
+
+    @Override
+    public String getNullableResult(ResultSet rs, String columnName) throws SQLException
+    {
+        return clobToString(rs.getObject(columnName));
+    }
+
+    @Override
+    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException
+    {
+        return clobToString(rs.getObject(columnIndex));
+    }
+
+    @Override
+    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException
+    {
+        return clobToString(cs.getObject(columnIndex));
+    }
+
+    /**
+     * 将 Clob / NClob / ClobProxyImpl 等对象转为字符串
+     */
+    private String clobToString(Object obj) throws SQLException
+    {
+        if (obj == null)
+        {
+            return null;
+        }
+        if (obj instanceof String)
+        {
+            return (String) obj;
+        }
+        if (obj instanceof Clob)
+        {
+            Clob clob = (Clob) obj;
+            long length = clob.length();
+            if (length == 0)
+            {
+                return "";
+            }
+            return clob.getSubString(1, (int) length);
+        }
+        if (obj instanceof NClob)
+        {
+            NClob nclob = (NClob) obj;
+            long length = nclob.length();
+            if (length == 0)
+            {
+                return "";
+            }
+            return nclob.getSubString(1, (int) length);
+        }
+        return obj.toString();
+    }
+}

+ 9 - 0
gw-ui/src/api/hzz/adtree.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getChildren(adCode) {
+  return request({ url: '/hzz/adtree/children', method: 'get', params: { adCode } })
+}
+
+export function getInfo(adCode) {
+  return request({ url: '/hzz/adtree/info', method: 'get', params: { adCode } })
+}

+ 14 - 0
gw-ui/src/api/hzz/assess.js

@@ -0,0 +1,14 @@
+import request from '@/utils/request'
+
+export function getTotalScore(adCode, assTime, effDate) {
+  return request({ url: '/hzz/assess/totalScore', method: 'get', params: { adCode, assTime, effDate } })
+}
+export function getMthNameAndScore(adCode, assTime, effDate) {
+  return request({ url: '/hzz/assess/mthNameAndScore', method: 'get', params: { adCode, assTime, effDate } })
+}
+export function getMethodFile(adCode, effDate) {
+  return request({ url: '/hzz/assess/methodFile', method: 'get', params: { adCode, effDate } })
+}
+export function getFileText(mdCode, effDate, exprDate) {
+  return request({ url: '/hzz/assess/fileText', method: 'get', params: { mdCode, effDate, exprDate } })
+}

+ 8 - 0
gw-ui/src/api/hzz/eventclear.js

@@ -0,0 +1,8 @@
+import request from '@/utils/request'
+
+export function listEventClear(query) {
+  return request({ url: '/hzz/eventclear/list', method: 'get', params: query })
+}
+export function searchEventClear(data, pageNum, pageSize) {
+  return request({ url: '/hzz/eventclear/search', method: 'post', params: { pageNum, pageSize }, data })
+}

+ 23 - 0
gw-ui/src/api/hzz/riverproject.js

@@ -0,0 +1,23 @@
+import request from '@/utils/request'
+
+export function listProject(query) {
+  return request({ url: '/hzz/riverproject/list', method: 'get', params: query })
+}
+export function getAllProject(searchName) {
+  return request({ url: '/hzz/riverproject/all', method: 'get', params: { searchName } })
+}
+export function getProject(guid) {
+  return request({ url: '/hzz/riverproject/detail/' + guid, method: 'get' })
+}
+export function getProjectFiles(guid) {
+  return request({ url: '/hzz/riverproject/files/' + guid, method: 'get' })
+}
+export function addProject(data) {
+  return request({ url: '/hzz/riverproject', method: 'post', data })
+}
+export function updateProject(data) {
+  return request({ url: '/hzz/riverproject', method: 'put', data })
+}
+export function delProject(guid) {
+  return request({ url: '/hzz/riverproject/' + guid, method: 'delete' })
+}

+ 56 - 0
gw-ui/src/api/hzz/rule.js

@@ -0,0 +1,56 @@
+import request from '@/utils/request'
+
+// 将大写字段转换为小写驼峰格式
+function convertToCamelCase(data) {
+  if (!data || !Array.isArray(data)) return data
+  return data.map(item => {
+    const converted = {}
+    for (const key in item) {
+      if (item.hasOwnProperty(key)) {
+        // 将大写下划线格式转换为小写驼峰格式
+        const camelKey = key.toLowerCase().replace(/_([a-z])/g, (match, letter) => letter.toUpperCase())
+        converted[camelKey] = item[key]
+      }
+    }
+    return converted
+  })
+}
+
+// ==================== 制度规则 ====================
+export function listRule(adCode, ruleType) {
+  return request({ url: '/hzz/rule/list', method: 'get', params: { adCode, ruleType } }).then(res => {
+    res.data = convertToCamelCase(res.data)
+    return res
+  })
+}
+
+// ==================== 制度文件 ====================
+export function listFile(query) {
+  return request({ url: '/hzz/rule/file/list', method: 'get', params: query })
+}
+export function addFile(data) {
+  return request({ url: '/hzz/rule/file', method: 'post', data })
+}
+export function updateFile(data) {
+  return request({ url: '/hzz/rule/file', method: 'put', data })
+}
+export function delFile(guid) {
+  return request({ url: '/hzz/rule/file/' + guid, method: 'delete' })
+}
+
+// ==================== 工作动态 ====================
+export function listArticle(query) {
+  return request({ url: '/hzz/rule/article/list', method: 'get', params: query })
+}
+export function getArticleTop5(type) {
+  return request({ url: '/hzz/rule/article/top5', method: 'get', params: { type } })
+}
+export function addArticle(data) {
+  return request({ url: '/hzz/rule/article', method: 'post', data })
+}
+export function updateArticle(data) {
+  return request({ url: '/hzz/rule/article', method: 'put', data })
+}
+export function delArticle(guid) {
+  return request({ url: '/hzz/rule/article/' + guid, method: 'delete' })
+}

+ 5 - 0
gw-ui/src/api/hzz/shoreline.js

@@ -0,0 +1,5 @@
+import request from '@/utils/request'
+
+export function getShorelineStats() {
+  return request({ url: '/hzz/shoreline/stats', method: 'get' })
+}

+ 11 - 0
gw-ui/src/api/hzz/supervision.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+export function listSupervision(query) {
+  return request({ url: '/hzz/supervision/list', method: 'get', params: query })
+}
+export function getSupervisionDetail(guid) {
+  return request({ url: '/hzz/supervision/detail/' + guid, method: 'get' })
+}
+export function auditSupervision(guid, audit, auditInfo) {
+  return request({ url: '/hzz/supervision/audit', method: 'post', params: { guid, audit, auditInfo } })
+}

+ 10 - 0
gw-ui/src/api/hzz/workplan.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+// 查询工作方案列表
+export function listWorkPlan(adCode) {
+  return request({
+    url: '/hzz/workplan/list',
+    method: 'get',
+    params: { adCode }
+  })
+}

+ 14 - 0
gw-ui/src/api/hzz/wps.js

@@ -0,0 +1,14 @@
+import request from '@/utils/request'
+
+export function listWps(query) {
+  return request({ url: '/hzz/wps/list', method: 'get', params: query })
+}
+export function addWps(data) {
+  return request({ url: '/hzz/wps', method: 'post', data })
+}
+export function updateWps(data) {
+  return request({ url: '/hzz/wps', method: 'put', data })
+}
+export function delWps(guid) {
+  return request({ url: '/hzz/wps/' + guid, method: 'delete' })
+}

BIN
gw-ui/src/assets/thlogo.png


BIN
gw-ui/src/assets/top_bg.png


+ 37 - 0
gw-ui/src/views/hlgl/IframeView.vue

@@ -0,0 +1,37 @@
+<template>
+  <iframe :src="url" class="iframe-full" frameborder="0" />
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+
+// 旧页面映射表
+const oldPages = {
+  '/gzfa':     '/src/pages/gwHzzIndex.html#/gzfa',
+  '/shjsxm':   '/src/pages/gwHzzIndex.html#/shjsxm',
+  '/syaxgl':   '/src/pages/gwHzzIndex.html#/syaxgl',
+  '/khpg':     '/src/pages/gwHzzIndex.html#/khpg',
+  '/khwj':     '/src/pages/gwHzzIndex.html#/khwj',
+  '/ddlb':     '/src/pages/gwHzzIndex.html#/ddlb',
+  '/ddsh':     '/src/pages/gwHzzIndex.html#/ddsh',
+  '/sjml':     '/src/pages/gwHzzIndex.html#/sjml',
+}
+
+const isDev = import.meta.env.MODE === 'development'
+
+const url = computed(() => {
+  const path = route.path
+  const oldPath = oldPages[path] || ''
+  if (isDev) {
+    return 'http://localhost:8081' + oldPath
+  }
+  return oldPath
+})
+</script>
+
+<style scoped>
+.iframe-full { width: 100%; height: calc(100vh - 84px); border: none; }
+</style>

+ 102 - 0
gw-ui/src/views/hlgl/assess/index.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" :inline="true">
+      <el-form-item label="行政区划编码">
+        <el-input v-model="queryParams.adCode" placeholder="请输入行政区划编码" clearable style="width:180px" />
+      </el-form-item>
+      <el-form-item label="考核时间">
+        <el-input v-model="queryParams.assTime" placeholder="yyyy-MM-dd" clearable style="width:180px" />
+      </el-form-item>
+      <el-form-item label="生效日期">
+        <el-input v-model="queryParams.effDate" placeholder="yyyy-MM-dd" clearable style="width:180px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="20">
+      <!-- 考核总分卡片 -->
+      <el-col :span="24" style="margin-bottom:20px">
+        <el-card shadow="hover">
+          <template #header><span>考核总分</span></template>
+          <el-table v-loading="scoreLoading" :data="scoreList" border size="small">
+            <el-table-column label="行政区划编码" prop="AD_CODE" width="150" />
+            <el-table-column label="行政区划名称" prop="AD_NAME" width="200" />
+            <el-table-column label="总分" prop="SCORE" width="120" />
+          </el-table>
+        </el-card>
+      </el-col>
+
+      <!-- 考核方法与分数 -->
+      <el-col :span="24" style="margin-bottom:20px">
+        <el-card shadow="hover">
+          <template #header><span>考核方法名称与分数</span></template>
+          <el-table v-loading="methodLoading" :data="methodList" border size="small">
+            <el-table-column label="行政区划编码" prop="AD_CODE" width="150" />
+            <el-table-column label="行政区划名称" prop="AD_NAME" width="200" />
+            <el-table-column label="考核方法" prop="MTH_NAME" :show-overflow-tooltip="true" />
+            <el-table-column label="分数" prop="SCORE" width="100" />
+            <el-table-column label="考核时间" prop="ASS_TIME" width="120" />
+          </el-table>
+        </el-card>
+      </el-col>
+
+      <!-- 考核文件 -->
+      <el-col :span="24">
+        <el-card shadow="hover">
+          <template #header><span>考核相关文件</span></template>
+          <el-table v-loading="fileLoading" :data="fileList" border size="small">
+            <el-table-column label="考核名称" prop="ASS_NAME" :show-overflow-tooltip="true" />
+            <el-table-column label="文件标题" prop="FILE_TITL" :show-overflow-tooltip="true" />
+            <el-table-column label="提交单位" prop="ENTER_UNIT" width="160" />
+            <el-table-column label="总分" prop="TOTAL_POINTS" width="100" />
+            <el-table-column label="提交时间" prop="ENTER_TM" width="120" />
+            <el-table-column label="附件" width="100">
+              <template #default="scope">
+                <el-link v-if="scope.row.FILE_PATH" :href="scope.row.FILE_PATH" target="_blank" type="primary">查看</el-link>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { getTotalScore, getMthNameAndScore, getMethodFile } from '@/api/hzz/assess'
+
+const queryParams = ref({ adCode: '', assTime: '', effDate: '' })
+const scoreLoading = ref(false)
+const methodLoading = ref(false)
+const fileLoading = ref(false)
+const scoreList = ref([])
+const methodList = ref([])
+const fileList = ref([])
+
+const handleQuery = async () => {
+  const { adCode, assTime, effDate } = queryParams.value
+  if (!adCode || !assTime || !effDate) return
+
+  scoreLoading.value = true
+  methodLoading.value = true
+  fileLoading.value = true
+  try {
+    const [scoreRes, methodRes, fileRes] = await Promise.all([
+      getTotalScore(adCode, assTime, effDate),
+      getMthNameAndScore(adCode, assTime, effDate),
+      getMethodFile(adCode, effDate)
+    ])
+    scoreList.value = scoreRes.data || []
+    methodList.value = methodRes.data || []
+    fileList.value = fileRes.data || []
+  } finally {
+    scoreLoading.value = false
+    methodLoading.value = false
+    fileLoading.value = false
+  }
+}
+</script>

+ 138 - 0
gw-ui/src/views/hlgl/bankline/index.vue

@@ -0,0 +1,138 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeTab" type="border-card">
+      <!-- 工作方案 Tab -->
+      <el-tab-pane label="工作方案" name="workplan">
+        <el-table v-loading="wpLoading" :data="workPlanList" border>
+          <el-table-column label="方案编号" prop="wpCode" width="160" />
+          <el-table-column label="方案名称" prop="wpName" :show-overflow-tooltip="true" />
+          <el-table-column label="文号" prop="fileNum" width="140" />
+          <el-table-column label="发布日期" prop="rlsTm" width="120" />
+          <el-table-column label="发布单位" prop="releWiunName" width="180" />
+          <el-table-column label="附件" prop="attc" width="100">
+            <template #default="scope">
+              <el-link v-if="scope.row.attc" :href="scope.row.attc" target="_blank" type="primary">查看</el-link>
+              <span v-else>-</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-tab-pane>
+
+      <!-- 制度文件 Tab -->
+      <el-tab-pane label="制度文件" name="files">
+        <el-form :model="fileQuery" :inline="true">
+          <el-form-item label="文件名称">
+            <el-input v-model="fileQuery.fileName" placeholder="请输入文件名称" clearable style="width:200px" />
+          </el-form-item>
+          <el-form-item label="文件类型">
+            <el-input v-model="fileQuery.fileType" placeholder="请输入文件类型" clearable style="width:150px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="getFileList">搜索</el-button>
+            <el-button icon="Refresh" @click="resetFileQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+        <el-table v-loading="fileLoading" :data="fileList" border>
+          <el-table-column label="文件名称" prop="FILE_NAME" :show-overflow-tooltip="true" />
+          <el-table-column label="文件类型" prop="FILE_TYPE" width="120" />
+          <el-table-column label="文号" prop="WH" width="150" />
+          <el-table-column label="日期" prop="START_DATE" width="120" />
+          <el-table-column label="附件" width="100">
+            <template #default="scope">
+              <el-link v-if="scope.row.URL" :href="scope.row.URL" target="_blank" type="primary">下载</el-link>
+              <span v-else>-</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <pagination v-show="fileTotal > 0" :total="fileTotal" v-model:page="filePageNum" v-model:limit="filePageSize" @pagination="getFileList" />
+      </el-tab-pane>
+
+      <!-- 工作动态 Tab -->
+      <el-tab-pane label="工作动态" name="articles">
+        <el-form :model="articleQuery" :inline="true">
+          <el-form-item label="标题">
+            <el-input v-model="articleQuery.title" placeholder="请输入标题" clearable style="width:200px" />
+          </el-form-item>
+          <el-form-item label="类型">
+            <el-select v-model="articleQuery.type" placeholder="动态类型" clearable style="width:150px">
+              <el-option label="水利动态" value="0" />
+              <el-option label="水利要闻" value="1" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="getArticleList">搜索</el-button>
+            <el-button icon="Refresh" @click="resetArticleQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+        <el-table v-loading="articleLoading" :data="articleList" border>
+          <el-table-column label="标题" prop="TITLE" :show-overflow-tooltip="true" />
+          <el-table-column label="类型" prop="TYPE" width="100">
+            <template #default="scope">{{ scope.row.TYPE === '0' ? '水利动态' : '水利要闻' }}</template>
+          </el-table-column>
+          <el-table-column label="发布人" prop="PUBMAN" width="120" />
+          <el-table-column label="发布时间" prop="PUBTIME" width="160" />
+        </el-table>
+        <pagination v-show="articleTotal > 0" :total="articleTotal" v-model:page="articlePageNum" v-model:limit="articlePageSize" @pagination="getArticleList" />
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { listWorkPlan } from '@/api/hzz/workplan'
+import { listFile, listArticle } from '@/api/hzz/rule'
+
+const activeTab = ref('workplan')
+
+// ========== 工作方案 ==========
+const wpLoading = ref(false)
+const workPlanList = ref([])
+const getWorkPlanList = async () => {
+  wpLoading.value = true
+  try {
+    const res = await listWorkPlan('')
+    workPlanList.value = res.data || []
+  } finally { wpLoading.value = false }
+}
+
+// ========== 制度文件 ==========
+const fileLoading = ref(false)
+const fileList = ref([])
+const fileTotal = ref(0)
+const filePageNum = ref(1)
+const filePageSize = ref(10)
+const fileQuery = ref({ fileName: '', fileType: '' })
+const getFileList = async () => {
+  fileLoading.value = true
+  try {
+    const res = await listFile({ ...fileQuery.value, types: '0', pageNum: filePageNum.value, pageSize: filePageSize.value })
+    fileList.value = res.rows || []
+    fileTotal.value = res.total || 0
+  } finally { fileLoading.value = false }
+}
+const resetFileQuery = () => { fileQuery.value = { fileName: '', fileType: '' }; getFileList() }
+
+// ========== 工作动态 ==========
+const articleLoading = ref(false)
+const articleList = ref([])
+const articleTotal = ref(0)
+const articlePageNum = ref(1)
+const articlePageSize = ref(10)
+const articleQuery = ref({ title: '', type: '' })
+const getArticleList = async () => {
+  articleLoading.value = true
+  try {
+    const res = await listArticle({ ...articleQuery.value, pageNum: articlePageNum.value, pageSize: articlePageSize.value })
+    articleList.value = res.rows || []
+    articleTotal.value = res.total || 0
+  } finally { articleLoading.value = false }
+}
+const resetArticleQuery = () => { articleQuery.value = { title: '', type: '' }; getArticleList() }
+
+onMounted(() => {
+  getWorkPlanList()
+  getFileList()
+  getArticleList()
+})
+</script>

+ 66 - 0
gw-ui/src/views/hlgl/eventclear/index.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" :inline="true" v-show="showSearch">
+      <el-form-item label="行政区划">
+        <el-input v-model="queryParams.adCode" placeholder="请输入行政区划编码" clearable style="width:180px" />
+      </el-form-item>
+      <el-form-item label="事件类型">
+        <el-input v-model="queryParams.evType" placeholder="请输入事件类型" clearable style="width:150px" />
+      </el-form-item>
+      <el-form-item label="关键词">
+        <el-input v-model="queryParams.keyWord" placeholder="河段名/描述/区划" clearable style="width:200px" @keyup.enter="getList" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="Search" @click="getList">搜索</el-button>
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
+    </el-row>
+
+    <el-table v-loading="loading" :data="eventList" border>
+      <el-table-column label="序号" type="index" width="55" align="center" />
+      <el-table-column label="河段名称" prop="OBJECT_NAME" :show-overflow-tooltip="true" width="150" />
+      <el-table-column label="事件类型" prop="EV_TYPE" width="110" />
+      <el-table-column label="事件类别" prop="EVENT_TYPES" width="110" />
+      <el-table-column label="问题描述" prop="EVENT_DESC" :show-overflow-tooltip="true" min-width="200" />
+      <el-table-column label="当前情况" prop="STATUS" width="100" />
+      <el-table-column label="整改情况" prop="DEAL_DESC" :show-overflow-tooltip="true" width="150" />
+      <el-table-column label="检查情况" width="100">
+        <template #default="scope">{{ scope.row.STATUS === '1' ? '已整改' : '未整改' }}</template>
+      </el-table-column>
+      <el-table-column label="发现时间" prop="FIND_TS" width="160" />
+      <el-table-column label="整改完成" prop="DONE_DATE" width="160" />
+      <el-table-column label="上报人" prop="USER_NAME" width="100" />
+      <el-table-column label="行政区域" prop="AD_NAME" width="150" />
+    </el-table>
+    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { listEventClear } from '@/api/hzz/eventclear'
+
+const showSearch = ref(true)
+const loading = ref(false)
+const total = ref(0)
+const pageNum = ref(1)
+const pageSize = ref(10)
+const eventList = ref([])
+const queryParams = ref({ adCode: '', evType: '', keyWord: '', objectType: '' })
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const res = await listEventClear({ ...queryParams.value, pageNum: pageNum.value, pageSize: pageSize.value })
+    eventList.value = res.rows || []
+    total.value = res.total || 0
+  } finally { loading.value = false }
+}
+const resetQuery = () => { queryParams.value = { adCode: '', evType: '', keyWord: '', objectType: '' }; getList() }
+
+onMounted(() => getList())
+</script>

+ 292 - 0
gw-ui/src/views/hlgl/gzfa/index.vue

@@ -0,0 +1,292 @@
+<template>
+  <div class="gzfa-page">
+    <!-- 左侧行政区划树 -->
+    <div class="left-tree">
+      <div class="tree-title">行政区划</div>
+      <el-input v-model="treeFilter" placeholder="搜索" size="small" clearable style="margin:8px"/>
+      <el-tree
+          :data="treeData" :props="treeProps" node-key="adCode"
+          :filter-node-method="filterNode" ref="treeRef"
+          highlight-current lazy :load="loadTreeNode"
+          @node-click="onNodeClick"
+      />
+    </div>
+
+    <!-- 右侧内容 -->
+    <div class="right-content" v-loading="loading">
+      <!-- 当前选中区域 -->
+      <div class="current-area" v-if="currentAdName">
+        当前区域:<strong>{{ currentAdName }}</strong>
+      </div>
+
+      <!-- 全国视图:中央/国务院/水利部文件 -->
+      <template v-if="isNational">
+        <div class="file-section" v-for="cat in fileCategories" :key="cat.title">
+          <h3 class="section-title">{{ cat.title }}</h3>
+          <el-table :data="cat.data" border size="small">
+            <el-table-column label="文件名称" prop="fileName" show-overflow-tooltip>
+              <template #default="{row}">
+                <el-link type="primary" @click="showDetail(row)">{{ row.fileName || row.FILE_NAME }}</el-link>
+              </template>
+            </el-table-column>
+            <el-table-column label="文号" prop="wh" width="200">
+              <template #default="{row}">{{ row.wh || row.WH }}</template>
+            </el-table-column>
+            <el-table-column label="出台时间" width="140">
+              <template #default="{row}">{{ fmtDate(row.startDate || row.START_DATE) }}</template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </template>
+
+      <!-- 地区视图:工作方案 + 工作制度 -->
+      <template v-else>
+        <div class="file-section">
+          <h3 class="section-title">工作方案</h3>
+          <el-table :data="workPlans" @row-click="showDetail">
+            <el-table-column label="方案名称" prop="wpName" show-overflow-tooltip/>
+            <el-table-column label="文号" prop="fileNum" width="200"/>
+            <el-table-column label="发布日期" prop="rlsTm" width="140"/>
+            <el-table-column label="发布单位" prop="releWiunName" width="180"/>
+          </el-table>
+          <el-empty v-if="!workPlans.length" description="暂无工作方案"/>
+        </div>
+
+        <div class="file-section" style="margin-top:20px">
+          <h3 class="section-title">工作制度</h3>
+          <el-table :data="ruleData">
+            <el-table-column label="制度类别" prop="sysType" width="160">
+              <template #default="scope">
+                {{ sysTypeFilter(scope.row) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="制度名称" prop="sysName" show-overflow-tooltip/>
+            <el-table-column label="状态" prop="ruleProgress" width="100">
+              <template #default="scope">
+                {{ ruleProgressFilter(scope.row) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="文号" prop="fileNum" width="200"/>
+            <el-table-column label="出台时间" prop="rlsTm" width="140"/>
+          </el-table>
+          <el-empty v-if="!ruleData.length" description="暂无工作制度"/>
+        </div>
+      </template>
+    </div>
+
+    <!-- 文件详情弹窗 -->
+    <el-dialog v-model="detailVisible" :title="detailItem?.wpName" width="700px">
+      <iframe-view :url="detailItem?.url" style="height: 79vh;"></iframe-view>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {computed, onMounted, reactive, ref, watch} from 'vue'
+import {listFile, listRule} from '@/api/hzz/rule'
+import {getChildren} from '@/api/hzz/adtree'
+import {listWorkPlan} from '@/api/hzz/workplan'
+import IframeView from "@/views/hlgl/IframeView.vue";
+
+const loading = ref(false)
+const treeRef = ref(null)
+const treeFilter = ref('')
+const treeData = ref([])
+const treeProps = {label: 'adName', children: 'children'}
+
+const currentAdCode = ref('000000000000')
+const currentAdName = ref('太湖流域')
+const isNational = computed(() => currentAdCode.value === '000000000000')
+
+const fileCategories = reactive([
+  {title: '中央文件', key: '0', data: []},
+  {title: '国务院文件', key: '1', data: []},
+  {title: '水利部文件', key: '2', data: []},
+])
+
+const workPlans = ref([])
+const ruleData = ref([])
+const detailVisible = ref(false)
+const detailItem = ref(null)
+
+const fmtDate = (d) => d ? String(d).substring(0, 10) : '-'
+
+// 加载文件列表
+const loadFiles = async () => {
+  try {
+    const res = await listFile({types: '0', pageNum: 1, pageSize: 500})
+    const rows = res.rows || []
+    fileCategories[0].data = rows.filter(r => (r.FILE_TYPE || r.fileType) === '0')
+    fileCategories[1].data = rows.filter(r => (r.FILE_TYPE || r.fileType) === '1')
+    fileCategories[2].data = rows.filter(r => (r.FILE_TYPE || r.fileType) === '2')
+  } catch { /* ok */
+  }
+}
+
+// 加载工作方案 + 制度
+const loadAreaData = async () => {
+  workPlans.value = []
+  ruleData.value = []
+  if (isNational.value) return
+  try {
+    const [wpRes, ruleRes] = await Promise.all([
+      listWorkPlan(currentAdCode.value),
+      listRule(currentAdCode.value, '')
+    ])
+    workPlans.value = wpRes.data || []
+    ruleData.value = ruleRes.data || []
+  } catch { /* ok */
+  }
+}
+
+// 加载行政区划树
+const loadTree = async () => {
+  // 根节点从数据库加载
+  const res = await getChildren('000000000000')
+  treeData.value = (res.data || []).map(item => ({
+    ...item,
+    adName: item.AD_NAME, adCode: item.AD_CODE
+  }))
+}
+
+// 懒加载子节点(逐级下钻)
+const loadTreeNode = async (node, resolve) => {
+  if (node.level === 0) {
+    // 根节点已在 loadTree 中加载
+    return resolve(treeData.value)
+  }
+  try {
+    const res = await getChildren(node.data.adCode)
+    const children = (res.data || []).map(item => ({
+      ...item,
+      adName: item.AD_NAME,
+      adCode: item.AD_CODE,
+      leaf: item.AD_GRAD >= '5' // 村级为叶子节点
+    }))
+    resolve(children)
+  } catch {
+    resolve([])
+  }
+}
+
+const onNodeClick = (node) => {
+  currentAdCode.value = node.adCode
+  currentAdName.value = node.adName
+  if (!isNational.value) loadAreaData()
+}
+
+const sysTypeFilter = (item) => {
+  switch (item.sysType) {
+    case '1':
+      return '河长会议制度'
+    case '2':
+      return '信息报送制度'
+    case '3':
+      return '工作督察制度'
+    case '4':
+      return '考核问责与激励制度'
+    case '5':
+      return '验收制度'
+    case '6':
+      return '信息共享制度'
+    default:
+      return '其他'
+  }
+}
+
+const ruleProgressFilter = (item) => {
+  if (item.ruleProgress === '1') {
+    return '研讨'
+  } else if (item.ruleProgress === '2') {
+    return '起草'
+  } else if (item.ruleProgress === '3') {
+    return '征求意见'
+  } else if (item.ruleProgress === '4') {
+    return '报批'
+  } else if (item.ruleProgress === '5') {
+    return '印发'
+  }
+}
+
+const showDetail = (item) => {
+  detailVisible.value = true
+  detailItem.value = item
+  // if (item.url.substring(item.url.length - 3) === 'doc' || item.url.substring(item.url.length - 4) === 'docx') {
+  //   detailVisible.value = false;
+  //   window.open(item.url);
+  // } else if (item.url.substring(item.url.length - 3) === 'pdf') {
+  //   detailVisible.value = true
+  //   detailItem.value = item
+  // }
+}
+
+const filterNode = (value, data) => {
+  if (!value) return true
+  return data.adName?.includes(value)
+}
+
+watch(treeFilter, (val) => {
+  treeRef.value?.filter(val)
+})
+
+onMounted(async () => {
+  loading.value = true
+  await Promise.all([loadFiles(), loadTree()])
+  loading.value = false
+})
+</script>
+
+<style scoped>
+.gzfa-page {
+  display: flex;
+  height: calc(100vh - 60px);
+}
+
+.left-tree {
+  width: 260px;
+  flex-shrink: 0;
+  border-right: 1px solid #e8e8e8;
+  overflow-y: auto;
+  background: #fafbfc;
+}
+
+.tree-title {
+  padding: 12px 16px;
+  font-weight: 600;
+  font-size: 15px;
+  color: #333;
+  border-bottom: 1px solid #eee;
+}
+
+.right-content {
+  flex: 1;
+  overflow-y: auto;
+  padding: 20px;
+}
+
+.file-section {
+  margin-bottom: 16px;
+}
+
+.section-title {
+  font-size: 16px;
+  color: #0d4b80;
+  padding: 8px 0;
+  margin: 0 0 8px 0;
+  border-bottom: 2px solid #6baed6;
+}
+
+.current-area {
+  margin-bottom: 20px;
+  padding: 10px;
+  background: #f0f7ff;
+  border-radius: 6px;
+  font-size: 13px;
+  color: #555;
+}
+
+.detail-info p {
+  margin: 8px 0;
+  font-size: 14px;
+}
+</style>

+ 36 - 0
gw-ui/src/views/hlgl/khwj/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div class="app-container">
+    <el-table v-loading="loading" :data="list" border>
+      <el-table-column label="考核名称" prop="ASS_NAME" show-overflow-tooltip min-width="180" />
+      <el-table-column label="文件标题" prop="FILE_TITL" show-overflow-tooltip min-width="200" />
+      <el-table-column label="提交单位" prop="ENTER_UNIT" width="160" />
+      <el-table-column label="总分" prop="TOTAL_POINTS" width="80" />
+      <el-table-column label="通过线" prop="ACCEPTANCE_LINE" width="80" />
+      <el-table-column label="提交时间" prop="ENTER_TM" width="120" />
+      <el-table-column label="附件" width="100">
+        <template #default="{row}">
+          <el-link v-if="row.FILE_PATH" :href="row.FILE_PATH" target="_blank" type="primary">查看</el-link>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { getMethodFile } from '@/api/hzz/assess'
+
+const loading = ref(false)
+const list = ref([])
+
+onMounted(async () => {
+  loading.value = true
+  try {
+    // 默认查询太湖流域考核文件
+    const res = await getMethodFile('', new Date().toISOString().slice(0, 10))
+    list.value = res.data || []
+  } catch { /* 接口可能未就绪 */ }
+  finally { loading.value = false }
+})
+</script>

+ 78 - 0
gw-ui/src/views/hlgl/onemap/components/FeaturePopup.vue

@@ -0,0 +1,78 @@
+<template>
+  <Teleport to="body">
+    <div v-if="visible" class="feature-popup-overlay" @click.self="close">
+      <div class="feature-popup" :style="{ top: y + 'px', left: x + 'px' }">
+        <div class="popup-header">
+          <span class="popup-title">{{ title }}</span>
+          <el-icon class="popup-close" @click="close"><Close /></el-icon>
+        </div>
+        <div class="popup-body">
+          <div v-for="(v, k) in attributes" :key="k" class="popup-row">
+            <span class="popup-label">{{ k }}</span>
+            <span class="popup-value">{{ formatValue(v) }}</span>
+          </div>
+          <div v-if="!attributes || !Object.keys(attributes).length" class="popup-empty">
+            无详细数据
+          </div>
+        </div>
+        <div class="popup-actions" v-if="actions && actions.length">
+          <el-button v-for="act in actions" :key="act.label" size="small"
+            @click="act.handler(attributes)">{{ act.label }}</el-button>
+        </div>
+      </div>
+    </div>
+  </Teleport>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const visible = ref(false)
+const title = ref('')
+const attributes = ref({})
+const actions = ref([])
+const x = ref(200)
+const y = ref(100)
+
+const show = (opts) => {
+  title.value = opts.title || '要素信息'
+  attributes.value = opts.attributes || {}
+  actions.value = opts.actions || []
+  x.value = Math.min(opts.x || 200, window.innerWidth - 380)
+  y.value = Math.min(opts.y || 100, window.innerHeight - 400)
+  visible.value = true
+}
+
+const close = () => { visible.value = false }
+
+const formatValue = (v) => {
+  if (v === null || v === undefined) return '-'
+  if (typeof v === 'number') return v.toFixed(v % 1 ? 6 : 0)
+  return String(v)
+}
+
+defineExpose({ show, close })
+</script>
+
+<style scoped>
+.feature-popup-overlay { position: fixed; inset: 0; z-index: 1000; }
+.feature-popup {
+  position: absolute; width: 360px; max-height: 500px;
+  background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,.25);
+  display: flex; flex-direction: column; overflow: hidden;
+}
+.popup-header {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 10px 14px; background: #409EFF; color: #fff; font-size: 14px; font-weight: 500;
+}
+.popup-close { cursor: pointer; }
+.popup-body { flex: 1; overflow-y: auto; padding: 8px 14px; }
+.popup-row {
+  display: flex; justify-content: space-between; padding: 4px 0;
+  border-bottom: 1px dashed #eee; font-size: 13px;
+}
+.popup-label { color: #888; min-width: 80px; }
+.popup-value { color: #333; text-align: right; word-break: break-all; }
+.popup-empty { color: #999; text-align: center; padding: 20px; }
+.popup-actions { padding: 8px 14px; border-top: 1px solid #eee; display: flex; gap: 8px; }
+</style>

+ 114 - 0
gw-ui/src/views/hlgl/onemap/components/InfoPanel.vue

@@ -0,0 +1,114 @@
+<template>
+  <Teleport to="body">
+    <transition name="panel-slide">
+      <div v-if="visible" class="info-panel">
+        <div class="panel-hd">
+          <span class="panel-title">{{ title }}</span>
+          <el-icon class="panel-close" @click="close"><Close /></el-icon>
+        </div>
+        <div class="panel-tabs">
+          <el-tabs v-model="activeTab" type="border-card">
+            <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.name" :name="tab.name">
+              <div class="tab-content" v-loading="tab.loading">
+                <!-- 键值对布局 -->
+                <template v-if="tab.type === 'kv'">
+                  <div class="kv-list">
+                    <div class="kv-row" v-for="(v, k) in tab.data" :key="k">
+                      <span class="kv-key">{{ tab.labelMap?.[k] || k }}</span>
+                      <span class="kv-value">{{ formatVal(v) }}</span>
+                    </div>
+                  </div>
+                </template>
+                <!-- 表格数据 -->
+                <template v-else-if="tab.type === 'table' && tab.data">
+                  <el-table :data="Array.isArray(tab.data) ? tab.data : [tab.data]" border size="small" max-height="420">
+                    <el-table-column v-for="col in tab.columns" :key="col.key"
+                      :prop="col.key" :label="col.label" :width="col.width"
+                      :show-overflow-tooltip="true" />
+                  </el-table>
+                </template>
+                <!-- HTML 内容 -->
+                <template v-else-if="tab.type === 'html'">
+                  <div v-html="tab.html" />
+                </template>
+                <!-- 空 -->
+                <template v-else>
+                  <el-empty description="暂无数据" />
+                </template>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+      </div>
+    </transition>
+  </Teleport>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const visible = ref(false)
+const title = ref('')
+const activeTab = ref('')
+const tabs = ref([])
+
+const show = (opts) => {
+  title.value = opts.title || '详情'
+  tabs.value = opts.tabs || [{ name: '基本信息', type: 'table', data: opts.attributes, loading: false }]
+  activeTab.value = tabs.value[0]?.name || ''
+  visible.value = true
+}
+
+const close = () => { visible.value = false }
+
+const updateTab = (tabName, data) => {
+  const tab = tabs.value.find(t => t.name === tabName)
+  if (tab) {
+    tab.data = data
+    tab.loading = false
+  }
+}
+
+const formatVal = (v) => {
+  if (v === null || v === undefined) return '-'
+  return String(v)
+}
+
+defineExpose({ show, close, updateTab })
+</script>
+
+<style scoped>
+.info-panel {
+  position: fixed; right: 0; top: 0; bottom: 0; width: 420px; z-index: 1001;
+  background: #fff; box-shadow: -4px 0 20px rgba(0,0,0,.15);
+  display: flex; flex-direction: column;
+}
+.panel-hd {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 14px 18px; background: #409EFF; color: #fff;
+  font-size: 15px; font-weight: 500; flex-shrink: 0;
+}
+.panel-close { cursor: pointer; font-size: 18px; }
+.panel-tabs { flex: 1; overflow: hidden; }
+.panel-tabs :deep(.el-tabs) { height: 100%; display: flex; flex-direction: column; }
+.panel-tabs :deep(.el-tabs__content) { flex: 1; overflow-y: auto; padding: 8px; }
+.tab-content { min-height: 200px; }
+
+.kv-list { padding: 4px 0; }
+.kv-row {
+  display: flex; align-items: flex-start; padding: 6px 0;
+  border-bottom: 1px dashed #e8e8e8; font-size: 13px;
+}
+.kv-key {
+  min-width: 90px; max-width: 130px; color: #888; flex-shrink: 0;
+  text-align: right; padding-right: 12px; word-break: break-all;
+}
+.kv-value {
+  color: #333; flex: 1; word-break: break-all;
+}
+
+.panel-slide-enter-active { transition: transform .3s ease; }
+.panel-slide-leave-active { transition: transform .25s ease; }
+.panel-slide-enter-from { transform: translateX(100%); }
+.panel-slide-leave-to { transform: translateX(100%); }
+</style>

+ 125 - 0
gw-ui/src/views/hlgl/onemap/components/LayerMenu.vue

@@ -0,0 +1,125 @@
+<template>
+  <div class="layer-menu">
+    <!-- 侧边 Tab -->
+    <div class="tab-bar">
+      <div v-for="(g, i) in groups" :key="i"
+        class="tab-item" :class="{ active: activeTab === i }"
+        @click="onTabClick(i)">
+        <span class="tab-text">{{ g.tab }}</span>
+      </div>
+    </div>
+
+    <!-- 展开面板 -->
+    <div class="layer-panel" v-show="activeTab !== null">
+      <div class="panel-header">
+        <span>{{ groups[activeTab]?.tab || '' }}</span>
+        <el-icon class="close-btn" @click="activeTab = null"><Close /></el-icon>
+      </div>
+      <div class="panel-body">
+        <div v-for="(cat, ci) in currentCategories" :key="activeTab + '-' + ci" class="layer-category">
+          <div class="cat-title" @click="cat.expanded = !cat.expanded">
+            <span class="cat-arrow">{{ cat.expanded ? '▼' : '▶' }}</span>
+            <span>{{ cat.title }}</span>
+          </div>
+          <div v-show="cat.expanded" class="cat-items">
+            <div v-for="layer in cat.layers" :key="layer.id"
+              class="layer-item" :class="{ active: layer.checked }"
+              @click="onLayerClick(layer)">
+              <span class="check-box">{{ layer.checked ? '☑' : '☐' }}</span>
+              <span class="layer-name">{{ layer.name }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue'
+import { layerGroups } from '../config/layerMenu'
+
+const emit = defineEmits(['layerToggle'])
+
+// 响应式数据:用 ref 保证深度响应
+const groups = ref(
+  layerGroups.map(g => ({
+    tab: g.tab,
+    categories: g.categories.map(c => ({
+      title: c.title,
+      expanded: true,
+      layers: c.layers.map(l => ({ ...l, checked: false }))
+    }))
+  }))
+)
+
+const activeTab = ref(null)
+
+const currentCategories = computed(() => {
+  if (activeTab.value === null) return []
+  return groups.value[activeTab.value]?.categories || []
+})
+
+const onTabClick = (i) => {
+  activeTab.value = activeTab.value === i ? null : i
+}
+
+const onLayerClick = (layer) => {
+  layer.checked = !layer.checked
+  emit('layerToggle', { layer, checked: layer.checked })
+}
+</script>
+
+<style scoped>
+.layer-menu { position: absolute; left: 0; top: 0; bottom: 0; z-index: 20; display: flex; pointer-events: none; }
+.layer-menu * { pointer-events: auto; }
+
+.tab-bar {
+  width: 40px; background: rgba(27,53,111,.85); display: flex;
+  flex-direction: column; padding-top: 8px; flex-shrink: 0;
+}
+.tab-item {
+  width: 100%; height: 100px; display: flex; align-items: center;
+  justify-content: center; cursor: pointer; writing-mode: vertical-rl;
+  color: #ccc; font-size: 13px; letter-spacing: 4px;
+  border-bottom: 1px solid rgba(255,255,255,.1); user-select: none;
+}
+.tab-item:hover { color: #fff; }
+.tab-item.active { background: rgba(43,102,223,.8); color: #fff; }
+
+.layer-panel {
+  width: 310px; background: rgba(43,68,156,.92); color: #fff;
+  display: flex; flex-direction: column; overflow: hidden;
+  border-radius: 0 8px 8px 0; box-shadow: 4px 0 12px rgba(0,0,0,.3);
+}
+.panel-header {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 12px 16px; border-bottom: 1px solid rgba(255,255,255,.15);
+  font-size: 15px; font-weight: 500; flex-shrink: 0;
+}
+.close-btn { cursor: pointer; }
+.close-btn:hover { color: #7ec8f8; }
+
+.panel-body { flex: 1; overflow-y: auto; padding: 8px 0; }
+
+.layer-category { margin-bottom: 2px; }
+.cat-title {
+  display: flex; align-items: center; gap: 6px; padding: 10px 16px;
+  font-size: 13px; font-weight: 500; cursor: pointer; user-select: none;
+  background: rgba(255,255,255,.06); border-bottom: 1px solid rgba(255,255,255,.08);
+}
+.cat-title:hover { background: rgba(255,255,255,.12); }
+.cat-arrow { font-size: 10px; width: 14px; }
+
+.cat-items { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 12px; }
+
+.layer-item {
+  width: calc(33.33% - 4px); display: flex; align-items: center;
+  gap: 4px; padding: 6px 8px; border-radius: 4px; cursor: pointer;
+  font-size: 12px; transition: background .15s; user-select: none;
+}
+.layer-item:hover { background: rgba(255,255,255,.12); }
+.layer-item.active { background: rgba(38,11,150,.6); }
+.check-box { font-size: 14px; flex-shrink: 0; }
+.layer-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+</style>

+ 132 - 0
gw-ui/src/views/hlgl/onemap/config/layerMenu.js

@@ -0,0 +1,132 @@
+/**
+ * 左侧图层目录 - 完全对照旧代码 leftLayerMenuConfig.js
+ *
+ * 图层ID分两类:
+ * 1. th_*  / anxian* → ArcGIS 要素服务层(mapContent预加载为FeatureLayer/MapImageLayer,勾选=切换visible)
+ * 2. oneMap_*       → SuperMap WMTS瓦片层(勾选=动态创建WebTileLayer添加/移除)
+ */
+
+// ============ 太湖一张图 ============
+const page1 = [
+  {
+    title: '基础信息',
+    layers: [
+      { id: 'th_jcys0',     name: '河流',       icon: 'icon-heliu' },
+      { id: 'th_jcys1',     name: '湖泊',       icon: 'icon-hubo' },
+      { id: 'th_jcys2',     name: '水库',       icon: 'icon-shuiku' },
+      { id: 'th_gjjbswz1', name: '水文',       icon: 'icon-shuikushuiwenzhan' },
+      { id: 'th_gjjbswz7', name: '水位',       icon: 'icon-shuiweizhan' },
+      { id: 'th_gjjbswz8', name: '雨量',       icon: 'icon-yuliangzhan' },
+      { id: 'th_dbsqsh0',  name: '取水户',     icon: 'watericon1-quyongshuicezhan' },
+      { id: 'th_slfq',     name: '水利分区',   icon: 'icon-hehuqushuikou' },
+      { id: 'th_bz',       name: '泵站',       icon: 'icon-bengzhan' },
+      { id: 'th_xjb',      name: '橡胶坝',     icon: 'icon-xiangjiaoba' },
+      { id: 'th_gq',       name: '灌区',       icon: 'icon-guanqu' },
+      { id: 'th_qd',       name: '渠道',       icon: 'icon-qudao' },
+      { id: 'th_ydsgc',    name: '引调水工程', icon: 'icon-tangyanbagongcheng' },
+    ]
+  },
+  {
+    title: '一湖两河口门',
+    layers: [
+      { id: 'th_slgc1',  name: '环湖口门',   icon: 'icon-shuishuiyuandi' },
+      { id: 'th_slgc2',  name: '太浦河口门', icon: 'icon-shuishuiyuandi' },
+      { id: 'th_slgc3',  name: '望虞河口门', icon: 'icon-shuishuiyuandi' },
+    ]
+  },
+  {
+    title: '堤防岸段',
+    layers: [
+      { id: 'th_slgc15', name: '一级堤防',   icon: 'icon-difanggongcheng' },
+      { id: 'th_slgc16', name: '二三级堤防', icon: 'icon-difanggongcheng' },
+      { id: 'th_slgc17', name: '其他堤防',   icon: 'icon-difanggongcheng' },
+    ]
+  },
+  {
+    title: '岸线',
+    layers: [
+      { id: 'anxian23', name: '太湖岸线',       icon: 'icon-hubo' },
+      { id: 'anxian27', name: '太湖岛屿岸线',   icon: 'icon-hubo' },
+      { id: 'anxian24', name: '淀山湖',         icon: 'icon-hubo' },
+      { id: 'anxian25', name: '太浦河',         icon: 'icon-hubo' },
+      { id: 'anxian26', name: '望虞河',         icon: 'icon-hubo' },
+    ]
+  },
+  {
+    title: '项目',
+    layers: [
+      { id: 'shGraphicLayer', name: '涉河项目', icon: 'icon-shuilishiyedanwei' },
+    ]
+  },
+  {
+    title: '省内河流(1000k㎡以上)和湖泊(1k㎡以上)',
+    layers: [
+      { id: 'js', name: '江苏省' },
+      { id: 'zj', name: '浙江省' },
+      { id: 'sh', name: '上海市' },
+      { id: 'ah', name: '安徽省' },
+      { id: 'fj', name: '福建省' },
+    ]
+  }
+]
+
+// ============ 专题数据 (WMTS) ============
+const page2 = [
+  {
+    title: '国家基础数据',
+    layers: [
+      { id: 'oneMap_xzqh', name: '行政区划', url: '/iserver/services/XZQH_HZZR/wmts100?k=CF36000BBA2E31E84B100C0C4C3694C6', tileMatrixSet: 'Custom_XZQH_HZZR' },
+    ]
+  },
+  {
+    title: '水利基础数据',
+    layers: [
+      { id: 'oneMap_hl',     name: '河流',         url: '/iserver/services/RIVER_R/wmts100?k=D0068B86A8FF78D4595A3001B2848723', tileMatrixSet: 'Custom_RIVER_R' },
+      { id: 'oneMap_hp',     name: '湖泊',         url: '/iserver/services/LAKE_R/wmts100?k=AAF9C677F714D313589876081DB07AD7', tileMatrixSet: 'Custom_LAKE_R' },
+      { id: 'oneMap_lyfq',   name: '流域分区',     url: '/iserver/services/BAS_R/wmts100?k=43590F1B5DFD1EC8806DE79E2EBF0E50', tileMatrixSet: 'Custom_BAS_R' },
+      { id: 'oneMap_sk',     name: '水库',         url: '/iserver/services/RSWB_R/wmts100?k=AA43929573DEE529ED8A853448B01C4F', tileMatrixSet: 'Custom_RSWB_R' },
+      { id: 'oneMap_swz',    name: '观测站',       url: '/iserver/services/OBS_R/wmts100?k=D5BA1A4825371A32E57DEE6E2B0EFDB4', tileMatrixSet: 'Custom_OBS_R' },
+      { id: 'oneMap_sz',     name: '水闸',         url: '/iserver/services/GATE_R/wmts100?k=6C58317ED2F2E1D931E0EEC73B17166B', tileMatrixSet: 'Custom_GATE_R' },
+      { id: 'oneMap_bz',     name: '泵站',         url: '/iserver/services/PUMP_R/wmts100?k=3C45E2ECF47A9D2854B05179D45EA731', tileMatrixSet: 'Custom_PUMP_R' },
+      { id: 'oneMap_df',     name: '堤防',         url: '/iserver/services/DIKE_R/wmts100?k=3B635F40A7B2A7C951B30BEFB8EB66F1', tileMatrixSet: 'Custom_DIKE_R' },
+      { id: 'oneMap_qd',     name: '渠道',         url: '/iserver/services/CH_R/wmts100?k=31B770B196607102AB792D0355703BB5', tileMatrixSet: 'Custom_CH_R' },
+      { id: 'oneMap_xjb',    name: '橡胶坝',       url: '/iserver/services/RUB_R/wmts100?k=28B2DE3EA2B7F19E9D19C90AE01D2275', tileMatrixSet: 'Custom_RUB_R' },
+      { id: 'oneMap_ydb',    name: '淤地坝',       url: '/iserver/services/SD_R/wmts100?k=C0634C8983D83D6C826F7E9BAB3A5BFD', tileMatrixSet: 'Custom_SD_R' },
+      { id: 'oneMap_ncgsgc', name: '农村供水工程', url: '/iserver/services/CWS_R/wmts100?k=B7E567F7BAA2D6290162457556CE164C', tileMatrixSet: 'Custom_CWS_R' },
+      { id: 'oneMap_gq',     name: '灌区',         url: '/iserver/services/IRR_R/wmts100?k=B4D108DD49C72FA872169540952B71D8', tileMatrixSet: 'Custom_IRR_R' },
+      { id: 'oneMap_sdz',    name: '水电站',       url: '/iserver/services/HYPO_R/wmts100?k=48D0C9EB71602144508B0A0B1457BE18', tileMatrixSet: 'Custom_HYPO_R' },
+      { id: 'oneMap_ydsgc',  name: '引调水工程',   url: '/iserver/services/DITR_R/wmts100?k=A51B0F16A82DFE1207D036AA097610ED', tileMatrixSet: 'Custom_DITR_R' },
+    ]
+  },
+  {
+    title: '水资源业务专题',
+    layers: [
+      { id: 'oneMap_szyfq',    name: '水资源分区',     url: '/iserver/services/WRZ_R/wmts100?k=6BD4BA8E4303F3B7C373FECD1C87DA04', tileMatrixSet: 'Custom_WRZ_R' },
+      { id: 'oneMap_qysh',     name: '取用水户',       url: '/iserver/services/WIU_R/wmts100?k=3D5E9872F3964CACE4A2211B3C779CE8', tileMatrixSet: 'Custom_WIU_R' },
+      { id: 'oneMap_qyscz',    name: '取用水测站',     url: '/iserver/services/WIUST_R/wmts100?k=EB991CE7C53ABBFCA0CC3B3D89D7C8C4', tileMatrixSet: 'Custom_WIUST_R' },
+      { id: 'oneMap_dbssyd',   name: '地表水水源地',   url: '/iserver/services/SUWS_R/wmts100?k=316B3101D13C495A916309D93BD092E5', tileMatrixSet: 'Custom_SUWS_R' },
+      { id: 'oneMap_sgnqh',    name: '水功能区划',     url: '/iserver/services/WFZ_R/wmts100?k=A2F853E3E3F2B018D2782B0073FB9A21', tileMatrixSet: 'Custom_WFZ_R' },
+      { id: 'oneMap_sgnqjcdm', name: '水功能区监测断面', url: '/iserver/services/WFZB_5T_R/wmts100?k=8E228FC0771A0796C49B9F6764064159', tileMatrixSet: 'Custom_WFZB_5T_R' },
+      { id: 'oneMap_dxsqsj',   name: '地下水取水井',   url: '/iserver/services/WELL_R/wmts100?k=2354ED0BA5ADFFF50FD3B156D50C6EC2', tileMatrixSet: 'Custom_WELL_R' },
+      { id: 'oneMap_sdz',      name: '水电站',         url: '/iserver/services/HYPO_R/wmts100?k=48D0C9EB71602144508B0A0B1457BE18', tileMatrixSet: 'Custom_HYPO_R' },
+    ]
+  },
+  {
+    title: '水利单位',
+    layers: [
+      { id: 'oneMap_sszzjg',   name: '涉水组织机构',   url: '/iserver/services/CORP_R/wmts100?k=0773DB0845A281A1E42BCA47A359E8C1', tileMatrixSet: 'Custom_CORP_R' },
+      { id: 'oneMap_slxzjg',   name: '水利行政机关',   url: '/iserver/services/OFLP_R/wmts100?k=A6422E9087AB728EA337E0F6ACD7765B', tileMatrixSet: 'Custom_OFLP_R' },
+      { id: 'oneMap_slqy',     name: '水利企业',       url: '/iserver/services/ENLP_R/wmts100?k=06008AAA85DC5040A993C4F8D24D7015', tileMatrixSet: 'Custom_ENLP_R' },
+      { id: 'oneMap_slshtt',   name: '水利社会团体',   url: '/iserver/services/COLP_R/wmts100?k=AE0D62E9E68CFEEA60469BD765743CEF', tileMatrixSet: 'Custom_COLP_R' },
+      { id: 'oneMap_slsydw',   name: '水利事业单位',   url: '/iserver/services/INLP_R/wmts100?k=79571A8D03A7FB8EA09168617BA8C94C', tileMatrixSet: 'Custom_INLP_R' },
+      { id: 'oneMap_xzslgldw', name: '乡镇水利管理单位', url: '/iserver/services/UNORTOWN_R/wmts100?k=95DE238C7DE44D12E4D41B5E12D0AEFC', tileMatrixSet: 'Custom_UNORTOWN_R' },
+    ]
+  }
+]
+
+export const layerGroups = [
+  { tab: '太湖一张图', categories: page1 },
+  { tab: '专题数据', categories: page2 }
+]
+
+export default layerGroups

+ 101 - 0
gw-ui/src/views/hlgl/onemap/config/layers.js

@@ -0,0 +1,101 @@
+/**
+ * 太湖一张图 ArcGIS 要素服务图层
+ * 所有FeatureLayer/MapImageLayer,地图初始时预加载(visible=false)
+ * 勾选图层时切换visible
+ */
+const ARCGIS = 'http://10.8.4.128/server/rest/services'
+
+export const taihuServiceLayers = [
+  // ===== 基础信息 =====
+  { id: 'th_jcys0',     name: '河流',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_HL/MapServer' },
+  { id: 'th_jcys1',     name: '湖泊',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_HP/MapServer' },
+  { id: 'th_jcys2',     name: '水库',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_SK/MapServer' },
+  { id: 'th_gjjbswz1', name: '水文',         type: 'mapimage',  url: ARCGIS + '/HHGL/HHGL_SW/MapServer' },
+  { id: 'th_gjjbswz7', name: '水位',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_SWZ/MapServer' },
+  { id: 'th_gjjbswz8', name: '雨量',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_YL/MapServer' },
+  { id: 'th_dbsqsh0',  name: '取水户',       type: 'feature',  url: ARCGIS + '/HHGL/HHGL_QSH/MapServer' },
+  { id: 'th_slfq',     name: '水利分区',     type: 'mapimage',  url: ARCGIS + '/HHGL/HHGL_SLFQ/MapServer' },
+  { id: 'th_bz',       name: '泵站',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_BZ/MapServer' },
+  // 以下暂用近似URL,后续确认
+  { id: 'th_xjb',      name: '橡胶坝',       type: 'feature',  url: ARCGIS + '/HHGL/HHGL_XJB/MapServer' },
+  { id: 'th_gq',       name: '灌区',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_GQ/MapServer' },
+  { id: 'th_qd',       name: '渠道',         type: 'feature',  url: ARCGIS + '/HHGL/HHGL_QD/MapServer' },
+  { id: 'th_ydsgc',    name: '引调水工程',   type: 'feature',  url: ARCGIS + '/HHGL/HHGL_YDSGC/MapServer' },
+
+  // ===== 一湖两河口门 =====
+  { id: 'th_slgc1',    name: '环湖口门',     type: 'feature',  url: ARCGIS + '/HHGL/HHGL_YHLHKM/MapServer/0' },
+  { id: 'th_slgc2',    name: '太浦河口门',   type: 'feature',  url: ARCGIS + '/HHGL/HHGL_YHLHKM/MapServer/1' },
+  { id: 'th_slgc3',    name: '望虞河口门',   type: 'feature',  url: ARCGIS + '/HHGL/HHGL_YHLHKM/MapServer/2' },
+
+  // ===== 堤防岸段 =====
+  { id: 'th_slgc15',   name: '一级堤防',     type: 'feature',  url: ARCGIS + '/HHGL/HHGL_DFAD/MapServer/0' },
+  { id: 'th_slgc16',   name: '二三级堤防',   type: 'feature',  url: ARCGIS + '/HHGL/HHGL_DFAD/MapServer/1' },
+  { id: 'th_slgc17',   name: '其他堤防',     type: 'feature',  url: ARCGIS + '/HHGL/HHGL_DFAD/MapServer/2' },
+
+  // ===== 岸线 =====
+  { id: 'anxian23',    name: '太湖岸线',     type: 'feature',  url: ARCGIS + '/HHGL/HHGL_AX/MapServer/0' },
+  { id: 'anxian27',    name: '太湖岛屿岸线', type: 'feature',  url: ARCGIS + '/HHGL/HHGL_AX/MapServer/4' },
+  { id: 'anxian24',    name: '淀山湖',       type: 'feature',  url: ARCGIS + '/HHGL/HHGL_AX/MapServer/1' },
+  { id: 'anxian25',    name: '太浦河',       type: 'feature',  url: ARCGIS + '/HHGL/HHGL_AX/MapServer/2' },
+  { id: 'anxian26',    name: '望虞河',       type: 'feature',  url: ARCGIS + '/HHGL/HHGL_AX/MapServer/3' },
+]
+
+// 底图 WMTS
+export const taihuBaseMap = {
+  id: 'taihu_basemap', title: '太湖一张图', type: 'mapserver',
+  url: ARCGIS + '/OneMap/TH_XZQH_2000/MapServer', visible: true
+}
+
+export const tianditu = {
+  img: { id: 'tianditu_img', title: '天地图影像',
+    urlTemplate: 'http://{subDomain}.mwr.gov.cn:81/img_c/wmts?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=img&STYLE=default&FORMAT=tiles&TILEMATRIXSET=c&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}',
+    subDomains: ['tdt'] },
+  imgLabel: { id: 'tianditu_img_label', title: '天地图影像注记',
+    urlTemplate: 'http://{subDomain}.mwr.gov.cn:81/cia_c/wmts?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=cia&STYLE=default&FORMAT=tiles&TILEMATRIXSET=c&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}',
+    subDomains: ['tdt'] },
+  vec: { id: 'tianditu_vec', title: '天地图矢量',
+    urlTemplate: 'http://{subDomain}.mwr.gov.cn:81/vec_c/wmts?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=vec&STYLE=default&FORMAT=tiles&TILEMATRIXSET=c&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}',
+    subDomains: ['tdt'] },
+  vecLabel: { id: 'tianditu_vec_label', title: '天地图矢量注记',
+    urlTemplate: 'http://{subDomain}.mwr.gov.cn:81/cva_c/wmts?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=cva&STYLE=default&FORMAT=tiles&TILEMATRIXSET=c&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}',
+    subDomains: ['tdt'] },
+}
+
+export const supermapLabel = {
+  id: 'supermap_label', title: '超图注记',
+  urlTemplate: 'http://{subDomain}.mwr.gov.cn/iserver/services/XZQH_HZZR/wmts100?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=XZQH_HZZR&STYLE=Default&FORMAT=image/png&TILEMATRIXSET=Custom_XZQH_HZZR&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}',
+  subDomains: ['map']
+}
+
+export const supermapRiver = {
+  id: 'supermap_river', title: '超图水系',
+  urlTemplate: 'http://{subDomain}.mwr.gov.cn/iserver/services/RIVER_R/wmts100?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=RIVER_R&STYLE=Default&FORMAT=image/png&TILEMATRIXSET=Custom_RIVER_R&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}',
+  subDomains: ['map']
+}
+
+export const basemapGroups = {
+  taihu: {
+    name: '太湖一张图',
+    layers: [
+      { ...taihuBaseMap, index: 0 },
+      { ...supermapRiver, index: 1 },
+      { ...supermapLabel, index: 2 }
+    ]
+  },
+  tianditu_img: {
+    name: '天地图影像',
+    layers: [
+      { ...tianditu.img, index: 0 },
+      { ...tianditu.imgLabel, index: 1 },
+      { ...supermapRiver, index: 2 }
+    ]
+  },
+  tianditu_vec: {
+    name: '天地图矢量',
+    layers: [
+      { ...tianditu.vec, index: 0 },
+      { ...tianditu.vecLabel, index: 1 },
+      { ...supermapRiver, index: 2 }
+    ]
+  }
+}

+ 152 - 0
gw-ui/src/views/hlgl/onemap/index.vue

@@ -0,0 +1,152 @@
+<template>
+  <div class="onemap-page">
+    <!-- 图层目录 -->
+    <LayerMenu @layerToggle="onLayerToggle" />
+
+    <!-- 地图 -->
+    <MapContent ref="mapRef" @mapReady="onMapReady" @mapClick="onMapClick" />
+
+    <!-- 点击要素弹窗 -->
+    <FeaturePopup ref="featurePopupRef" />
+    <!-- 右侧详情面板 (河档/河段) -->
+    <InfoPanel ref="infoPanelRef" />
+  </div>
+</template>
+
+<script setup>
+import { ref, shallowRef } from 'vue'
+import MapContent from './mapContent.vue'
+import LayerMenu from './components/LayerMenu.vue'
+import FeaturePopup from './components/FeaturePopup.vue'
+import InfoPanel from './components/InfoPanel.vue'
+import WebTileLayer from '@arcgis/core/layers/WebTileLayer'
+
+const mapRef = ref(null)
+const viewRef = shallowRef(null)
+const featurePopupRef = ref(null)
+const infoPanelRef = ref(null)
+const addedLayers = new Map()
+
+const onMapReady = ({ view }) => {
+  viewRef.value = view
+}
+
+// 从 ArcGIS MapServer REST API 查询要素全部属性
+const queryFeatureAttributes = async (layerUrl, objectId) => {
+  if (!layerUrl || !objectId) return null
+  try {
+    const url = `${layerUrl}/query?f=json&objectIds=${objectId}&returnGeometry=false&outFields=*`
+    const res = await fetch(url)
+    const data = await res.json()
+    if (data.features && data.features.length > 0) {
+      return data.features[0].attributes
+    }
+  } catch (e) {
+    console.warn('Query attributes failed:', e)
+  }
+  return null
+}
+
+const onMapClick = async (evt) => {
+  if (!evt.attributes || !Object.keys(evt.attributes).length) return
+
+  const lid = evt.layerId || ''
+  let attrs = evt.attributes
+  const isServiceLayer = lid.startsWith('th_') || lid.startsWith('anxian')
+
+  // 要素服务层: 如果属性只有 OBJECTID,则通过 REST API 查询完整属性
+  if (isServiceLayer && evt.layerUrl) {
+    const attrKeys = Object.keys(attrs)
+    if (attrKeys.length <= 2 || (attrKeys.length <= 3 && attrKeys.includes('SHAPE'))) {
+      const objectId = attrs.OBJECTID || attrs.FID || attrs.objectId
+      if (objectId) {
+        const fullAttrs = await queryFeatureAttributes(evt.layerUrl, objectId)
+        if (fullAttrs) attrs = fullAttrs
+      }
+    }
+  }
+
+  // 显示弹窗
+  if (lid.startsWith('th_') || lid.startsWith('anxian') || lid.startsWith('oneMap_')) {
+    const keyAttrs = {}
+    Object.entries(attrs).forEach(([k, v]) => {
+      if (typeof v === 'string' && v.length > 200) return
+      if (['Shape', 'SHAPE', 'the_geom', 'geometry'].includes(k)) return
+      keyAttrs[k] = v
+    })
+
+    featurePopupRef.value?.show({
+      title: evt.layerTitle || lid,
+      attributes: keyAttrs,
+      x: evt.x + 20,
+      y: evt.y + 10,
+      actions: [
+        {
+          label: '查看详情',
+          handler: () => showDetailPanel(lid, evt.layerTitle, attrs)
+        }
+      ]
+    })
+  }
+}
+
+const showDetailPanel = (layerId, layerTitle, attrs) => {
+  featurePopupRef.value?.close()
+  const tabs = []
+  const keys = Object.keys(attrs).filter(k => !['Shape', 'SHAPE', 'the_geom', 'geometry'].includes(k))
+  tabs.push({ name: '基本信息', type: 'kv', data: attrs, loading: false })
+  tabs.push({ name: '全部属性', type: 'html',
+    html: '<pre style="font-size:12px;max-height:400px;overflow:auto">' +
+      JSON.stringify(attrs, null, 2).replace(/</g, '&lt;') + '</pre>',
+    loading: false })
+  infoPanelRef.value?.show({ title: layerTitle || layerId, tabs })
+}
+
+// ---- 图层切换 ----
+const onLayerToggle = ({ layer, checked }) => {
+  const view = viewRef.value
+  if (!view) return
+
+  const noUrlIds = ['js', 'zj', 'sh', 'ah', 'fj']
+  if (noUrlIds.includes(layer.id) || layer.id.startsWith('anxian')) return
+
+  // 预加载的 ArcGIS 要素服务层 → toggle visibility
+  const existLayer = view.map.findLayerById(layer.id)
+  if (existLayer) {
+    existLayer.visible = checked
+    return
+  }
+
+  // WMTS 瓦片图层
+  if (checked && layer.url) {
+    let wmtsLayer = addedLayers.get(layer.id)
+    if (!wmtsLayer) {
+      const svcName = layer.tileMatrixSet
+        ? layer.tileMatrixSet.replace(/^Custom_/, '')
+        : layer.id
+      const urlTemplate = 'http://{subDomain}.1.58.34' + layer.url
+        + '&SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile'
+        + '&LAYER=' + svcName
+        + '&STYLE=Default&FORMAT=image/png'
+        + '&TILEMATRIXSET=' + (layer.tileMatrixSet || '')
+        + '&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}'
+      wmtsLayer = new WebTileLayer({
+        id: layer.id, title: layer.name,
+        urlTemplate, subDomains: ['10'], visible: true
+      })
+      addedLayers.set(layer.id, wmtsLayer)
+    }
+    view.map.add(wmtsLayer)
+  } else if (!checked) {
+    const wmtsLayer = addedLayers.get(layer.id)
+    if (wmtsLayer) view.map.remove(wmtsLayer)
+  }
+}
+</script>
+
+<style scoped>
+.onemap-page {
+  width: 100%; height: calc(100vh - 60px);
+  position: relative; overflow: hidden;
+}
+</style>

+ 251 - 0
gw-ui/src/views/hlgl/onemap/mapContent.vue

@@ -0,0 +1,251 @@
+<template>
+  <div class="map-container">
+    <div ref="mapNodeRef" class="map-node" />
+
+    <!-- 底图切换 -->
+    <div class="basemap-panel" v-show="showBasemapMenu">
+      <el-radio-group v-model="currentBasemap" @change="onBasemapChange" size="small">
+        <el-radio-button value="taihu">太湖一张图</el-radio-button>
+        <el-radio-button value="tianditu_img">天地图影像</el-radio-button>
+        <el-radio-button value="tianditu_vec">天地图矢量</el-radio-button>
+      </el-radio-group>
+    </div>
+
+    <!-- 图层按钮 -->
+    <div class="layer-btn" @click="showBasemapMenu = !showBasemapMenu" title="图层切换">
+      <el-icon :size="22"><Operation /></el-icon>
+    </div>
+
+    <!-- 全图 -->
+    <div class="full-extent-btn" @click="zoomToFull" title="全图">
+      <el-icon :size="22"><FullScreen /></el-icon>
+    </div>
+
+    <!-- 经纬度 -->
+    <div class="coord-bar" v-show="showCoords">
+      经度 {{ coordLng }} &nbsp; 纬度 {{ coordLat }}
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted, shallowRef } from 'vue'
+import MapView from '@arcgis/core/views/MapView'
+import Map from '@arcgis/core/Map'
+import WebTileLayer from '@arcgis/core/layers/WebTileLayer'
+import TileLayer from '@arcgis/core/layers/TileLayer'
+import MapImageLayer from '@arcgis/core/layers/MapImageLayer'
+import Extent from '@arcgis/core/geometry/Extent'
+import SpatialReference from '@arcgis/core/geometry/SpatialReference'
+import Graphic from '@arcgis/core/Graphic'
+import FeatureLayer from '@arcgis/core/layers/FeatureLayer'
+import { basemapGroups, taihuServiceLayers } from './config/layers'
+
+const emit = defineEmits(['mapReady', 'mapClick'])
+
+// ---- map ----
+const mapNodeRef = ref(null)
+const mapView = shallowRef(null)
+const currentBasemap = ref('taihu')
+const showBasemapMenu = ref(false)
+const showCoords = ref(false)
+const coordLng = ref('')
+const coordLat = ref('')
+const layerGroups = shallowRef({})
+
+// 太湖流域范围 [119.92, 31.17], WGS84 (wkid 4490)
+const taihuCenter = [119.92, 31.17]
+const taihuZoom = 9
+const fullExtent = new Extent({ xmin: 115, ymin: 29, xmax: 123, ymax: 33, spatialReference: SpatialReference.WGS84 })
+
+// ---- create WMTS layer from URL template ----
+function createWmtsLayer(cfg) {
+  // 替换 {subDomain} 占位符
+  const subDomains = cfg.subDomains || ['t0']
+  const urlTemplate = cfg.urlTemplate.replace('{subDomain}', subDomains[0])
+  // ArcGIS WebTileLayer uses {level},{col},{row} pattern
+  const arcgisUrl = urlTemplate
+    .replace(/\{level\}/g, '{level}')
+    .replace(/\{row\}/g, '{row}')
+    .replace(/\{col\}/g, '{col}')
+  return new WebTileLayer({
+    id: cfg.id,
+    title: cfg.title,
+    urlTemplate: arcgisUrl,
+    subDomains: subDomains,
+    visible: cfg.visible !== false,
+    opacity: cfg.opacity || 1
+  })
+}
+
+// ---- create layers for a basemap group ----
+function createBasemapLayers(key) {
+  const group = basemapGroups[key]
+  if (!group) return []
+  return group.layers.map((cfg) => {
+    // ArcGIS REST tile layer (SuperMap 兼容)
+    if (cfg.type === 'mapserver' || cfg.url?.includes('/MapServer')) {
+      return new MapImageLayer({
+        id: cfg.id,
+        title: cfg.title,
+        url: cfg.url,
+        visible: cfg.visible !== false
+      })
+    }
+    // WMTS template URL
+    return createWmtsLayer(cfg)
+  })
+}
+
+// ---- init ----
+onMounted(async () => {
+  const layers = createBasemapLayers('taihu')
+  layerGroups.value = { taihu: layers }
+
+  const map = new Map({ layers })
+
+  const view = new MapView({
+    container: mapNodeRef.value,
+    map,
+    center: taihuCenter,
+    zoom: taihuZoom
+  })
+
+  mapView.value = view
+  await view.when()
+
+  // 太湖一张图:预加载 ArcGIS 要素服务图层(默认不可见,checkbox 控制显隐)
+  taihuServiceLayers.forEach((cfg) => {
+    const LayerCtor = cfg.type === 'mapimage' ? MapImageLayer : FeatureLayer
+    const layer = new LayerCtor({
+      id: cfg.id,
+      title: cfg.name,
+      url: cfg.url,
+      visible: false,
+      spatialReference: { wkid: 4490 },
+      outFields: ['*']
+    })
+    map.add(layer)
+  })
+  taihuServiceLoaded.value = true
+
+  // pointer move → coords
+  view.on('pointer-move', (evt) => {
+    const pt = view.toMap({ x: evt.x, y: evt.y })
+    if (pt) {
+      coordLng.value = pt.longitude.toFixed(6)
+      coordLat.value = pt.latitude.toFixed(6)
+      showCoords.value = true
+    }
+  })
+
+  // click → identify features and emit
+  view.on('click', (evt) => {
+    view.hitTest(evt).then((res) => {
+      if (res.results.length > 0) {
+        const g = res.results[0].graphic
+        const layer = g?.layer
+        const attrs = g?.attributes || {}
+        emit('mapClick', {
+          longitude: evt.mapPoint.longitude,
+          latitude: evt.mapPoint.latitude,
+          x: evt.x, y: evt.y,
+          layerId: layer?.id,
+          layerTitle: layer?.title,
+          layerUrl: layer?.url,
+          attributes: attrs
+        })
+      } else {
+        emit('mapClick', {
+          longitude: evt.mapPoint.longitude,
+          latitude: evt.mapPoint.latitude
+        })
+      }
+    })
+  })
+
+  emit('mapReady', { view, map })
+})
+
+onUnmounted(() => {
+  mapView.value?.destroy()
+})
+
+// ---- methods ----
+const taihuServiceLoaded = ref(false)
+
+const onBasemapChange = async (key) => {
+  if (!mapView.value) return
+  // cache layers
+  if (!layerGroups.value[key]) {
+    layerGroups.value[key] = createBasemapLayers(key)
+  }
+  const mp = mapView.value.map
+  mp.layers.removeAll()
+  // add basemap layers
+  layerGroups.value[key].forEach((l) => mp.add(l))
+
+  // 太湖一张图:预加载 ArcGIS 要素服务图层(默认不可见)
+  if (key === 'taihu' && !taihuServiceLoaded.value) {
+    taihuServiceLayers.forEach((cfg) => {
+      const LayerCtor = cfg.type === 'mapimage' ? MapImageLayer : FeatureLayer
+      const layer = new LayerCtor({
+        id: cfg.id,
+        title: cfg.name,
+        url: cfg.url,
+        visible: false
+      })
+      mp.add(layer)
+    })
+    taihuServiceLoaded.value = true
+  }
+}
+
+const zoomToFull = () => {
+  mapView.value?.goTo(fullExtent)
+}
+
+defineExpose({
+  getView: () => mapView.value,
+  zoomToFull,
+  goTo(point, zoom) {
+    mapView.value?.goTo({ target: point, zoom: zoom || 12 })
+  },
+  addLayer(layer) {
+    mapView.value?.map.add(layer)
+  },
+  removeLayer(id) {
+    const l = mapView.value?.map.findLayerById(id)
+    if (l) mapView.value.map.remove(l)
+  }
+})
+</script>
+
+<style scoped>
+.map-container { width: 100%; height: 100%; position: relative; }
+.map-node { width: 100%; height: 100%; }
+
+.basemap-panel {
+  position: absolute; bottom: 80px; left: 16px; z-index: 10;
+  background: rgba(255,255,255,.92); padding: 6px 10px; border-radius: 6px;
+  box-shadow: 0 2px 8px rgba(0,0,0,.15);
+}
+.layer-btn {
+  position: absolute; bottom: 130px; left: 16px; z-index: 10;
+  background: white; width: 36px; height: 36px; border-radius: 6px;
+  display: flex; align-items: center; justify-content: center; cursor: pointer;
+  box-shadow: 0 2px 8px rgba(0,0,0,.15);
+}
+.layer-btn:hover { background: #e6f7ff; }
+.full-extent-btn {
+  position: absolute; top: 16px; right: 16px; z-index: 10;
+  background: white; width: 36px; height: 36px; border-radius: 4px;
+  display: flex; align-items: center; justify-content: center; cursor: pointer;
+  box-shadow: 0 2px 8px rgba(0,0,0,.15);
+}
+.full-extent-btn:hover { background: #e6f7ff; }
+.coord-bar {
+  position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%); z-index: 10;
+  background: rgba(0,0,0,.5); color: #ccc; padding: 2px 16px; border-radius: 3px; font-size: 13px;
+}
+</style>

+ 114 - 0
gw-ui/src/views/hlgl/riverproject/index.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" :inline="true" v-show="showSearch">
+      <el-form-item label="项目名称">
+        <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width:200px" @keyup.enter="getList" />
+      </el-form-item>
+      <el-form-item label="建设阶段">
+        <el-input v-model="queryParams.buildStage" placeholder="请输入建设阶段" clearable style="width:200px" @keyup.enter="getList" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="Search" @click="getList">搜索</el-button>
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
+      </el-col>
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
+    </el-row>
+
+    <el-table v-loading="loading" :data="projectList" border>
+      <el-table-column label="序号" type="index" width="55" align="center" />
+      <el-table-column label="项目名称" prop="S_PROJECT_NAME" :show-overflow-tooltip="true" min-width="180" />
+      <el-table-column label="建设单位" prop="S_DEPT" :show-overflow-tooltip="true" width="160" />
+      <el-table-column label="法人代表" prop="projectLegalPerson" width="120" />
+      <el-table-column label="联系人" prop="S_LINKMAN" width="100" />
+      <el-table-column label="联系电话" prop="S_PHONE" width="130" />
+      <el-table-column label="建设阶段" prop="S_P_BUILD_STAGE" width="110" />
+      <el-table-column label="申请日期" prop="S_PGOA_DATE" width="120" />
+      <el-table-column label="项目地点" prop="S_LOCATION" :show-overflow-tooltip="true" width="160" />
+      <el-table-column label="操作" align="center" width="150" fixed="right">
+        <template #default="scope">
+          <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
+          <el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)">修改</el-button>
+          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
+
+    <!-- 编辑对话框 -->
+    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="700px" append-to-body>
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="项目名称" prop="sProjectName"><el-input v-model="form.sProjectName" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="建设单位" prop="sDept"><el-input v-model="form.sDept" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="联系人" prop="sLinkman"><el-input v-model="form.sLinkman" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="联系电话" prop="sPhone"><el-input v-model="form.sPhone" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="建设阶段" prop="sPBuildStage"><el-input v-model="form.sPBuildStage" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="项目性质" prop="sType"><el-input v-model="form.sType" /></el-form-item></el-col>
+          <el-col :span="24"><el-form-item label="项目地点" prop="sLocation"><el-input v-model="form.sLocation" /></el-form-item></el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitForm">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { listProject, addProject, updateProject, delProject } from '@/api/hzz/riverproject'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+const showSearch = ref(true)
+const loading = ref(false)
+const total = ref(0)
+const pageNum = ref(1)
+const pageSize = ref(10)
+const projectList = ref([])
+const queryParams = ref({ projectName: '', buildStage: '' })
+
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const formRef = ref(null)
+const form = reactive({})
+const rules = { sProjectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }] }
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const res = await listProject({ ...queryParams.value, pageNum: pageNum.value, pageSize: pageSize.value })
+    projectList.value = res.rows || []
+    total.value = res.total || 0
+  } finally { loading.value = false }
+}
+
+const resetQuery = () => { queryParams.value = { projectName: '', buildStage: '' }; getList() }
+
+const handleAdd = () => { dialogTitle.value = '新增涉河项目'; Object.keys(form).forEach(k => delete form[k]); dialogVisible.value = true }
+const handleEdit = (row) => { dialogTitle.value = '修改涉河项目'; Object.assign(form, row); dialogVisible.value = true }
+const handleDetail = (row) => { dialogTitle.value = '项目详情'; Object.assign(form, row); dialogVisible.value = true }
+const handleDelete = async (row) => {
+  await ElMessageBox.confirm('确认删除该项目?', '提示', { type: 'warning' })
+  await delProject(row.GUID)
+  ElMessage.success('删除成功')
+  getList()
+}
+
+const submitForm = async () => {
+  if (!formRef.value) return
+  await formRef.value.validate()
+  if (form.GUID) { await updateProject(form) } else { await addProject(form) }
+  ElMessage.success('操作成功')
+  dialogVisible.value = false
+  getList()
+}
+
+onMounted(() => getList())
+</script>

+ 73 - 0
gw-ui/src/views/hlgl/shoreline/index.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <!-- 表格 -->
+      <el-col :span="24">
+        <el-card shadow="hover">
+          <template #header>
+            <span>岸线功能区统计</span>
+          </template>
+          <el-table v-loading="loading" :data="tableData" border stripe>
+            <el-table-column prop="TYPE" label="类别" width="120" />
+            <el-table-column prop="BELONG" label="所属" width="150" />
+            <el-table-column prop="LINELENG" label="岸线长度(km)" width="140" />
+            <el-table-column prop="LINEGN" label="功能区长度(km)" width="140" />
+            <el-table-column prop="COUNT" label="功能区数量" width="120" />
+          </el-table>
+        </el-card>
+      </el-col>
+
+      <!-- 图表 -->
+      <el-col :span="24" style="margin-top:20px">
+        <el-card shadow="hover">
+          <template #header>
+            <span>岸线长度分布图</span>
+          </template>
+          <div ref="chartRef" style="width:100%;height:400px" />
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, nextTick } from 'vue'
+import { getShorelineStats } from '@/api/hzz/shoreline'
+import * as echarts from 'echarts'
+
+const loading = ref(false)
+const tableData = ref([])
+const chartRef = ref(null)
+
+onMounted(async () => {
+  loading.value = true
+  try {
+    const res = await getShorelineStats()
+    tableData.value = res.data || []
+    await nextTick()
+    initChart()
+  } finally {
+    loading.value = false
+  }
+})
+
+const initChart = () => {
+  if (!chartRef.value || !tableData.value.length) return
+  const chart = echarts.init(chartRef.value)
+  const categories = tableData.value.map(r => r.BELONG || r.TYPE)
+  const lineLeng = tableData.value.map(r => Number(r.LINELENG) || 0)
+  const funcLeng = tableData.value.map(r => Number(r.LINEGN) || 0)
+
+  chart.setOption({
+    tooltip: { trigger: 'axis' },
+    legend: { data: ['岸线长度(km)', '功能区长度(km)'] },
+    xAxis: { type: 'category', data: categories, axisLabel: { rotate: 30 } },
+    yAxis: { type: 'value', name: 'km' },
+    series: [
+      { name: '岸线长度(km)', type: 'bar', data: lineLeng, itemStyle: { color: '#409EFF' } },
+      { name: '功能区长度(km)', type: 'bar', data: funcLeng, itemStyle: { color: '#67C23A' } }
+    ]
+  })
+  window.addEventListener('resize', () => chart.resize())
+}
+</script>

+ 104 - 0
gw-ui/src/views/hlgl/supervision/index.vue

@@ -0,0 +1,104 @@
+<template>
+  <div class="app-container">
+    <el-table v-loading="loading" :data="supervisionList" border>
+      <el-table-column label="序号" type="index" width="55" align="center" />
+      <el-table-column label="GUID" prop="GUID" width="100" :show-overflow-tooltip="true" />
+      <el-table-column label="状态" prop="STATUS" width="100" />
+      <el-table-column label="审核信息" prop="AUDIT_INFO" :show-overflow-tooltip="true" min-width="180" />
+      <el-table-column label="操作" align="center" width="150" fixed="right">
+        <template #default="scope">
+          <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
+          <el-button link type="primary" icon="Check" @click="handleAudit(scope.row)">审核</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
+
+    <!-- 详情对话框 -->
+    <el-dialog title="督导详情" v-model="detailVisible" width="800px" append-to-body>
+      <el-tabs>
+        <el-tab-pane label="附件">
+          <el-table :data="attachments" border size="small">
+            <el-table-column label="GUID" prop="GUID" :show-overflow-tooltip="true" />
+            <el-table-column label="文件名" prop="FILE_NAME" :show-overflow-tooltip="true" />
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="分工">
+          <el-table :data="divisions" border size="small">
+            <el-table-column label="GUID" prop="GUID" :show-overflow-tooltip="true" />
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="结果">
+          <el-table :data="results" border size="small">
+            <el-table-column label="GUID" prop="GUID" :show-overflow-tooltip="true" />
+          </el-table>
+        </el-tab-pane>
+      </el-tabs>
+    </el-dialog>
+
+    <!-- 审核对话框 -->
+    <el-dialog title="审核督导" v-model="auditVisible" width="500px" append-to-body>
+      <el-form :model="auditForm" label-width="80px">
+        <el-form-item label="审核结果"><el-select v-model="auditForm.audit" style="width:100%"><el-option label="通过" value="1" /><el-option label="不通过" value="0" /></el-select></el-form-item>
+        <el-form-item label="审核意见"><el-input v-model="auditForm.auditInfo" type="textarea" :rows="3" /></el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="auditVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitAudit">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { listSupervision, getSupervisionDetail, auditSupervision } from '@/api/hzz/supervision'
+import { ElMessage } from 'element-plus'
+
+const loading = ref(false)
+const total = ref(0)
+const pageNum = ref(1)
+const pageSize = ref(10)
+const supervisionList = ref([])
+
+const detailVisible = ref(false)
+const attachments = ref([])
+const divisions = ref([])
+const results = ref([])
+
+const auditVisible = ref(false)
+const auditForm = reactive({ guid: '', audit: '1', auditInfo: '' })
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const res = await listSupervision({ pageNum: pageNum.value, pageSize: pageSize.value })
+    supervisionList.value = res.rows || []
+    total.value = res.total || 0
+  } finally { loading.value = false }
+}
+
+const handleDetail = async (row) => {
+  const res = await getSupervisionDetail(row.GUID)
+  attachments.value = res.data?.attachments || []
+  divisions.value = res.data?.divisions || []
+  results.value = res.data?.results || []
+  detailVisible.value = true
+}
+
+const handleAudit = (row) => {
+  auditForm.guid = row.GUID
+  auditForm.audit = '1'
+  auditForm.auditInfo = ''
+  auditVisible.value = true
+}
+
+const submitAudit = async () => {
+  await auditSupervision(auditForm.guid, auditForm.audit, auditForm.auditInfo)
+  ElMessage.success('审核完成')
+  auditVisible.value = false
+  getList()
+}
+
+onMounted(() => getList())
+</script>

+ 49 - 0
gw-ui/src/views/hlgl/syaxgl/index.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <el-col :span="24">
+        <el-card shadow="hover" style="margin-bottom:20px">
+          <template #header><span>岸线功能区统计</span></template>
+          <el-table v-loading="sLoading" :data="shoreData" border stripe size="small">
+            <el-table-column prop="TYPE" label="类别" width="120" />
+            <el-table-column prop="BELONG" label="所属" width="150" />
+            <el-table-column prop="LINELENG" label="岸线长度(km)" width="140" />
+            <el-table-column prop="LINEGN" label="功能区长度(km)" width="140" />
+            <el-table-column prop="COUNT" label="功能区数量" width="120" />
+          </el-table>
+        </el-card>
+      </el-col>
+      <el-col :span="24">
+        <el-card shadow="hover">
+          <template #header><span>河湖长制工作制度</span></template>
+          <el-table v-loading="wLoading" :data="wpList" border size="small">
+            <el-table-column label="方案编号" prop="wpCode" width="160" />
+            <el-table-column label="方案名称" prop="wpName" show-overflow-tooltip />
+            <el-table-column label="文号" prop="fileNum" width="140" />
+            <el-table-column label="发布日期" prop="rlsTm" width="120" />
+            <el-table-column label="发布单位" prop="releWiunName" width="180" />
+          </el-table>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { getShorelineStats } from '@/api/hzz/shoreline'
+import { listWorkPlan } from '@/api/hzz/workplan'
+
+const sLoading = ref(false), shoreData = ref([])
+const wLoading = ref(false), wpList = ref([])
+
+onMounted(async () => {
+  sLoading.value = true; wLoading.value = true
+  try {
+    const [sr, wr] = await Promise.all([getShorelineStats(), listWorkPlan('')])
+    shoreData.value = sr.data || []
+    wpList.value = wr.data || []
+  } catch { /* ok */ }
+  finally { sLoading.value = false; wLoading.value = false }
+})
+</script>

+ 101 - 0
gw-ui/src/views/hlgl/wps/index.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" :inline="true" v-show="showSearch">
+      <el-form-item label="项目名称">
+        <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width:200px" @keyup.enter="getList" />
+      </el-form-item>
+      <el-form-item label="开始时间">
+        <el-date-picker v-model="queryParams.startDate" type="date" placeholder="选择开始时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:200px" />
+      </el-form-item>
+      <el-form-item label="结束时间">
+        <el-date-picker v-model="queryParams.endDate" type="date" placeholder="选择结束时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:200px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="Search" @click="getList">搜索</el-button>
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd">新增督查</el-button></el-col>
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
+    </el-row>
+
+    <el-table v-loading="loading" :data="wpsList" border>
+      <el-table-column label="序号" type="index" width="55" align="center" />
+      <el-table-column label="项目名称" prop="S_PROJECT_NAME" :show-overflow-tooltip="true" min-width="180" />
+      <el-table-column label="督察情况" prop="SUPERVISION" :show-overflow-tooltip="true" min-width="200" />
+      <el-table-column label="开始时间" prop="START_DATE" width="160" />
+      <el-table-column label="结束时间" prop="END_DATE" width="160" />
+      <el-table-column label="操作" align="center" width="150" fixed="right">
+        <template #default="scope">
+          <el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)">修改</el-button>
+          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
+
+    <!-- 编辑对话框 -->
+    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body>
+      <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
+        <el-form-item label="关联项目" prop="smId"><el-input v-model="form.smId" /></el-form-item>
+        <el-form-item label="督察情况" prop="supervision"><el-input v-model="form.supervision" type="textarea" :rows="4" /></el-form-item>
+        <el-form-item label="开始时间" prop="startDate"><el-date-picker v-model="form.startDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" /></el-form-item>
+        <el-form-item label="结束时间" prop="endDate"><el-date-picker v-model="form.endDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" /></el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitForm">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { listWps, addWps, updateWps, delWps } from '@/api/hzz/wps'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+const showSearch = ref(true)
+const loading = ref(false)
+const total = ref(0)
+const pageNum = ref(1)
+const pageSize = ref(10)
+const wpsList = ref([])
+const queryParams = ref({ projectName: '', startDate: '', endDate: '' })
+
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const formRef = ref(null)
+const form = reactive({})
+const formRules = { supervision: [{ required: true, message: '督察情况不能为空', trigger: 'blur' }] }
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const res = await listWps({ ...queryParams.value, pageNum: pageNum.value, pageSize: pageSize.value })
+    wpsList.value = res.rows || []
+    total.value = res.total || 0
+  } finally { loading.value = false }
+}
+const resetQuery = () => { queryParams.value = { projectName: '', startDate: '', endDate: '' }; getList() }
+const handleAdd = () => { dialogTitle.value = '新增督查'; Object.keys(form).forEach(k => delete form[k]); dialogVisible.value = true }
+const handleEdit = (row) => { dialogTitle.value = '修改督查'; Object.assign(form, row); dialogVisible.value = true }
+const handleDelete = async (row) => {
+  await ElMessageBox.confirm('确认删除该督查记录?', '提示', { type: 'warning' })
+  await delWps(row.GUID)
+  ElMessage.success('删除成功')
+  getList()
+}
+const submitForm = async () => {
+  if (!formRef.value) return
+  await formRef.value.validate()
+  if (form.GUID) { await updateWps(form) } else { await addWps(form) }
+  ElMessage.success('操作成功')
+  dialogVisible.value = false
+  getList()
+}
+
+onMounted(() => getList())
+</script>

+ 6 - 0
package-lock.json

@@ -0,0 +1,6 @@
+{
+  "name": "tba-hzz-new",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {}
+}

BIN
sql/InsertUsers.class


+ 61 - 0
sql/InsertUsers.java

@@ -0,0 +1,61 @@
+import java.sql.*;
+
+public class InsertUsers {
+    public static void main(String[] args) throws Exception {
+        Class.forName("dm.jdbc.driver.DmDriver");
+        Connection conn = DriverManager.getConnection(
+            "jdbc:dm://39.98.38.2:30236", "TBA_HZZ", "TBA_HZZ_123");
+        Statement stmt = conn.createStatement();
+
+        stmt.execute("DELETE FROM SYS_USER");
+        stmt.execute("DELETE FROM SYS_USER_ROLE");
+        stmt.execute("DELETE FROM SYS_USER_POST");
+        stmt.execute("DELETE FROM SYS_ROLE_DEPT");
+
+        String hash = "$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2";
+
+        PreparedStatement ps = conn.prepareStatement(
+            "INSERT INTO SYS_USER VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
+        ps.setInt(1, 1); ps.setInt(2, 103); ps.setString(3, "admin");
+        ps.setString(4, "ruoyi"); ps.setString(5, "00"); ps.setString(6, "ry@163.com");
+        ps.setString(7, "15888888888"); ps.setString(8, "1"); ps.setString(9, "");
+        ps.setString(10, hash); ps.setString(11, "0"); ps.setString(12, "0");
+        ps.setString(13, "127.0.0.1");
+        ps.setTimestamp(14, new Timestamp(System.currentTimeMillis()));
+        ps.setTimestamp(15, new Timestamp(System.currentTimeMillis()));
+        ps.setString(16, "admin");
+        ps.setTimestamp(17, new Timestamp(System.currentTimeMillis()));
+        ps.setString(18, ""); ps.setString(19, "admin_user");
+        ps.executeUpdate();
+
+        ps.setInt(1, 2); ps.setInt(2, 105); ps.setString(3, "ry");
+        ps.setString(4, "ruoyi"); ps.setString(5, "00"); ps.setString(6, "ry@qq.com");
+        ps.setString(7, "15666666666"); ps.setString(8, "1"); ps.setString(9, "");
+        ps.setString(10, hash); ps.setString(11, "0"); ps.setString(12, "0");
+        ps.setString(13, "127.0.0.1");
+        ps.setTimestamp(14, new Timestamp(System.currentTimeMillis()));
+        ps.setTimestamp(15, new Timestamp(System.currentTimeMillis()));
+        ps.setString(16, "admin");
+        ps.setTimestamp(17, new Timestamp(System.currentTimeMillis()));
+        ps.setString(18, ""); ps.setString(19, "tester");
+        ps.executeUpdate();
+
+        stmt.execute("INSERT INTO SYS_USER_ROLE VALUES('1','1')");
+        stmt.execute("INSERT INTO SYS_USER_ROLE VALUES('2','2')");
+        stmt.execute("INSERT INTO SYS_USER_POST VALUES('1','1')");
+        stmt.execute("INSERT INTO SYS_USER_POST VALUES('2','2')");
+        stmt.execute("INSERT INTO SYS_ROLE_DEPT VALUES('2','100')");
+        stmt.execute("INSERT INTO SYS_ROLE_DEPT VALUES('2','101')");
+        stmt.execute("INSERT INTO SYS_ROLE_DEPT VALUES('2','105')");
+
+        conn.commit();
+
+        ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM SYS_USER");
+        rs.next(); System.out.println("Users: " + rs.getInt(1));
+        rs = stmt.executeQuery("SELECT user_name, password FROM SYS_USER");
+        while(rs.next()) System.out.println(rs.getString(1) + ": " + rs.getString(2).substring(0,10)+"...");
+
+        conn.close();
+        System.out.println("Done!");
+    }
+}

+ 27 - 0
sql/fix_critical.sql

@@ -0,0 +1,27 @@
+SET DEFINE OFF;
+-- Insert critical seed data
+DELETE FROM SYS_USER;
+INSERT INTO SYS_USER VALUES(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', SYSDATE, SYSDATE, 'admin', SYSDATE, '', NULL, '管理员');
+INSERT INTO SYS_USER VALUES(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', SYSDATE, SYSDATE, 'admin', SYSDATE, '', NULL, '测试员');
+
+DELETE FROM SYS_ROLE;
+INSERT INTO SYS_ROLE VALUES('1', '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', SYSDATE, '', NULL, '超级管理员');
+INSERT INTO SYS_ROLE VALUES('2', '普通角色', 'common', 2, '2', 1, 1, '0', '0', 'admin', SYSDATE, '', NULL, '普通角色');
+
+DELETE FROM SYS_USER_ROLE;
+INSERT INTO SYS_USER_ROLE VALUES('1', '1');
+INSERT INTO SYS_USER_ROLE VALUES('2', '2');
+
+DELETE FROM SYS_USER_POST;
+INSERT INTO SYS_USER_POST VALUES('1', '1');
+INSERT INTO SYS_USER_POST VALUES('2', '2');
+
+DELETE FROM SYS_ROLE_DEPT;
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '100');
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '101');
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '105');
+
+COMMIT;
+
+SELECT 'user count: ' || COUNT(*) FROM SYS_USER;
+SELECT 'role count: ' || COUNT(*) FROM SYS_ROLE;

+ 12 - 0
sql/fix_quotes.py

@@ -0,0 +1,12 @@
+import os
+os.chdir(os.path.dirname(os.path.abspath(__file__)))
+with open('gw_dm.sql', 'r', encoding='utf-8') as f:
+    content = f.read()
+# Fix backslash-escaped single quotes: \' -> ''
+content = content.replace("\\'ry\\'", "''ry''")
+content = content.replace("\\'", "''")
+# Remove any trailing ''; artifacts from earlier sed
+content = content.replace("'');''", "'');")
+with open('gw_dm.sql', 'w', encoding='utf-8') as f:
+    f.write(content)
+print('Done')

Разница между файлами не показана из-за своего большого размера
+ 645 - 0
sql/gw.sql


Разница между файлами не показана из-за своего большого размера
+ 645 - 0
sql/gw_dameng_temp.sql


Разница между файлами не показана из-за своего большого размера
+ 645 - 0
sql/gw_dm.sql


Разница между файлами не показана из-за своего большого размера
+ 645 - 0
sql/gw_mysql_backup.sql


+ 27 - 0
sql/insert_seed.sql

@@ -0,0 +1,27 @@
+SET DEFINE OFF;
+SET ESCAPE ON;
+
+DELETE FROM SYS_USER;
+DELETE FROM SYS_ROLE;
+DELETE FROM SYS_USER_ROLE;
+DELETE FROM SYS_USER_POST;
+DELETE FROM SYS_ROLE_DEPT;
+
+INSERT INTO SYS_USER VALUES(1, 103, 'admin', 'ruoyi', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', SYSDATE, SYSDATE, 'admin', SYSDATE, '', NULL, 'admin');
+
+INSERT INTO SYS_USER VALUES(2, 105, 'ry', 'ruoyi', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', SYSDATE, SYSDATE, 'admin', SYSDATE, '', NULL, 'tester');
+
+INSERT INTO SYS_ROLE VALUES('1', 'admin', 'admin', 1, '1', 1, 1, '0', '0', 'admin', SYSDATE, '', NULL, 'admin');
+INSERT INTO SYS_ROLE VALUES('2', 'common', 'common', 2, '2', 1, 1, '0', '0', 'admin', SYSDATE, '', NULL, 'common');
+
+INSERT INTO SYS_USER_ROLE VALUES('1', '1');
+INSERT INTO SYS_USER_ROLE VALUES('2', '2');
+
+INSERT INTO SYS_USER_POST VALUES('1', '1');
+INSERT INTO SYS_USER_POST VALUES('2', '2');
+
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '100');
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '101');
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '105');
+
+COMMIT;

+ 71 - 0
sql/menu_hzz.sql

@@ -0,0 +1,71 @@
+-- 河湖管理菜单配置 (达梦数据库)
+-- 在 DBeaver/Navicat 中执行
+
+DECLARE
+  v_pid INT;
+  v_id INT;
+BEGIN
+  -- 获取最大menu_id + 1
+  SELECT NVL(MAX(menu_id), 2000) + 1 INTO v_id FROM SYS_MENU;
+
+  -- 顶级菜单:河湖管理
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '河湖管理', 0, 1, 'hzz', NULL, 1, 0, 'M', '0', '0', NULL, 'tree', 'admin', SYSDATE, 'admin', SYSDATE, '河湖管理系统');
+
+  v_pid := v_id;
+  v_id := v_id + 1;
+
+  -- 1. 一张图(首页 - iframe嵌入老系统,后续用Vue3+ArcGIS重写)
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '一张图', v_pid, 1, 'onemap', 'hzz/onemap/index', 1, 0, 'C', '0', '0', 'hzz:onemap:list', 'map', 'admin', SYSDATE, 'admin', SYSDATE, '一张图首页');
+  v_id := v_id + 1;
+
+  -- 2. 水域岸线管理
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '水域岸线管理', v_pid, 2, 'bankline', 'hzz/bankline/index', 1, 0, 'C', '0', '0', 'hzz:bankline:list', 'form', 'admin', SYSDATE, 'admin', SYSDATE, '河湖长制工作制度');
+  v_id := v_id + 1;
+
+  -- 3. 涉河项目查询
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '涉河项目查询', v_pid, 3, 'riverproject', 'hzz/riverproject/index', 1, 0, 'C', '0', '0', 'hzz:riverproject:list', 'build', 'admin', SYSDATE, 'admin', SYSDATE, '涉河项目查询与地图可视化');
+  v_id := v_id + 1;
+
+  -- 4. 岸线功能区统计
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '岸线功能区统计', v_pid, 4, 'shoreline', 'hzz/shoreline/index', 1, 0, 'C', '0', '0', 'hzz:shoreline:list', 'chart', 'admin', SYSDATE, 'admin', SYSDATE, '岸线功能区长度统计');
+  v_id := v_id + 1;
+
+  -- 5. 考核评估
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '考核评估', v_pid, 5, 'assess', 'hzz/assess/index', 1, 0, 'C', '0', '0', 'hzz:assess:list', 'edit', 'admin', SYSDATE, 'admin', SYSDATE, '考核评估');
+  v_id := v_id + 1;
+
+  -- 6. 督导检查
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '督导检查', v_pid, 6, 'supervision', 'hzz/supervision/index', 1, 0, 'C', '0', '0', 'hzz:supervision:list', 'monitor', 'admin', SYSDATE, 'admin', SYSDATE, '督导检查');
+  v_id := v_id + 1;
+
+  -- 7. 涉河项目督查
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '涉河项目督查', v_pid, 7, 'wps', 'hzz/wps/index', 1, 0, 'C', '0', '0', 'hzz:wps:list', 'list', 'admin', SYSDATE, 'admin', SYSDATE, '涉河项目督查');
+  v_id := v_id + 1;
+
+  -- 8. 一湖两河清四乱
+  INSERT INTO SYS_MENU (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+  VALUES (v_id, '一湖两河清四乱', v_pid, 8, 'eventclear', 'hzz/eventclear/index', 1, 0, 'C', '0', '0', 'hzz:eventclear:list', 'warning', 'admin', SYSDATE, 'admin', SYSDATE, '一湖两河清四乱');
+  v_id := v_id + 1;
+
+  -- 给超级管理员(role_id=1)分配所有河湖管理菜单
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+1);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+2);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+3);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+4);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+5);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+6);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+7);
+  INSERT INTO SYS_ROLE_MENU (role_id, menu_id) VALUES ('1', v_pid+8);
+
+  COMMIT;
+END;
+/

+ 174 - 0
sql/quartz.sql

@@ -0,0 +1,174 @@
+DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
+DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
+DROP TABLE IF EXISTS QRTZ_LOCKS;
+DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
+DROP TABLE IF EXISTS QRTZ_CALENDARS;
+
+-- ----------------------------
+-- 1、存储每一个已配置的 jobDetail 的详细信息
+-- ----------------------------
+create table QRTZ_JOB_DETAILS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    job_name             varchar(200)    not null            comment '任务名称',
+    job_group            varchar(200)    not null            comment '任务组名',
+    description          varchar(250)    null                comment '相关介绍',
+    job_class_name       varchar(250)    not null            comment '执行任务类名称',
+    is_durable           varchar(1)      not null            comment '是否持久化',
+    is_nonconcurrent     varchar(1)      not null            comment '是否并发',
+    is_update_data       varchar(1)      not null            comment '是否更新数据',
+    requests_recovery    varchar(1)      not null            comment '是否接受恢复执行',
+    job_data             blob            null                comment '存放持久化job对象',
+    primary key (sched_name, job_name, job_group)
+) engine=innodb comment = '任务详细信息表';
+
+-- ----------------------------
+-- 2、 存储已配置的 Trigger 的信息
+-- ----------------------------
+create table QRTZ_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment '触发器的名字',
+    trigger_group        varchar(200)    not null            comment '触发器所属组的名字',
+    job_name             varchar(200)    not null            comment 'qrtz_job_details表job_name的外键',
+    job_group            varchar(200)    not null            comment 'qrtz_job_details表job_group的外键',
+    description          varchar(250)    null                comment '相关介绍',
+    next_fire_time       bigint(13)      null                comment '上一次触发时间(毫秒)',
+    prev_fire_time       bigint(13)      null                comment '下一次触发时间(默认为-1表示不触发)',
+    priority             integer         null                comment '优先级',
+    trigger_state        varchar(16)     not null            comment '触发器状态',
+    trigger_type         varchar(8)      not null            comment '触发器的类型',
+    start_time           bigint(13)      not null            comment '开始时间',
+    end_time             bigint(13)      null                comment '结束时间',
+    calendar_name        varchar(200)    null                comment '日程表名称',
+    misfire_instr        smallint(2)     null                comment '补偿执行的策略',
+    job_data             blob            null                comment '存放持久化job对象',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, job_name, job_group) references QRTZ_JOB_DETAILS(sched_name, job_name, job_group)
+) engine=innodb comment = '触发器详细信息表';
+
+-- ----------------------------
+-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数
+-- ----------------------------
+create table QRTZ_SIMPLE_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    repeat_count         bigint(7)       not null            comment '重复的次数统计',
+    repeat_interval      bigint(12)      not null            comment '重复的间隔时间',
+    times_triggered      bigint(10)      not null            comment '已经触发的次数',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = '简单触发器的信息表';
+
+-- ----------------------------
+-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息
+-- ---------------------------- 
+create table QRTZ_CRON_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    cron_expression      varchar(200)    not null            comment 'cron表达式',
+    time_zone_id         varchar(80)                         comment '时区',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = 'Cron类型的触发器表';
+
+-- ----------------------------
+-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
+-- ---------------------------- 
+create table QRTZ_BLOB_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    blob_data            blob            null                comment '存放持久化Trigger对象',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = 'Blob类型的触发器表';
+
+-- ----------------------------
+-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围
+-- ---------------------------- 
+create table QRTZ_CALENDARS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    calendar_name        varchar(200)    not null            comment '日历名称',
+    calendar             blob            not null            comment '存放持久化calendar对象',
+    primary key (sched_name, calendar_name)
+) engine=innodb comment = '日历信息表';
+
+-- ----------------------------
+-- 7、 存储已暂停的 Trigger 组的信息
+-- ---------------------------- 
+create table QRTZ_PAUSED_TRIGGER_GRPS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    primary key (sched_name, trigger_group)
+) engine=innodb comment = '暂停的触发器表';
+
+-- ----------------------------
+-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
+-- ---------------------------- 
+create table QRTZ_FIRED_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    entry_id             varchar(95)     not null            comment '调度器实例id',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    instance_name        varchar(200)    not null            comment '调度器实例名',
+    fired_time           bigint(13)      not null            comment '触发的时间',
+    sched_time           bigint(13)      not null            comment '定时器制定的时间',
+    priority             integer         not null            comment '优先级',
+    state                varchar(16)     not null            comment '状态',
+    job_name             varchar(200)    null                comment '任务名称',
+    job_group            varchar(200)    null                comment '任务组名',
+    is_nonconcurrent     varchar(1)      null                comment '是否并发',
+    requests_recovery    varchar(1)      null                comment '是否接受恢复执行',
+    primary key (sched_name, entry_id)
+) engine=innodb comment = '已触发的触发器表';
+
+-- ----------------------------
+-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例
+-- ---------------------------- 
+create table QRTZ_SCHEDULER_STATE (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    instance_name        varchar(200)    not null            comment '实例名称',
+    last_checkin_time    bigint(13)      not null            comment '上次检查时间',
+    checkin_interval     bigint(13)      not null            comment '检查间隔时间',
+    primary key (sched_name, instance_name)
+) engine=innodb comment = '调度器状态表';
+
+-- ----------------------------
+-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁)
+-- ---------------------------- 
+create table QRTZ_LOCKS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    lock_name            varchar(40)     not null            comment '悲观锁名称',
+    primary key (sched_name, lock_name)
+) engine=innodb comment = '存储的悲观锁信息表';
+
+-- ----------------------------
+-- 11、 Quartz集群实现同步机制的行锁表
+-- ---------------------------- 
+create table QRTZ_SIMPROP_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    str_prop_1           varchar(512)    null                comment 'String类型的trigger的第一个参数',
+    str_prop_2           varchar(512)    null                comment 'String类型的trigger的第二个参数',
+    str_prop_3           varchar(512)    null                comment 'String类型的trigger的第三个参数',
+    int_prop_1           int             null                comment 'int类型的trigger的第一个参数',
+    int_prop_2           int             null                comment 'int类型的trigger的第二个参数',
+    long_prop_1          bigint          null                comment 'long类型的trigger的第一个参数',
+    long_prop_2          bigint          null                comment 'long类型的trigger的第二个参数',
+    dec_prop_1           numeric(13,4)   null                comment 'decimal类型的trigger的第一个参数',
+    dec_prop_2           numeric(13,4)   null                comment 'decimal类型的trigger的第二个参数',
+    bool_prop_1          varchar(1)      null                comment 'Boolean类型的trigger的第一个参数',
+    bool_prop_2          varchar(1)      null                comment 'Boolean类型的trigger的第二个参数',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = '同步机制的行锁表';
+
+commit;

+ 219 - 0
sql/quartz_dm.sql

@@ -0,0 +1,219 @@
+SET DEFINE OFF;
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_FIRED_TRIGGERS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_PAUSED_TRIGGER_GRPS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_SCHEDULER_STATE';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_LOCKS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_SIMPLE_TRIGGERS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_SIMPROP_TRIGGERS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_CRON_TRIGGERS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_BLOB_TRIGGERS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_TRIGGERS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_JOB_DETAILS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE QRTZ_CALENDARS';
+EXCEPTION WHEN OTHERS THEN NULL;
+END;
+/
+
+-- ----------------------------
+-- 1、存储每一个已配置的 jobDetail 的详细信息
+-- ----------------------------
+create table QRTZ_JOB_DETAILS (
+    sched_name           VARCHAR2(120)    not null           ,
+    job_name             VARCHAR2(200)    not null           ,
+    job_group            VARCHAR2(200)    not null           ,
+    description          VARCHAR2(250)    null               ,
+    job_class_name       VARCHAR2(250)    not null           ,
+    is_durable           VARCHAR2(1)      not null           ,
+    is_nonconcurrent     VARCHAR2(1)      not null           ,
+    is_update_data       VARCHAR2(1)      not null           ,
+    requests_recovery    VARCHAR2(1)      not null           ,
+    job_data             BLOB            null               ,
+    primary key (sched_name, job_name, job_group)
+);
+
+-- ----------------------------
+-- 2、 存储已配置的 Trigger 的信息
+-- ----------------------------
+create table QRTZ_TRIGGERS (
+    sched_name           VARCHAR2(120)    not null           ,
+    trigger_name         VARCHAR2(200)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    job_name             VARCHAR2(200)    not null           ,
+    job_group            VARCHAR2(200)    not null           ,
+    description          VARCHAR2(250)    null               ,
+    next_fire_time       BIGINT      null               ,
+    prev_fire_time       BIGINT      null               ,
+    priority             INT         null               ,
+    trigger_state        VARCHAR2(16)     not null           ,
+    trigger_type         VARCHAR2(8)      not null           ,
+    start_time           BIGINT      not null           ,
+    end_time             BIGINT      null               ,
+    calendar_name        VARCHAR2(200)    null               ,
+    misfire_instr        SMALLINT     null               ,
+    job_data             BLOB            null               ,
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, job_name, job_group) references QRTZ_JOB_DETAILS(sched_name, job_name, job_group)
+);
+
+-- ----------------------------
+-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数
+-- ----------------------------
+create table QRTZ_SIMPLE_TRIGGERS (
+    sched_name           VARCHAR2(120)    not null           ,
+    trigger_name         VARCHAR2(200)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    repeat_count         BIGINT       not null           ,
+    repeat_interval      BIGINT      not null           ,
+    times_triggered      BIGINT      not null           ,
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+);
+
+-- ----------------------------
+-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息
+-- ---------------------------- 
+create table QRTZ_CRON_TRIGGERS (
+    sched_name           VARCHAR2(120)    not null           ,
+    trigger_name         VARCHAR2(200)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    cron_expression      VARCHAR2(200)    not null           ,
+    time_zone_id         VARCHAR2(80)                        ,
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+);
+
+-- ----------------------------
+-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
+-- ---------------------------- 
+create table QRTZ_BLOB_TRIGGERS (
+    sched_name           VARCHAR2(120)    not null           ,
+    trigger_name         VARCHAR2(200)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    blob_data            BLOB            null               ,
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+);
+
+-- ----------------------------
+-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围
+-- ---------------------------- 
+create table QRTZ_CALENDARS (
+    sched_name           VARCHAR2(120)    not null           ,
+    calendar_name        VARCHAR2(200)    not null           ,
+    calendar             BLOB            not null           ,
+    primary key (sched_name, calendar_name)
+);
+
+-- ----------------------------
+-- 7、 存储已暂停的 Trigger 组的信息
+-- ---------------------------- 
+create table QRTZ_PAUSED_TRIGGER_GRPS (
+    sched_name           VARCHAR2(120)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    primary key (sched_name, trigger_group)
+);
+
+-- ----------------------------
+-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
+-- ---------------------------- 
+create table QRTZ_FIRED_TRIGGERS (
+    sched_name           VARCHAR2(120)    not null           ,
+    entry_id             VARCHAR2(95)     not null           ,
+    trigger_name         VARCHAR2(200)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    instance_name        VARCHAR2(200)    not null           ,
+    fired_time           BIGINT      not null           ,
+    sched_time           BIGINT      not null           ,
+    priority             INT         not null           ,
+    state                VARCHAR2(16)     not null           ,
+    job_name             VARCHAR2(200)    null               ,
+    job_group            VARCHAR2(200)    null               ,
+    is_nonconcurrent     VARCHAR2(1)      null               ,
+    requests_recovery    VARCHAR2(1)      null               ,
+    primary key (sched_name, entry_id)
+);
+
+-- ----------------------------
+-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例
+-- ---------------------------- 
+create table QRTZ_SCHEDULER_STATE (
+    sched_name           VARCHAR2(120)    not null           ,
+    instance_name        VARCHAR2(200)    not null           ,
+    last_checkin_time    BIGINT      not null           ,
+    checkin_interval     BIGINT      not null           ,
+    primary key (sched_name, instance_name)
+);
+
+-- ----------------------------
+-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁)
+-- ---------------------------- 
+create table QRTZ_LOCKS (
+    sched_name           VARCHAR2(120)    not null           ,
+    lock_name            VARCHAR2(40)     not null           ,
+    primary key (sched_name, lock_name)
+);
+
+-- ----------------------------
+-- 11、 Quartz集群实现同步机制的行锁表
+-- ---------------------------- 
+create table QRTZ_SIMPROP_TRIGGERS (
+    sched_name           VARCHAR2(120)    not null           ,
+    trigger_name         VARCHAR2(200)    not null           ,
+    trigger_group        VARCHAR2(200)    not null           ,
+    str_prop_1           VARCHAR2(512)    null               ,
+    str_prop_2           VARCHAR2(512)    null               ,
+    str_prop_3           VARCHAR2(512)    null               ,
+    int_prop_1           INT             null               ,
+    int_prop_2           INT             null               ,
+    long_prop_1          BIGINT          null               ,
+    long_prop_2          BIGINT          null               ,
+    dec_prop_1           NUMBER(13,4)   null               ,
+    dec_prop_2           NUMBER(13,4)   null               ,
+    bool_prop_1          VARCHAR2(1)      null               ,
+    bool_prop_2          VARCHAR2(1)      null               ,
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+);
+
+commit;

+ 174 - 0
sql/quartz_mysql_backup.sql

@@ -0,0 +1,174 @@
+DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
+DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
+DROP TABLE IF EXISTS QRTZ_LOCKS;
+DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
+DROP TABLE IF EXISTS QRTZ_CALENDARS;
+
+-- ----------------------------
+-- 1、存储每一个已配置的 jobDetail 的详细信息
+-- ----------------------------
+create table QRTZ_JOB_DETAILS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    job_name             varchar(200)    not null            comment '任务名称',
+    job_group            varchar(200)    not null            comment '任务组名',
+    description          varchar(250)    null                comment '相关介绍',
+    job_class_name       varchar(250)    not null            comment '执行任务类名称',
+    is_durable           varchar(1)      not null            comment '是否持久化',
+    is_nonconcurrent     varchar(1)      not null            comment '是否并发',
+    is_update_data       varchar(1)      not null            comment '是否更新数据',
+    requests_recovery    varchar(1)      not null            comment '是否接受恢复执行',
+    job_data             blob            null                comment '存放持久化job对象',
+    primary key (sched_name, job_name, job_group)
+) engine=innodb comment = '任务详细信息表';
+
+-- ----------------------------
+-- 2、 存储已配置的 Trigger 的信息
+-- ----------------------------
+create table QRTZ_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment '触发器的名字',
+    trigger_group        varchar(200)    not null            comment '触发器所属组的名字',
+    job_name             varchar(200)    not null            comment 'qrtz_job_details表job_name的外键',
+    job_group            varchar(200)    not null            comment 'qrtz_job_details表job_group的外键',
+    description          varchar(250)    null                comment '相关介绍',
+    next_fire_time       bigint(13)      null                comment '上一次触发时间(毫秒)',
+    prev_fire_time       bigint(13)      null                comment '下一次触发时间(默认为-1表示不触发)',
+    priority             integer         null                comment '优先级',
+    trigger_state        varchar(16)     not null            comment '触发器状态',
+    trigger_type         varchar(8)      not null            comment '触发器的类型',
+    start_time           bigint(13)      not null            comment '开始时间',
+    end_time             bigint(13)      null                comment '结束时间',
+    calendar_name        varchar(200)    null                comment '日程表名称',
+    misfire_instr        smallint(2)     null                comment '补偿执行的策略',
+    job_data             blob            null                comment '存放持久化job对象',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, job_name, job_group) references QRTZ_JOB_DETAILS(sched_name, job_name, job_group)
+) engine=innodb comment = '触发器详细信息表';
+
+-- ----------------------------
+-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数
+-- ----------------------------
+create table QRTZ_SIMPLE_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    repeat_count         bigint(7)       not null            comment '重复的次数统计',
+    repeat_interval      bigint(12)      not null            comment '重复的间隔时间',
+    times_triggered      bigint(10)      not null            comment '已经触发的次数',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = '简单触发器的信息表';
+
+-- ----------------------------
+-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息
+-- ---------------------------- 
+create table QRTZ_CRON_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    cron_expression      varchar(200)    not null            comment 'cron表达式',
+    time_zone_id         varchar(80)                         comment '时区',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = 'Cron类型的触发器表';
+
+-- ----------------------------
+-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
+-- ---------------------------- 
+create table QRTZ_BLOB_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    blob_data            blob            null                comment '存放持久化Trigger对象',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = 'Blob类型的触发器表';
+
+-- ----------------------------
+-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围
+-- ---------------------------- 
+create table QRTZ_CALENDARS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    calendar_name        varchar(200)    not null            comment '日历名称',
+    calendar             blob            not null            comment '存放持久化calendar对象',
+    primary key (sched_name, calendar_name)
+) engine=innodb comment = '日历信息表';
+
+-- ----------------------------
+-- 7、 存储已暂停的 Trigger 组的信息
+-- ---------------------------- 
+create table QRTZ_PAUSED_TRIGGER_GRPS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    primary key (sched_name, trigger_group)
+) engine=innodb comment = '暂停的触发器表';
+
+-- ----------------------------
+-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
+-- ---------------------------- 
+create table QRTZ_FIRED_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    entry_id             varchar(95)     not null            comment '调度器实例id',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    instance_name        varchar(200)    not null            comment '调度器实例名',
+    fired_time           bigint(13)      not null            comment '触发的时间',
+    sched_time           bigint(13)      not null            comment '定时器制定的时间',
+    priority             integer         not null            comment '优先级',
+    state                varchar(16)     not null            comment '状态',
+    job_name             varchar(200)    null                comment '任务名称',
+    job_group            varchar(200)    null                comment '任务组名',
+    is_nonconcurrent     varchar(1)      null                comment '是否并发',
+    requests_recovery    varchar(1)      null                comment '是否接受恢复执行',
+    primary key (sched_name, entry_id)
+) engine=innodb comment = '已触发的触发器表';
+
+-- ----------------------------
+-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例
+-- ---------------------------- 
+create table QRTZ_SCHEDULER_STATE (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    instance_name        varchar(200)    not null            comment '实例名称',
+    last_checkin_time    bigint(13)      not null            comment '上次检查时间',
+    checkin_interval     bigint(13)      not null            comment '检查间隔时间',
+    primary key (sched_name, instance_name)
+) engine=innodb comment = '调度器状态表';
+
+-- ----------------------------
+-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁)
+-- ---------------------------- 
+create table QRTZ_LOCKS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    lock_name            varchar(40)     not null            comment '悲观锁名称',
+    primary key (sched_name, lock_name)
+) engine=innodb comment = '存储的悲观锁信息表';
+
+-- ----------------------------
+-- 11、 Quartz集群实现同步机制的行锁表
+-- ---------------------------- 
+create table QRTZ_SIMPROP_TRIGGERS (
+    sched_name           varchar(120)    not null            comment '调度名称',
+    trigger_name         varchar(200)    not null            comment 'qrtz_triggers表trigger_name的外键',
+    trigger_group        varchar(200)    not null            comment 'qrtz_triggers表trigger_group的外键',
+    str_prop_1           varchar(512)    null                comment 'String类型的trigger的第一个参数',
+    str_prop_2           varchar(512)    null                comment 'String类型的trigger的第二个参数',
+    str_prop_3           varchar(512)    null                comment 'String类型的trigger的第三个参数',
+    int_prop_1           int             null                comment 'int类型的trigger的第一个参数',
+    int_prop_2           int             null                comment 'int类型的trigger的第二个参数',
+    long_prop_1          bigint          null                comment 'long类型的trigger的第一个参数',
+    long_prop_2          bigint          null                comment 'long类型的trigger的第二个参数',
+    dec_prop_1           numeric(13,4)   null                comment 'decimal类型的trigger的第一个参数',
+    dec_prop_2           numeric(13,4)   null                comment 'decimal类型的trigger的第二个参数',
+    bool_prop_1          varchar(1)      null                comment 'Boolean类型的trigger的第一个参数',
+    bool_prop_2          varchar(1)      null                comment 'Boolean类型的trigger的第二个参数',
+    primary key (sched_name, trigger_name, trigger_group),
+    foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
+) engine=innodb comment = '同步机制的行锁表';
+
+commit;

+ 5 - 0
sql/run_import.sql

@@ -0,0 +1,5 @@
+SET DEFINE OFF;
+SET FEEDBACK OFF;
+start gw_dm.sql
+start quartz_dm.sql
+exit;

+ 115 - 0
sql/seed_data_dameng.sql

@@ -0,0 +1,115 @@
+-- 达梦数据库种子数据 (INSERT only, tables already exist)
+-- 在 DBeaver/Navicat 中直接执行此文件
+
+-- 只删除种子数据,保留已导入的菜单(83行)
+DELETE FROM SYS_USER_ROLE;
+DELETE FROM SYS_USER_POST;
+DELETE FROM SYS_ROLE_DEPT;
+DELETE FROM SYS_USER;
+DELETE FROM SYS_POST;
+DELETE FROM SYS_DEPT;
+DELETE FROM SYS_ROLE;
+DELETE FROM SYS_DICT_DATA;
+DELETE FROM SYS_DICT_TYPE;
+DELETE FROM SYS_CONFIG;
+DELETE FROM SYS_JOB;
+DELETE FROM SYS_NOTICE;
+
+-- 部门
+INSERT INTO SYS_DEPT VALUES(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+INSERT INTO SYS_DEPT VALUES(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', SYSDATE, '', NULL);
+
+-- 用户 (admin/admin123, ry/admin123 的 bcrypt hash)
+INSERT INTO SYS_USER VALUES(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', SYSDATE, SYSDATE, 'admin', SYSDATE, '', NULL, '管理员');
+INSERT INTO SYS_USER VALUES(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', SYSDATE, SYSDATE, 'admin', SYSDATE, '', NULL, '测试员');
+
+-- 岗位
+INSERT INTO SYS_POST VALUES(1, 'ceo', '董事长', 1, '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_POST VALUES(2, 'se', '项目经理', 2, '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_POST VALUES(3, 'hr', '人力资源', 3, '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_POST VALUES(4, 'user', '普通员工', 4, '0', 'admin', SYSDATE, '', NULL, '');
+
+-- 角色
+INSERT INTO SYS_ROLE VALUES('1', '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', SYSDATE, '', NULL, '超级管理员');
+INSERT INTO SYS_ROLE VALUES('2', '普通角色', 'common', 2, '2', 1, 1, '0', '0', 'admin', SYSDATE, '', NULL, '普通角色');
+
+-- 用户角色关联
+INSERT INTO SYS_USER_ROLE VALUES('1', '1');
+INSERT INTO SYS_USER_ROLE VALUES('2', '2');
+
+-- 用户岗位关联
+INSERT INTO SYS_USER_POST VALUES('1', '1');
+INSERT INTO SYS_USER_POST VALUES('2', '2');
+
+-- 角色部门关联
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '100');
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '101');
+INSERT INTO SYS_ROLE_DEPT VALUES('2', '105');
+
+-- 字典类型
+INSERT INTO SYS_DICT_TYPE VALUES(1, '用户性别', 'sys_user_sex', '0', 'admin', SYSDATE, '', NULL, '用户性别列表');
+INSERT INTO SYS_DICT_TYPE VALUES(2, '菜单状态', 'sys_show_hide', '0', 'admin', SYSDATE, '', NULL, '菜单状态列表');
+INSERT INTO SYS_DICT_TYPE VALUES(3, '系统开关', 'sys_normal_disable', '0', 'admin', SYSDATE, '', NULL, '系统开关列表');
+INSERT INTO SYS_DICT_TYPE VALUES(4, '任务状态', 'sys_job_status', '0', 'admin', SYSDATE, '', NULL, '任务状态列表');
+INSERT INTO SYS_DICT_TYPE VALUES(5, '任务分组', 'sys_job_group', '0', 'admin', SYSDATE, '', NULL, '任务分组列表');
+INSERT INTO SYS_DICT_TYPE VALUES(6, '系统是否', 'sys_yes_no', '0', 'admin', SYSDATE, '', NULL, '系统是否列表');
+INSERT INTO SYS_DICT_TYPE VALUES(7, '通知类型', 'sys_notice_type', '0', 'admin', SYSDATE, '', NULL, '通知类型列表');
+INSERT INTO SYS_DICT_TYPE VALUES(8, '通知状态', 'sys_notice_status', '0', 'admin', SYSDATE, '', NULL, '通知状态列表');
+INSERT INTO SYS_DICT_TYPE VALUES(9, '操作类型', 'sys_oper_type', '0', 'admin', SYSDATE, '', NULL, '操作类型列表');
+INSERT INTO SYS_DICT_TYPE VALUES(10, '系统状态', 'sys_common_status', '0', 'admin', SYSDATE, '', NULL, '登录状态列表');
+
+-- 字典数据
+INSERT INTO SYS_DICT_DATA VALUES(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', SYSDATE, '', NULL, '性别男');
+INSERT INTO SYS_DICT_DATA VALUES(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', SYSDATE, '', NULL, '性别女');
+INSERT INTO SYS_DICT_DATA VALUES(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', SYSDATE, '', NULL, '性别未知');
+INSERT INTO SYS_DICT_DATA VALUES(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_DICT_DATA VALUES(29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', SYSDATE, '', NULL, '');
+
+-- 参数配置
+INSERT INTO SYS_CONFIG VALUES(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(6, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(7, '用户管理-初始密码修改策略', 'sys.account.initPasswordModify', '1', 'Y', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_CONFIG VALUES(8, '用户管理-账号密码更新周期', 'sys.account.passwordValidateDays', '0', 'Y', 'admin', SYSDATE, '', NULL, '');
+
+-- 定时任务
+INSERT INTO SYS_JOB VALUES(1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_JOB VALUES(2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(''ry'')', '0/15 * * * * ?', '3', '1', '1', 'admin', SYSDATE, '', NULL, '');
+INSERT INTO SYS_JOB VALUES(3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(''ry'', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', SYSDATE, '', NULL, '');
+
+COMMIT;

Некоторые файлы не были показаны из-за большого количества измененных файлов