소스 검색

前端页面更新

dumingliang 1 주 전
부모
커밋
ca0725d917
66개의 변경된 파일2969개의 추가작업 그리고 218개의 파일을 삭제
  1. 33 2
      .mybatis/index.json
  2. 1 1
      README.md
  3. 50 0
      docs/init-front-menu.sql
  4. 10 0
      gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeBizController.java
  5. 10 0
      gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeQualityController.java
  6. 8 0
      gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeStationController.java
  7. 9 0
      gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeUnitController.java
  8. 46 0
      gw-admin/src/main/java/com/goldenwater/web/controller/system/SysFrontMenuController.java
  9. 3 3
      gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtFileController.java
  10. 3 3
      gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtSlDataController.java
  11. 3 3
      gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtStationController.java
  12. 3 3
      gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtStatisticsController.java
  13. 3 3
      gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtSzDataController.java
  14. 13 0
      gw-common/src/main/java/com/goldenwater/common/core/domain/entity/SysMenu.java
  15. 1 1
      gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtCount.java
  16. 1 1
      gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtFile.java
  17. 1 1
      gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtSlData.java
  18. 1 1
      gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtStation.java
  19. 1 1
      gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtSzData.java
  20. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtCountMapper.java
  21. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtFileMapper.java
  22. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtSlDataMapper.java
  23. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtStationMapper.java
  24. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtSzDataMapper.java
  25. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtCountService.java
  26. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtFileService.java
  27. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtSlDataService.java
  28. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtStationService.java
  29. 2 2
      gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtSzDataService.java
  30. 4 4
      gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtCountServiceImpl.java
  31. 4 4
      gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtFileServiceImpl.java
  32. 4 4
      gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtSlDataServiceImpl.java
  33. 4 4
      gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtStationServiceImpl.java
  34. 4 4
      gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtSzDataServiceImpl.java
  35. 6 6
      gw-gx/src/main/resources/mapper/yjjt/YjjtCountMapper.xml
  36. 4 4
      gw-gx/src/main/resources/mapper/yjjt/YjjtFileMapper.xml
  37. 5 5
      gw-gx/src/main/resources/mapper/yjjt/YjjtSlDataMapper.xml
  38. 5 5
      gw-gx/src/main/resources/mapper/yjjt/YjjtStationMapper.xml
  39. 5 5
      gw-gx/src/main/resources/mapper/yjjt/YjjtSzDataMapper.xml
  40. 27 0
      gw-system/src/main/java/com/goldenwater/system/domain/vo/MetaVo.java
  41. 15 0
      gw-system/src/main/java/com/goldenwater/system/mapper/SysMenuMapper.java
  42. 16 0
      gw-system/src/main/java/com/goldenwater/system/service/ISysMenuService.java
  43. 34 1
      gw-system/src/main/java/com/goldenwater/system/service/impl/SysMenuServiceImpl.java
  44. 2 2
      gw-system/src/main/java/com/goldenwater/system/service/impl/SysUserOnlineServiceImpl.java
  45. 25 4
      gw-system/src/main/resources/mapper/system/SysMenuMapper.xml
  46. 1 0
      gw-ui/package.json
  47. 14 1
      gw-ui/src/api/menu.js
  48. BIN
      gw-ui/src/assets/images/pay.png
  49. BIN
      gw-ui/src/assets/images/tba-gxpt-logo-hei.png
  50. BIN
      gw-ui/src/assets/images/tba-gxpt-logo.png
  51. 234 0
      gw-ui/src/layout/FrontLayout.vue
  52. 17 25
      gw-ui/src/layout/components/TopNavbar/index.vue
  53. 6 4
      gw-ui/src/permission.js
  54. 18 29
      gw-ui/src/router/index.js
  55. 57 4
      gw-ui/src/store/modules/permission.js
  56. 78 0
      gw-ui/src/views/front/DataCommon.vue
  57. 78 0
      gw-ui/src/views/front/DataStatistics.vue
  58. 78 0
      gw-ui/src/views/front/Document.vue
  59. 690 0
      gw-ui/src/views/front/Gis.vue
  60. 471 0
      gw-ui/src/views/front/Home.vue
  61. 78 0
      gw-ui/src/views/front/OneThreeLake.vue
  62. 78 0
      gw-ui/src/views/front/Shl.vue
  63. 78 0
      gw-ui/src/views/front/SystemManage.vue
  64. 78 0
      gw-ui/src/views/front/Xaj.vue
  65. 78 0
      gw-ui/src/views/front/Yjjt.vue
  66. 453 60
      gw-ui/src/views/index.vue

+ 33 - 2
.mybatis/index.json

@@ -1,6 +1,37 @@
 {
   "version": "1.0",
-  "timestamp": 1784434148347,
+  "timestamp": 1784552948950,
   "projectRoot": "d:\\guochan\\tba-gx-guochan",
-  "entries": []
+  "entries": [
+    {
+      "path": "gw-framework\\target\\classes\\com\\goldenwater\\framework\\config\\ResourcesConfig.class",
+      "mtime": 1784528384572.2253,
+      "size": 4257
+    },
+    {
+      "path": "gw-common\\target\\classes\\com\\goldenwater\\common\\config\\RuoYiConfig.class",
+      "mtime": 1784552659877.3826,
+      "size": 2656
+    },
+    {
+      "path": "gw-system\\target\\classes\\com\\goldenwater\\system\\domain\\SysConfig.class",
+      "mtime": 1784552817135.2542,
+      "size": 3593
+    },
+    {
+      "path": "gw-framework\\target\\classes\\com\\goldenwater\\framework\\config\\ThreadPoolConfig.class",
+      "mtime": 1784552913658.5635,
+      "size": 2442
+    },
+    {
+      "path": "gw-admin\\target\\classes\\com\\goldenwater\\GoldenwaterApplication.class",
+      "mtime": 1784552943458.064,
+      "size": 1060
+    },
+    {
+      "path": "gw-admin\\target\\classes\\com\\goldenwater\\web\\core\\config\\SwaggerConfig.class",
+      "mtime": 1784552943887.2979,
+      "size": 3793
+    }
+  ]
 }

+ 1 - 1
README.md

@@ -99,7 +99,7 @@ tba-gx-guochan/
 
 ## 页面路由
 
-### 前台页面(通过 Layout 组件包裹,有公共 Header/Nav/Footer)
+###  (通过 Layout 组件包裹,有公共 Header/Nav/Footer)
 
 | 路由路径 | 页面名称 | 组件 | 功能描述 |
 |---|---|---|---|

+ 50 - 0
docs/init-front-menu.sql

@@ -0,0 +1,50 @@
+-- ============================================================
+-- 添加前台菜单标识字段
+-- ============================================================
+ALTER TABLE sys_menu ADD is_front VARCHAR2(1) DEFAULT '0';
+
+-- ============================================================
+-- 前台菜单初始化SQL
+-- ============================================================
+
+-- 前台首页(路由已在constantRoutes中定义)
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('首页', 0, 0, '/index', 'front/Home', NULL, 1, 0, 'C', '0', '0', 'front:index', 'dashboard', 'admin', SYSDATE, '前台首页', '1');
+
+-- GIS地图展示
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('GIS地图展示', 0, 1, '/gis', 'front/Gis', NULL, 1, 0, 'C', '0', '0', 'front:gis', 'map', 'admin', SYSDATE, 'GIS地图展示', '1');
+
+-- 数据查看
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('数据查看', 0, 2, '/data-common', 'front/DataCommon', NULL, 1, 0, 'C', '0', '0', 'front:data:common', 'table', 'admin', SYSDATE, '数据查看', '1');
+
+-- 数据统计
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('数据统计', 0, 3, '/data-statistics', 'front/DataStatistics', NULL, 1, 0, 'C', '0', '0', 'front:data:statistics', 'chart', 'admin', SYSDATE, '数据统计', '1');
+
+-- 新安江流域
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('新安江流域', 0, 4, '/xaj', 'front/Xaj', NULL, 1, 0, 'C', '0', '0', 'front:xaj', 'component', 'admin', SYSDATE, '新安江流域', '1');
+
+-- 一河三湖
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('一河三湖', 0, 5, '/1h3h', 'front/OneThreeLake', NULL, 1, 0, 'C', '0', '0', 'front:lake', 'component', 'admin', SYSDATE, '一河三湖', '1');
+
+-- 引江济太
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('引江济太', 0, 6, '/yjjt', 'front/Yjjt', NULL, 1, 0, 'C', '0', '0', 'front:yjjt', 'component', 'admin', SYSDATE, '引江济太', '1');
+
+-- 水葫芦
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('水葫芦', 0, 7, '/shl', 'front/Shl', NULL, 1, 0, 'C', '0', '0', 'front:shl', 'component', 'admin', SYSDATE, '水葫芦', '1');
+
+-- 系统管理
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('系统管理', 0, 8, '/systemmanage', 'front/SystemManage', NULL, 1, 0, 'C', '0', '0', 'front:system', 'system', 'admin', SYSDATE, '系统管理', '1');
+
+-- 河湖长制
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark, is_front)
+VALUES ('河湖长制', 0, 9, '/document', 'front/Document', NULL, 1, 0, 'C', '0', '0', 'front:document', 'documentation', 'admin', SYSDATE, '河湖长制', '1');
+
+COMMIT;

+ 10 - 0
gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeBizController.java

@@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import com.goldenwater.common.annotation.Anonymous;
 import com.goldenwater.common.annotation.Log;
 import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
@@ -97,4 +98,13 @@ public class GxExchangeBizController extends BaseController
     {
         return toAjax(gxExchangeBizService.deleteGxExchangeBizByIds(ids));
     }
+
+    @Anonymous
+    @GetMapping("/front/list")
+    public AjaxResult frontList(@RequestParam(value = "bizUnit", required = false) String bizUnit)
+    {
+        GxExchangeBiz biz = new GxExchangeBiz();
+        biz.setBizUnit(bizUnit);
+        return success(gxExchangeBizService.selectGxExchangeBizList(biz));
+    }
 }

+ 10 - 0
gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeQualityController.java

@@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import com.goldenwater.common.annotation.Anonymous;
 import com.goldenwater.common.annotation.Log;
 import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
@@ -57,4 +58,13 @@ public class GxExchangeQualityController extends BaseController
     {
         return toAjax(gxExchangeQualityService.deleteGxExchangeQualityByIds(ids));
     }
+
+    @Anonymous
+    @GetMapping("/front/list")
+    public AjaxResult frontList(@RequestParam(value = "stcd", required = false) String stcd)
+    {
+        GxExchangeQuality quality = new GxExchangeQuality();
+        quality.setStcd(stcd);
+        return success(gxExchangeQualityService.selectGxExchangeQualityList(quality));
+    }
 }

+ 8 - 0
gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeStationController.java

@@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
+import com.goldenwater.common.annotation.Anonymous;
 import com.goldenwater.common.annotation.Log;
 import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
@@ -118,4 +119,11 @@ public class GxExchangeStationController extends BaseController
     {
         return success(gxExchangeStationService.selectStationByBizCode(bizCode));
     }
+
+    @Anonymous
+    @GetMapping("/front/listByBizCode/{bizCode}")
+    public AjaxResult frontListByBizCode(@PathVariable String bizCode)
+    {
+        return success(gxExchangeStationService.selectStationByBizCode(bizCode));
+    }
 }

+ 9 - 0
gw-admin/src/main/java/com/goldenwater/web/controller/gx/GxExchangeUnitController.java

@@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import com.goldenwater.common.annotation.Anonymous;
 import com.goldenwater.common.annotation.Log;
 import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
@@ -79,4 +80,12 @@ public class GxExchangeUnitController extends BaseController
     {
         return toAjax(gxExchangeUnitService.deleteGxExchangeUnitByIds(ids));
     }
+
+    @Anonymous
+    @GetMapping("/front/list")
+    public AjaxResult frontList()
+    {
+        GxExchangeUnit unit = new GxExchangeUnit();
+        return success(gxExchangeUnitService.selectGxExchangeUnitList(unit));
+    }
 }

+ 46 - 0
gw-admin/src/main/java/com/goldenwater/web/controller/system/SysFrontMenuController.java

@@ -0,0 +1,46 @@
+package com.goldenwater.web.controller.system;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.goldenwater.common.core.domain.AjaxResult;
+import com.goldenwater.common.core.domain.entity.SysMenu;
+import com.goldenwater.common.utils.SecurityUtils;
+import com.goldenwater.system.service.ISysMenuService;
+import com.goldenwater.system.domain.vo.RouterVo;
+
+/**
+ * 前台菜单控制器
+ */
+@RestController
+@RequestMapping("/system/front")
+public class SysFrontMenuController
+{
+    @Autowired
+    private ISysMenuService menuService;
+
+    /**
+     * 获取前台菜单路由
+     */
+    @GetMapping("/getRouters")
+    public AjaxResult getFrontRouters()
+    {
+        Long userId = SecurityUtils.getUserId();
+        List<SysMenu> menus = menuService.selectFrontMenuTreeByUserId(userId);
+        List<RouterVo> routers = menuService.buildFrontMenus(menus);
+        return AjaxResult.success(routers);
+    }
+
+    /**
+     * 获取前台菜单列表
+     */
+    @GetMapping("/menuList")
+    public AjaxResult getFrontMenuList()
+    {
+        Long userId = SecurityUtils.getUserId();
+        List<SysMenu> menus = menuService.selectFrontMenuTreeByUserId(userId);
+        return AjaxResult.success(menus);
+    }
+}

+ 3 - 3
gw-admin/src/main/java/com/goldenwater/web/controller/gx/YjjtFileController.java → gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtFileController.java

@@ -1,4 +1,4 @@
-package com.goldenwater.web.controller.gx;
+package com.goldenwater.web.controller.yjjt;
 
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -9,8 +9,8 @@ import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
 import com.goldenwater.common.core.page.TableDataInfo;
 import com.goldenwater.common.enums.BusinessType;
-import com.goldenwater.gx.yjjt.domain.YjjtFile;
-import com.goldenwater.gx.yjjt.service.IYjjtFileService;
+import com.goldenwater.yjjt.domain.YjjtFile;
+import com.goldenwater.yjjt.service.IYjjtFileService;
 
 /**
  * 引江济太文件Controller

+ 3 - 3
gw-admin/src/main/java/com/goldenwater/web/controller/gx/YjjtSlDataController.java → gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtSlDataController.java

@@ -1,4 +1,4 @@
-package com.goldenwater.web.controller.gx;
+package com.goldenwater.web.controller.yjjt;
 
 import java.util.List;
 import java.util.Map;
@@ -12,8 +12,8 @@ import com.goldenwater.common.core.domain.AjaxResult;
 import com.goldenwater.common.core.page.TableDataInfo;
 import com.goldenwater.common.enums.BusinessType;
 import com.goldenwater.common.utils.poi.ExcelUtil;
-import com.goldenwater.gx.yjjt.domain.YjjtSlData;
-import com.goldenwater.gx.yjjt.service.IYjjtSlDataService;
+import com.goldenwater.yjjt.domain.YjjtSlData;
+import com.goldenwater.yjjt.service.IYjjtSlDataService;
 
 /**
  * 引江济太水量数据Controller

+ 3 - 3
gw-admin/src/main/java/com/goldenwater/web/controller/gx/YjjtStationController.java → gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtStationController.java

@@ -1,4 +1,4 @@
-package com.goldenwater.web.controller.gx;
+package com.goldenwater.web.controller.yjjt;
 
 import java.util.List;
 import jakarta.servlet.http.HttpServletResponse;
@@ -11,8 +11,8 @@ import com.goldenwater.common.core.domain.AjaxResult;
 import com.goldenwater.common.core.page.TableDataInfo;
 import com.goldenwater.common.enums.BusinessType;
 import com.goldenwater.common.utils.poi.ExcelUtil;
-import com.goldenwater.gx.yjjt.domain.YjjtStation;
-import com.goldenwater.gx.yjjt.service.IYjjtStationService;
+import com.goldenwater.yjjt.domain.YjjtStation;
+import com.goldenwater.yjjt.service.IYjjtStationService;
 
 /**
  * 引江济太站点Controller

+ 3 - 3
gw-admin/src/main/java/com/goldenwater/web/controller/gx/YjjtStatisticsController.java → gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtStatisticsController.java

@@ -1,4 +1,4 @@
-package com.goldenwater.web.controller.gx;
+package com.goldenwater.web.controller.yjjt;
 
 import java.util.List;
 import java.util.Map;
@@ -7,8 +7,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
-import com.goldenwater.gx.yjjt.domain.YjjtCount;
-import com.goldenwater.gx.yjjt.service.IYjjtCountService;
+import com.goldenwater.yjjt.domain.YjjtCount;
+import com.goldenwater.yjjt.service.IYjjtCountService;
 
 /**
  * 引江济太数据统计Controller

+ 3 - 3
gw-admin/src/main/java/com/goldenwater/web/controller/gx/YjjtSzDataController.java → gw-admin/src/main/java/com/goldenwater/web/controller/yjjt/YjjtSzDataController.java

@@ -1,4 +1,4 @@
-package com.goldenwater.web.controller.gx;
+package com.goldenwater.web.controller.yjjt;
 
 import java.util.List;
 import jakarta.servlet.http.HttpServletResponse;
@@ -11,8 +11,8 @@ import com.goldenwater.common.core.domain.AjaxResult;
 import com.goldenwater.common.core.page.TableDataInfo;
 import com.goldenwater.common.enums.BusinessType;
 import com.goldenwater.common.utils.poi.ExcelUtil;
-import com.goldenwater.gx.yjjt.domain.YjjtSzData;
-import com.goldenwater.gx.yjjt.service.IYjjtSzDataService;
+import com.goldenwater.yjjt.domain.YjjtSzData;
+import com.goldenwater.yjjt.service.IYjjtSzDataService;
 
 /**
  * 引江济太水质数据Controller

+ 13 - 0
gw-common/src/main/java/com/goldenwater/common/core/domain/entity/SysMenu.java

@@ -66,6 +66,9 @@ public class SysMenu extends BaseEntity
     /** 菜单图标 */
     private String icon;
 
+    /** 是否前台菜单(0否 1是) */
+    private String isFront;
+
     /** 子菜单 */
     private List<SysMenu> children = new ArrayList<SysMenu>();
 
@@ -236,6 +239,16 @@ public class SysMenu extends BaseEntity
         this.icon = icon;
     }
 
+    public String getIsFront()
+    {
+        return isFront;
+    }
+
+    public void setIsFront(String isFront)
+    {
+        this.isFront = isFront;
+    }
+
     public List<SysMenu> getChildren()
     {
         return children;

+ 1 - 1
gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtCount.java

@@ -1,4 +1,4 @@
-package com.goldenwater.gx.yjjt.domain;
+package com.goldenwater.yjjt.domain;
 
 /**
  * 引江济太统计对象(非持久化,用于聚合查询)

+ 1 - 1
gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtFile.java

@@ -1,4 +1,4 @@
-package com.goldenwater.gx.yjjt.domain;
+package com.goldenwater.yjjt.domain;
 
 import java.util.Date;
 import com.fasterxml.jackson.annotation.JsonFormat;

+ 1 - 1
gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtSlData.java

@@ -1,4 +1,4 @@
-package com.goldenwater.gx.yjjt.domain;
+package com.goldenwater.yjjt.domain;
 
 import java.math.BigDecimal;
 import java.util.Date;

+ 1 - 1
gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtStation.java

@@ -1,4 +1,4 @@
-package com.goldenwater.gx.yjjt.domain;
+package com.goldenwater.yjjt.domain;
 
 import com.goldenwater.common.annotation.Excel;
 import com.goldenwater.common.core.domain.BaseEntity;

+ 1 - 1
gw-gx/src/main/java/com/goldenwater/yjjt/domain/YjjtSzData.java

@@ -1,4 +1,4 @@
-package com.goldenwater.gx.yjjt.domain;
+package com.goldenwater.yjjt.domain;
 
 import java.math.BigDecimal;
 import java.util.Date;

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtCountMapper.java

@@ -1,9 +1,9 @@
-package com.goldenwater.gx.yjjt.mapper;
+package com.goldenwater.yjjt.mapper;
 
 import java.util.List;
 import java.util.Map;
 import org.apache.ibatis.annotations.Param;
-import com.goldenwater.gx.yjjt.domain.YjjtCount;
+import com.goldenwater.yjjt.domain.YjjtCount;
 
 /**
  * 引江济太统计Mapper接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtFileMapper.java

@@ -1,7 +1,7 @@
-package com.goldenwater.gx.yjjt.mapper;
+package com.goldenwater.yjjt.mapper;
 
 import java.util.List;
-import com.goldenwater.gx.yjjt.domain.YjjtFile;
+import com.goldenwater.yjjt.domain.YjjtFile;
 
 /**
  * 引江济太文件Mapper接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtSlDataMapper.java

@@ -1,9 +1,9 @@
-package com.goldenwater.gx.yjjt.mapper;
+package com.goldenwater.yjjt.mapper;
 
 import java.util.List;
 import java.util.Map;
 import org.apache.ibatis.annotations.Param;
-import com.goldenwater.gx.yjjt.domain.YjjtSlData;
+import com.goldenwater.yjjt.domain.YjjtSlData;
 
 /**
  * 引江济太水量数据Mapper接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtStationMapper.java

@@ -1,9 +1,9 @@
-package com.goldenwater.gx.yjjt.mapper;
+package com.goldenwater.yjjt.mapper;
 
 import java.util.List;
 import java.util.Map;
 import org.apache.ibatis.annotations.Param;
-import com.goldenwater.gx.yjjt.domain.YjjtStation;
+import com.goldenwater.yjjt.domain.YjjtStation;
 
 /**
  * 引江济太站点Mapper接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/mapper/YjjtSzDataMapper.java

@@ -1,9 +1,9 @@
-package com.goldenwater.gx.yjjt.mapper;
+package com.goldenwater.yjjt.mapper;
 
 import java.util.List;
 import java.util.Map;
 import org.apache.ibatis.annotations.Param;
-import com.goldenwater.gx.yjjt.domain.YjjtSzData;
+import com.goldenwater.yjjt.domain.YjjtSzData;
 
 /**
  * 引江济太水质数据Mapper接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtCountService.java

@@ -1,8 +1,8 @@
-package com.goldenwater.gx.yjjt.service;
+package com.goldenwater.yjjt.service;
 
 import java.util.List;
 import java.util.Map;
-import com.goldenwater.gx.yjjt.domain.YjjtCount;
+import com.goldenwater.yjjt.domain.YjjtCount;
 
 /**
  * 引江济太统计Service接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtFileService.java

@@ -1,7 +1,7 @@
-package com.goldenwater.gx.yjjt.service;
+package com.goldenwater.yjjt.service;
 
 import java.util.List;
-import com.goldenwater.gx.yjjt.domain.YjjtFile;
+import com.goldenwater.yjjt.domain.YjjtFile;
 
 /**
  * 引江济太文件Service接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtSlDataService.java

@@ -1,8 +1,8 @@
-package com.goldenwater.gx.yjjt.service;
+package com.goldenwater.yjjt.service;
 
 import java.util.List;
 import java.util.Map;
-import com.goldenwater.gx.yjjt.domain.YjjtSlData;
+import com.goldenwater.yjjt.domain.YjjtSlData;
 
 /**
  * 引江济太水量数据Service接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtStationService.java

@@ -1,7 +1,7 @@
-package com.goldenwater.gx.yjjt.service;
+package com.goldenwater.yjjt.service;
 
 import java.util.List;
-import com.goldenwater.gx.yjjt.domain.YjjtStation;
+import com.goldenwater.yjjt.domain.YjjtStation;
 
 /**
  * 引江济太站点Service接口

+ 2 - 2
gw-gx/src/main/java/com/goldenwater/yjjt/service/IYjjtSzDataService.java

@@ -1,8 +1,8 @@
-package com.goldenwater.gx.yjjt.service;
+package com.goldenwater.yjjt.service;
 
 import java.util.List;
 import java.util.Map;
-import com.goldenwater.gx.yjjt.domain.YjjtSzData;
+import com.goldenwater.yjjt.domain.YjjtSzData;
 
 /**
  * 引江济太水质数据Service接口

+ 4 - 4
gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtCountServiceImpl.java

@@ -1,12 +1,12 @@
-package com.goldenwater.gx.yjjt.service.impl;
+package com.goldenwater.yjjt.service.impl;
 
 import java.util.List;
 import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.goldenwater.gx.yjjt.domain.YjjtCount;
-import com.goldenwater.gx.yjjt.mapper.YjjtCountMapper;
-import com.goldenwater.gx.yjjt.service.IYjjtCountService;
+import com.goldenwater.yjjt.domain.YjjtCount;
+import com.goldenwater.yjjt.mapper.YjjtCountMapper;
+import com.goldenwater.yjjt.service.IYjjtCountService;
 
 /**
  * 引江济太统计Service业务层处理

+ 4 - 4
gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtFileServiceImpl.java

@@ -1,11 +1,11 @@
-package com.goldenwater.gx.yjjt.service.impl;
+package com.goldenwater.yjjt.service.impl;
 
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.goldenwater.gx.yjjt.domain.YjjtFile;
-import com.goldenwater.gx.yjjt.mapper.YjjtFileMapper;
-import com.goldenwater.gx.yjjt.service.IYjjtFileService;
+import com.goldenwater.yjjt.domain.YjjtFile;
+import com.goldenwater.yjjt.mapper.YjjtFileMapper;
+import com.goldenwater.yjjt.service.IYjjtFileService;
 
 /**
  * 引江济太文件Service业务层处理

+ 4 - 4
gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtSlDataServiceImpl.java

@@ -1,12 +1,12 @@
-package com.goldenwater.gx.yjjt.service.impl;
+package com.goldenwater.yjjt.service.impl;
 
 import java.util.List;
 import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.goldenwater.gx.yjjt.domain.YjjtSlData;
-import com.goldenwater.gx.yjjt.mapper.YjjtSlDataMapper;
-import com.goldenwater.gx.yjjt.service.IYjjtSlDataService;
+import com.goldenwater.yjjt.domain.YjjtSlData;
+import com.goldenwater.yjjt.mapper.YjjtSlDataMapper;
+import com.goldenwater.yjjt.service.IYjjtSlDataService;
 
 /**
  * 引江济太水量数据Service业务层处理

+ 4 - 4
gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtStationServiceImpl.java

@@ -1,12 +1,12 @@
-package com.goldenwater.gx.yjjt.service.impl;
+package com.goldenwater.yjjt.service.impl;
 
 import java.util.List;
 import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.goldenwater.gx.yjjt.domain.YjjtStation;
-import com.goldenwater.gx.yjjt.mapper.YjjtStationMapper;
-import com.goldenwater.gx.yjjt.service.IYjjtStationService;
+import com.goldenwater.yjjt.domain.YjjtStation;
+import com.goldenwater.yjjt.mapper.YjjtStationMapper;
+import com.goldenwater.yjjt.service.IYjjtStationService;
 
 /**
  * 引江济太站点Service业务层处理

+ 4 - 4
gw-gx/src/main/java/com/goldenwater/yjjt/service/impl/YjjtSzDataServiceImpl.java

@@ -1,12 +1,12 @@
-package com.goldenwater.gx.yjjt.service.impl;
+package com.goldenwater.yjjt.service.impl;
 
 import java.util.List;
 import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.goldenwater.gx.yjjt.domain.YjjtSzData;
-import com.goldenwater.gx.yjjt.mapper.YjjtSzDataMapper;
-import com.goldenwater.gx.yjjt.service.IYjjtSzDataService;
+import com.goldenwater.yjjt.domain.YjjtSzData;
+import com.goldenwater.yjjt.mapper.YjjtSzDataMapper;
+import com.goldenwater.yjjt.service.IYjjtSzDataService;
 
 /**
  * 引江济太水质数据Service业务层处理

+ 6 - 6
gw-gx/src/main/resources/mapper/yjjt/YjjtCountMapper.xml

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.goldenwater.gx.yjjt.mapper.YjjtCountMapper">
+<mapper namespace="com.goldenwater.yjjt.mapper.YjjtCountMapper">
 
     <!-- 按年份和单位统计数据条数 -->
-    <select id="selectCount" resultType="com.goldenwater.gx.yjjt.domain.YjjtCount">
+    <select id="selectCount" resultType="com.goldenwater.yjjt.domain.YjjtCount">
         select ty, year, addvcd, count as count from (
             select 'SL' as ty,
                    TO_CHAR(spt, 'YYYY') as year,
@@ -44,7 +44,7 @@
     </select>
 
     <!-- 按年份和单位统计站点监测天数 -->
-    <select id="selectStcdCount" resultType="com.goldenwater.gx.yjjt.domain.YjjtCount">
+    <select id="selectStcdCount" resultType="com.goldenwater.yjjt.domain.YjjtCount">
         select ty, year, addvcd, count(distinct stcd || '_' || dt) as stcdNm from (
             select 'SL' as ty,
                    TO_CHAR(spt, 'YYYY') as year,
@@ -86,7 +86,7 @@
     </select>
 
     <!-- 按站点统计水量数据条数 -->
-    <select id="selectStcdSLCount" resultType="com.goldenwater.gx.yjjt.domain.YjjtCount">
+    <select id="selectStcdSLCount" resultType="com.goldenwater.yjjt.domain.YjjtCount">
         select stcd, count(1) as count,
                UPPER(addvcd) as addvcd,
                TO_CHAR(spt, 'YYYY') as year
@@ -104,7 +104,7 @@
     </select>
 
     <!-- 按站点统计水质数据条数 -->
-    <select id="selectStcdSZCount" resultType="com.goldenwater.gx.yjjt.domain.YjjtCount">
+    <select id="selectStcdSZCount" resultType="com.goldenwater.yjjt.domain.YjjtCount">
         select stcd, count(1) as count,
                UPPER(addvcd) as addvcd,
                TO_CHAR(spt, 'YYYY') as year
@@ -122,7 +122,7 @@
     </select>
 
     <!-- 按年份和单位统计监测频次 -->
-    <select id="selectnum" resultType="com.goldenwater.gx.yjjt.domain.YjjtCount">
+    <select id="selectnum" resultType="com.goldenwater.yjjt.domain.YjjtCount">
         select ty, year, addvcd, sum(cnt) as count from (
             select 'SL' as ty,
                    TO_CHAR(spt, 'YYYY') as year,

+ 4 - 4
gw-gx/src/main/resources/mapper/yjjt/YjjtFileMapper.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.goldenwater.gx.yjjt.mapper.YjjtFileMapper">
+<mapper namespace="com.goldenwater.yjjt.mapper.YjjtFileMapper">
 
-    <resultMap type="com.goldenwater.gx.yjjt.domain.YjjtFile" id="YjjtFileResult">
+    <resultMap type="com.goldenwater.yjjt.domain.YjjtFile" id="YjjtFileResult">
         <id     property="id"            column="id"             />
         <result property="fileViewname"  column="file_viewname"  />
         <result property="fileSavename"  column="file_savename"  />
@@ -29,7 +29,7 @@
         from gx_yjjt_file
     </sql>
 
-    <select id="selectYjjtFileList" parameterType="com.goldenwater.gx.yjjt.domain.YjjtFile" resultMap="YjjtFileResult">
+    <select id="selectYjjtFileList" parameterType="com.goldenwater.yjjt.domain.YjjtFile" resultMap="YjjtFileResult">
         <include refid="selectFileVo"/>
         where del_flag = '0'
         <if test="fileViewname != null and fileViewname != ''">
@@ -52,7 +52,7 @@
         where id = #{id} and del_flag = '0'
     </select>
 
-    <insert id="insertYjjtFile" parameterType="com.goldenwater.gx.yjjt.domain.YjjtFile" useGeneratedKeys="true" keyProperty="id">
+    <insert id="insertYjjtFile" parameterType="com.goldenwater.yjjt.domain.YjjtFile" useGeneratedKeys="true" keyProperty="id">
         insert into gx_yjjt_file
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="fileViewname != null and fileViewname != ''">file_viewname,</if>

+ 5 - 5
gw-gx/src/main/resources/mapper/yjjt/YjjtSlDataMapper.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.goldenwater.gx.yjjt.mapper.YjjtSlDataMapper">
+<mapper namespace="com.goldenwater.yjjt.mapper.YjjtSlDataMapper">
 
-    <resultMap type="com.goldenwater.gx.yjjt.domain.YjjtSlData" id="YjjtSlDataResult">
+    <resultMap type="com.goldenwater.yjjt.domain.YjjtSlData" id="YjjtSlDataResult">
         <id     property="id"         column="id"          />
         <result property="stcd"       column="stcd"        />
         <result property="stnm"       column="stnm"        />
@@ -26,7 +26,7 @@
         from gx_yjjt_sl_data
     </sql>
 
-    <select id="selectYjjtSlDataList" parameterType="com.goldenwater.gx.yjjt.domain.YjjtSlData" resultMap="YjjtSlDataResult">
+    <select id="selectYjjtSlDataList" parameterType="com.goldenwater.yjjt.domain.YjjtSlData" resultMap="YjjtSlDataResult">
         <include refid="selectSlDataVo"/>
         <where>
             <if test="stcd != null and stcd != ''">
@@ -96,7 +96,7 @@
         from dual
     </select>
 
-    <insert id="insertYjjtSlData" parameterType="com.goldenwater.gx.yjjt.domain.YjjtSlData" useGeneratedKeys="true" keyProperty="id">
+    <insert id="insertYjjtSlData" parameterType="com.goldenwater.yjjt.domain.YjjtSlData" useGeneratedKeys="true" keyProperty="id">
         insert into gx_yjjt_sl_data
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="stcd != null and stcd != ''">stcd,</if>
@@ -128,7 +128,7 @@
         </trim>
     </insert>
 
-    <update id="updateYjjtSlData" parameterType="com.goldenwater.gx.yjjt.domain.YjjtSlData">
+    <update id="updateYjjtSlData" parameterType="com.goldenwater.yjjt.domain.YjjtSlData">
         update gx_yjjt_sl_data
         <set>
             <if test="z != null">z = #{z},</if>

+ 5 - 5
gw-gx/src/main/resources/mapper/yjjt/YjjtStationMapper.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.goldenwater.gx.yjjt.mapper.YjjtStationMapper">
+<mapper namespace="com.goldenwater.yjjt.mapper.YjjtStationMapper">
 
-    <resultMap type="com.goldenwater.gx.yjjt.domain.YjjtStation" id="YjjtStationResult">
+    <resultMap type="com.goldenwater.yjjt.domain.YjjtStation" id="YjjtStationResult">
         <id     property="id"        column="id"         />
         <result property="stcd"      column="stcd"       />
         <result property="stnm"      column="stnm"       />
@@ -43,7 +43,7 @@
         from gx_yjjt_station
     </sql>
 
-    <select id="selectYjjtStationList" parameterType="com.goldenwater.gx.yjjt.domain.YjjtStation" resultMap="YjjtStationResult">
+    <select id="selectYjjtStationList" parameterType="com.goldenwater.yjjt.domain.YjjtStation" resultMap="YjjtStationResult">
         <include refid="selectStationVo"/>
         where del_flag = '0'
         <if test="stcd != null and stcd != ''">
@@ -101,7 +101,7 @@
         group by stcd, year
     </select>
 
-    <insert id="insertYjjtStation" parameterType="com.goldenwater.gx.yjjt.domain.YjjtStation" useGeneratedKeys="true" keyProperty="id">
+    <insert id="insertYjjtStation" parameterType="com.goldenwater.yjjt.domain.YjjtStation" useGeneratedKeys="true" keyProperty="id">
         insert into gx_yjjt_station
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="stcd != null and stcd != ''">stcd,</if>
@@ -161,7 +161,7 @@
         </trim>
     </insert>
 
-    <update id="updateYjjtStation" parameterType="com.goldenwater.gx.yjjt.domain.YjjtStation">
+    <update id="updateYjjtStation" parameterType="com.goldenwater.yjjt.domain.YjjtStation">
         update gx_yjjt_station
         <set>
             <if test="stcd != null and stcd != ''">stcd = #{stcd},</if>

+ 5 - 5
gw-gx/src/main/resources/mapper/yjjt/YjjtSzDataMapper.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.goldenwater.gx.yjjt.mapper.YjjtSzDataMapper">
+<mapper namespace="com.goldenwater.yjjt.mapper.YjjtSzDataMapper">
 
-    <resultMap type="com.goldenwater.gx.yjjt.domain.YjjtSzData" id="YjjtSzDataResult">
+    <resultMap type="com.goldenwater.yjjt.domain.YjjtSzData" id="YjjtSzDataResult">
         <id     property="id"          column="id"           />
         <result property="stcd"        column="stcd"         />
         <result property="stnm"        column="stnm"         />
@@ -61,7 +61,7 @@
         from gx_yjjt_sz_data
     </sql>
 
-    <select id="selectYjjtSzDataList" parameterType="com.goldenwater.gx.yjjt.domain.YjjtSzData" resultMap="YjjtSzDataResult">
+    <select id="selectYjjtSzDataList" parameterType="com.goldenwater.yjjt.domain.YjjtSzData" resultMap="YjjtSzDataResult">
         <include refid="selectSzDataVo"/>
         <where>
             <if test="stcd != null and stcd != ''">
@@ -131,7 +131,7 @@
         from dual
     </select>
 
-    <insert id="insertYjjtSzData" parameterType="com.goldenwater.gx.yjjt.domain.YjjtSzData" useGeneratedKeys="true" keyProperty="id">
+    <insert id="insertYjjtSzData" parameterType="com.goldenwater.yjjt.domain.YjjtSzData" useGeneratedKeys="true" keyProperty="id">
         insert into gx_yjjt_sz_data
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="stcd != null and stcd != ''">stcd,</if>
@@ -227,7 +227,7 @@
         </trim>
     </insert>
 
-    <update id="updateYjjtSzData" parameterType="com.goldenwater.gx.yjjt.domain.YjjtSzData">
+    <update id="updateYjjtSzData" parameterType="com.goldenwater.yjjt.domain.YjjtSzData">
         update gx_yjjt_sz_data
         <set>
             <if test="wt != null">wt = #{wt},</if>

+ 27 - 0
gw-system/src/main/java/com/goldenwater/system/domain/vo/MetaVo.java

@@ -29,6 +29,11 @@ public class MetaVo
      */
     private String link;
 
+    /**
+     * 排序号
+     */
+    private Integer orderNum;
+
     public MetaVo()
     {
     }
@@ -64,6 +69,18 @@ public class MetaVo
         }
     }
 
+    public MetaVo(String title, String icon, boolean noCache, String link, Integer orderNum)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+        if (StringUtils.ishttp(link))
+        {
+            this.link = link;
+        }
+        this.orderNum = orderNum;
+    }
+
     public boolean isNoCache()
     {
         return noCache;
@@ -103,4 +120,14 @@ public class MetaVo
     {
         this.link = link;
     }
+
+    public Integer getOrderNum()
+    {
+        return orderNum;
+    }
+
+    public void setOrderNum(Integer orderNum)
+    {
+        this.orderNum = orderNum;
+    }
 }

+ 15 - 0
gw-system/src/main/java/com/goldenwater/system/mapper/SysMenuMapper.java

@@ -138,4 +138,19 @@ public interface SysMenuMapper
      * @return 匹配的菜单列表
      */
     public List<SysMenu> selectMenusByPathOrRouteName(@Param("path") String path, @Param("routeName") String routeName);
+
+    /**
+     * 查询前台菜单列表
+     *
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectFrontMenuTreeAll();
+
+    /**
+     * 根据用户ID查询前台菜单
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectFrontMenuTreeByUserId(Long userId);
 }

+ 16 - 0
gw-system/src/main/java/com/goldenwater/system/service/ISysMenuService.java

@@ -157,4 +157,20 @@ public interface ISysMenuService
      * @return 结果
      */
     public boolean checkRouteConfigUnique(SysMenu menu);
+
+    /**
+     * 根据用户ID查询前台菜单树信息
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectFrontMenuTreeByUserId(Long userId);
+
+    /**
+     * 构建前台前端路由所需要的菜单
+     * 
+     * @param menus 菜单列表
+     * @return 路由列表
+     */
+    public List<RouterVo> buildFrontMenus(List<SysMenu> menus);
 }

+ 34 - 1
gw-system/src/main/java/com/goldenwater/system/service/impl/SysMenuServiceImpl.java

@@ -181,7 +181,7 @@ public class SysMenuServiceImpl implements ISysMenuService
             router.setPath(getRouterPath(menu));
             router.setComponent(getComponent(menu));
             router.setQuery(menu.getQuery());
-            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
+            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath(), menu.getOrderNum()));
             List<SysMenu> cMenus = menu.getChildren();
             if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType()))
             {
@@ -221,6 +221,24 @@ public class SysMenuServiceImpl implements ISysMenuService
         return routers;
     }
 
+    @Override
+    public List<RouterVo> buildFrontMenus(List<SysMenu> menus)
+    {
+        List<RouterVo> routers = new LinkedList<RouterVo>();
+        for (SysMenu menu : menus)
+        {
+            RouterVo router = new RouterVo();
+            router.setHidden("1".equals(menu.getVisible()));
+            router.setName(getRouteName(menu));
+            router.setPath(menu.getPath());
+            router.setComponent(menu.getComponent());
+            router.setQuery(menu.getQuery());
+            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath(), menu.getOrderNum()));
+            routers.add(router);
+        }
+        return routers;
+    }
+
     /**
      * 构建前端所需要树结构
      * 
@@ -421,6 +439,21 @@ public class SysMenuServiceImpl implements ISysMenuService
         return UserConstants.UNIQUE;
     }
 
+    @Override
+    public List<SysMenu> selectFrontMenuTreeByUserId(Long userId)
+    {
+        List<SysMenu> menus = null;
+        if (SecurityUtils.isAdmin(userId))
+        {
+            menus = menuMapper.selectFrontMenuTreeAll();
+        }
+        else
+        {
+            menus = menuMapper.selectFrontMenuTreeByUserId(userId);
+        }
+        return getChildPerms(menus, MENU_ROOT_ID);
+    }
+
     /**
      * 获取路由名称
      * 

+ 2 - 2
gw-system/src/main/java/com/goldenwater/system/service/impl/SysUserOnlineServiceImpl.java

@@ -87,9 +87,9 @@ public class SysUserOnlineServiceImpl implements ISysUserOnlineService
         sysUserOnline.setBrowser(user.getBrowser());
         sysUserOnline.setOs(user.getOs());
         sysUserOnline.setLoginTime(user.getLoginTime());
-        if (StringUtils.isNotNull(user.getUser().getDept()))
+        if (StringUtils.isNotNull(user.getUser().getUnit()))
         {
-            sysUserOnline.setDeptName(user.getUser().getDept().getDeptName());
+            sysUserOnline.setDeptName(user.getUser().getUnit().getUnitName());
         }
         return sysUserOnline;
     }

+ 25 - 4
gw-system/src/main/resources/mapper/system/SysMenuMapper.xml

@@ -21,6 +21,7 @@
 		<result property="status"         column="status"         />
 		<result property="perms"          column="perms"          />
 		<result property="icon"           column="icon"           />
+		<result property="isFront"        column="is_front"       />
 		<result property="createBy"       column="create_by"      />
 		<result property="createTime"     column="create_time"    />
 		<result property="updateTime"     column="update_time"    />
@@ -29,7 +30,7 @@
 	</resultMap>
 
 	<sql id="selectMenuVo">
-        select menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
+        select menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, nvl(perms,'') as perms, icon, create_time
 		from sys_menu
     </sql>
     
@@ -50,13 +51,13 @@
 	</select>
 	
 	<select id="selectMenuTreeAll" resultMap="SysMenuResult">
-		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, nvl(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
 		from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
 		order by m.parent_id, m.order_num
 	</select>
 	
 	<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
-		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, nvl(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
 		from sys_menu m
 		left join sys_role_menu rm on m.menu_id = rm.menu_id
 		left join sys_user_role ur on rm.role_id = ur.role_id
@@ -75,7 +76,7 @@
 	</select>
     
     <select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
-		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, nvl(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
 		from sys_menu m
 			 left join sys_role_menu rm on m.menu_id = rm.menu_id
 			 left join sys_user_role ur on rm.role_id = ur.role_id
@@ -155,6 +156,7 @@
 			<if test="status != null">status = #{status},</if>
 			<if test="perms !=null">perms = #{perms},</if>
 			<if test="icon !=null and icon != ''">icon = #{icon},</if>
+			<if test="isFront != null and isFront != ''">is_front = #{isFront},</if>
 			<if test="remark != null and remark != ''">remark = #{remark},</if>
 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
 			update_time = sysdate()
@@ -179,6 +181,7 @@
 		<if test="status != null">status,</if>
 		<if test="perms !=null and perms != ''">perms,</if>
 		<if test="icon != null and icon != ''">icon,</if>
+		<if test="isFront != null and isFront != ''">is_front,</if>
 		<if test="remark != null and remark != ''">remark,</if>
 		<if test="createBy != null and createBy != ''">create_by,</if>
 		create_time
@@ -198,6 +201,7 @@
 		<if test="status != null">#{status},</if>
 		<if test="perms !=null and perms != ''">#{perms},</if>
 		<if test="icon != null and icon != ''">#{icon},</if>
+		<if test="isFront != null and isFront != ''">#{isFront},</if>
 		<if test="remark != null and remark != ''">#{remark},</if>
 		<if test="createBy != null and createBy != ''">#{createBy},</if>
 		sysdate()
@@ -212,4 +216,21 @@
 	    delete from sys_menu where menu_id = #{menuId}
 	</delete>
 
+	<select id="selectFrontMenuTreeAll" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, nvl(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time, m.is_front
+		from sys_menu m where m.menu_type in ('M', 'C') and m.status = '0' and m.is_front = '1'
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectFrontMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, nvl(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time, m.is_front
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+			 left join sys_role ro on ur.role_id = ro.role_id
+			 left join sys_user u on ur.user_id = u.user_id
+		where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = '0' and m.is_front = '1' AND ro.status = '0'
+		order by m.parent_id, m.order_num
+	</select>
+
 </mapper> 

+ 1 - 0
gw-ui/package.json

@@ -33,6 +33,7 @@
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
     "nprogress": "0.2.0",
+    "ol": "^10.9.0",
     "pinia": "3.0.4",
     "vue": "3.5.26",
     "vue-cropper": "1.1.1",

+ 14 - 1
gw-ui/src/api/menu.js

@@ -1,9 +1,22 @@
 import request from '@/utils/request'
 
-// 获取路由
 export const getRouters = () => {
   return request({
     url: '/getRouters',
     method: 'get'
   })
+}
+
+export const getFrontRouters = () => {
+  return request({
+    url: '/system/front/getRouters',
+    method: 'get'
+  })
+}
+
+export const getFrontMenuList = () => {
+  return request({
+    url: '/system/front/menuList',
+    method: 'get'
+  })
 }

BIN
gw-ui/src/assets/images/pay.png


BIN
gw-ui/src/assets/images/tba-gxpt-logo-hei.png


BIN
gw-ui/src/assets/images/tba-gxpt-logo.png


+ 234 - 0
gw-ui/src/layout/FrontLayout.vue

@@ -0,0 +1,234 @@
+<template>
+  <div class="front-layout">
+    <header class="front-header">
+      <div class="header-left">
+        <img src="@/assets/images/tba-gxpt-logo.png" alt="logo" class="logo" />
+      </div>
+      <nav class="front-nav">
+        <el-menu :default-active="activeMenu" mode="horizontal" background-color="transparent" text-color="#fff" active-text-color="#ffd700">
+          <el-menu-item 
+            v-for="menu in frontMenus" 
+            :key="menu.key" 
+            :index="menu.key" 
+            @click="navigate(menu.key)"
+          >
+            {{ menu.title }}
+          </el-menu-item>
+        </el-menu>
+      </nav>
+      <div class="header-right">
+        <el-button type="primary" @click="goAdmin">后台管理</el-button>
+        <el-dropdown @command="handleCommand" class="avatar-container" trigger="hover">
+          <div class="avatar-wrapper">
+            <svg-icon icon-class="user" class="user-icon" />
+            <span class="user-name">{{ userStore.nickName || userStore.name }}</span>
+            <el-icon class="dropdown-icon">
+              <ArrowDown />
+            </el-icon>
+          </div>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <router-link to="/user/profile">
+                <el-dropdown-item>个人中心</el-dropdown-item>
+              </router-link>
+              <el-dropdown-item divided command="logout">退出登录</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+    </header>
+
+    <main class="front-content">
+      <router-view />
+    </main>
+
+    <footer class="front-footer">
+      <p>主办单位:太湖流域管理局水文局(信息中心) | 技术支持:北京金水信息技术发展有限公司</p>
+      <p>沪ICP备2022022055号-1</p>
+    </footer>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, watch } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { ArrowDown } from '@element-plus/icons-vue'
+import { ElMessageBox } from 'element-plus'
+import useUserStore from '@/store/modules/user'
+import usePermissionStore from '@/store/modules/permission'
+
+const route = useRoute()
+const router = useRouter()
+const userStore = useUserStore()
+const permissionStore = usePermissionStore()
+
+const activeMenu = ref('/index')
+
+const frontMenus = computed(() => {
+  const routes = permissionStore.frontRouters || []
+  const menuList = []
+  routes.forEach(item => {
+    const orderNum = item.meta?.orderNum || 0
+    if (item.children && item.children.length > 0) {
+      item.children.forEach(child => {
+        if (child.meta?.title && !child.hidden) {
+          menuList.push({
+            key: item.path,
+            title: child.meta.title,
+            orderNum: child.meta?.orderNum || orderNum
+          })
+        }
+      })
+    } else if (item.meta?.title && !item.hidden) {
+      menuList.push({
+        key: item.path,
+        title: item.meta.title,
+        orderNum: orderNum
+      })
+    }
+  })
+  return menuList.sort((a, b) => {
+    return (a.orderNum || 0) - (b.orderNum || 0)
+  })
+})
+
+onMounted(() => {
+  activeMenu.value = route.path
+})
+
+watch(() => route.path, (newPath) => {
+  activeMenu.value = newPath
+})
+
+function navigate(path) {
+  activeMenu.value = path
+  router.push(path)
+}
+
+function goAdmin() {
+  router.push('/admin/index')
+}
+
+function handleCommand(command) {
+  switch (command) {
+    case 'logout':
+      ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(() => {
+        userStore.logOut().then(() => {
+          location.href = '/login'
+        })
+      }).catch(() => {})
+      break
+    default:
+      break
+  }
+}
+</script>
+
+<style scoped>
+.front-layout {
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+}
+
+.front-header {
+  height: 70px;
+  background: linear-gradient(135deg, #0066cc 0%, #0099ff 100%);
+  display: flex;
+  align-items: center;
+  padding: 0 30px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.header-left {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  flex-shrink: 0;
+}
+
+.logo {
+  height: 50px;
+  width: auto;
+}
+
+.title {
+  font-size: 20px;
+  font-weight: bold;
+  color: #fff;
+  letter-spacing: 2px;
+}
+
+.front-nav {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  margin-left: 20px;
+  overflow-x: auto;
+}
+
+.front-nav :deep(.el-menu) {
+  border-bottom: none;
+  background-color: transparent;
+  width: 100%;
+}
+
+.front-nav :deep(.el-menu-item) {
+  height: 60px;
+  line-height: 60px;
+  font-size: 15px;
+}
+
+.front-nav :deep(.el-menu-item:hover) {
+  background-color: rgba(255, 255, 255, 0.1);
+}
+
+.front-nav :deep(.el-menu-item.is-active) {
+  background-color: rgba(255, 255, 255, 0.2);
+}
+
+.header-right {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  flex-shrink: 0;
+}
+
+.user-icon {
+  width: 20px;
+  height: 20px;
+  color: #fff;
+}
+
+.user-name {
+  color: #fff;
+  font-size: 14px;
+}
+
+.dropdown-icon {
+  color: #fff;
+  font-size: 14px;
+}
+
+.front-content {
+  flex: 1;
+  background: #f5f7fa;
+  padding: 20px;
+}
+
+.front-footer {
+  background: #2c3e50;
+  color: #fff;
+  text-align: center;
+  padding: 15px 0;
+  font-size: 13px;
+}
+
+.front-footer p {
+  margin: 5px 0;
+}
+</style>

+ 17 - 25
gw-ui/src/layout/components/TopNavbar/index.vue

@@ -2,11 +2,10 @@
 <template>
   <div class="top-navbar">
     <div class="logo-area">
-      <img src="@/assets/images/tba-gxpt-logo.png" alt="dark" />
+      <img src="@/assets/images/tba-gxpt-logo.png" alt="logo" />
     </div>
 
     <div class="menu-area">
-      <!-- 首页菜单 -->
       <div
         :class="[
           'menu-link',
@@ -17,7 +16,6 @@
         首页
       </div>
 
-      <!-- 动态菜单 -->
       <div
         v-for="item in topMenus"
         :key="item.path"
@@ -26,25 +24,22 @@
       >
         {{ item.meta?.title }}
       </div>
-    </div>
 
-    <div class="right-menu">
+      <div class="menu-spacer"></div>
+
       <el-dropdown
         @command="handleCommand"
         class="avatar-container"
         trigger="hover"
       >
         <div class="avatar-wrapper">
-          <!-- 用户图标 -->
           <svg-icon icon-class="user" class="user-icon" />
-          <!-- 用户名 - 添加溢出处理 -->
           <span
             class="user-nickname"
             :title="userStore.nickName || userStore.name"
           >
             {{ userStore.nickName || userStore.name }}
           </span>
-          <!-- 下拉箭头 -->
           <el-icon class="dropdown-icon">
             <ArrowDown />
           </el-icon>
@@ -162,37 +157,41 @@ export default {
   background: #217ff4;
   display: flex;
   align-items: center;
-  justify-content: space-between;
-  padding: 0 30px;
+  padding: 0 10px 0 30px;
   box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
 }
 
 .logo-area {
-  width: 260px;
   flex-shrink: 0;
+  display: flex;
+  align-items: center;
 }
 
-.logo-text {
-  color: #fff;
-  font-size: 20px;
-  font-weight: bold;
+.logo-area img {
+  height: 45px;
+  width: auto;
 }
 
 .menu-area {
   flex: 1;
   display: flex;
+  align-items: center;
   gap: 8px;
-  margin: 0 30px;
+  margin-left: 30px;
   overflow-x: auto;
 }
 
+.menu-spacer {
+  flex: 1;
+}
+
 .menu-link {
   padding: 0 24px;
   height: 60px;
   line-height: 60px;
   color: #fff;
   cursor: pointer;
-  font-size: 18px;
+  font-size: 16px;
   font-weight: 500;
   letter-spacing: 1px;
   white-space: nowrap;
@@ -207,17 +206,10 @@ export default {
   background: rgba(255, 255, 255, 0.25);
 }
 
-.right-menu {
-  display: flex;
-  align-items: center;
-  color: #fff;
-  flex-shrink: 0;
-  height: 100%;
-}
-
 .avatar-container {
   margin-right: 0;
   padding-right: 0;
+  flex-shrink: 0;
 }
 
 .avatar-wrapper {

+ 6 - 4
gw-ui/src/permission.js

@@ -41,14 +41,16 @@ router.beforeEach((to, from, next) => {
         // 判断当前用户是否已拉取完user_info信息
         useUserStore().getInfo().then(() => {
           isRelogin.show = false
-          usePermissionStore().generateRoutes().then(accessRoutes => {
-            // 根据roles权限生成可访问的路由表
+          Promise.all([
+            usePermissionStore().generateRoutes(),
+            usePermissionStore().generateFrontRoutes()
+          ]).then(([accessRoutes]) => {
             accessRoutes.forEach(route => {
               if (!isHttp(route.path)) {
-                router.addRoute(route) // 动态添加可访问路由表
+                router.addRoute(route)
               }
             })
-            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
+            next({ ...to, replace: true })
           })
         }).catch(err => {
           useUserStore().logOut().then(() => {

+ 18 - 29
gw-ui/src/router/index.js

@@ -1,30 +1,7 @@
 import { createWebHistory, createRouter } from 'vue-router'
-/* Layout */
 import Layout from '@/layout'
+import FrontLayout from '@/layout/FrontLayout'
 
-/**
- * Note: 路由配置项
- *
- * hidden: true                     // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
- * alwaysShow: true                 // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
- *                                  // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
- *                                  // 若你想不管路由下面的 children 声明的个数都显示你的根路由
- *                                  // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
- * redirect: noRedirect             // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
- * name:'router-name'               // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
- * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
- * roles: ['admin', 'common']       // 访问路由的角色权限
- * permissions: ['a:a:a', 'b:b:b']  // 访问路由的菜单权限
- * meta : {
-    noCache: true                   // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
-    title: 'title'                  // 设置该路由在侧边栏和面包屑中展示的名字
-    icon: 'svg-name'                // 设置该路由的图标,对应路径src/assets/icons/svg
-    breadcrumb: false               // 如果设置为false,则不会在breadcrumb面包屑中显示
-    activeMenu: '/system/user'      // 当路由设置了该属性,则会高亮相对应的侧边栏。
-  }
- */
-
-// 公共路由
 export const constantRoutes = [
   {
     path: '/redirect',
@@ -59,17 +36,30 @@ export const constantRoutes = [
   },
   {
     path: '',
-    component: Layout,
+    component: FrontLayout,
     redirect: '/index',
     children: [
       {
         path: '/index',
-        component: () => import('@/views/index'),
-        name: 'Index',
+        component: () => import('@/views/front/Home'),
+        name: 'FrontIndex',
         meta: { title: '首页', icon: 'dashboard', affix: true }
       }
     ]
   },
+  {
+    path: '/admin',
+    component: Layout,
+    redirect: '/admin/index',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/index'),
+        name: 'AdminIndex',
+        meta: { title: '后台管理首页', icon: 'dashboard', affix: true }
+      }
+    ]
+  },
   {
     path: '/lock',
     component: () => import('@/views/lock'),
@@ -78,7 +68,7 @@ export const constantRoutes = [
   },
   {
     path: '/user',
-    component: Layout,
+    component: FrontLayout,
     hidden: true,
     redirect: 'noredirect',
     children: [
@@ -92,7 +82,6 @@ export const constantRoutes = [
   }
 ]
 
-// 动态路由,基于用户权限动态去加载
 export const dynamicRoutes = [
   {
     path: '/system/user-auth',

+ 57 - 4
gw-ui/src/store/modules/permission.js

@@ -1,7 +1,8 @@
 import auth from '@/plugins/auth'
 import router, { constantRoutes, dynamicRoutes } from '@/router'
-import { getRouters } from '@/api/menu'
+import { getRouters, getFrontRouters } from '@/api/menu'
 import Layout from '@/layout/index'
+import FrontLayout from '@/layout/FrontLayout'
 import ParentView from '@/components/ParentView'
 import InnerLink from '@/layout/components/InnerLink'
 
@@ -16,7 +17,8 @@ const usePermissionStore = defineStore(
       addRoutes: [],
       defaultRoutes: [],
       topbarRouters: [],
-      sidebarRouters: []
+      sidebarRouters: [],
+      frontRouters: []
     }),
     actions: {
       setRoutes(routes) {
@@ -32,9 +34,11 @@ const usePermissionStore = defineStore(
       setSidebarRouters(routes) {
         this.sidebarRouters = routes
       },
+      setFrontRouters(routes) {
+        this.frontRouters = routes
+      },
       generateRoutes(roles) {
         return new Promise(resolve => {
-          // 向后端请求路由数据
           getRouters().then(res => {
             const sdata = JSON.parse(JSON.stringify(res.data))
             const rdata = JSON.parse(JSON.stringify(res.data))
@@ -46,11 +50,22 @@ const usePermissionStore = defineStore(
             asyncRoutes.forEach(route => { router.addRoute(route) })
             this.setRoutes(rewriteRoutes)
             this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
+            this.setTopbarRoutes(sidebarRoutes)
             this.setDefaultRoutes(sidebarRoutes)
-            this.setTopbarRoutes(defaultRoutes)
             resolve(rewriteRoutes)
           })
         })
+      },
+      generateFrontRoutes() {
+        return new Promise(resolve => {
+          getFrontRouters().then(res => {
+            const data = JSON.parse(JSON.stringify(res.data))
+            const frontRoutes = filterFrontRouter(data)
+            frontRoutes.forEach(route => { router.addRoute(route) })
+            this.setFrontRouters(frontRoutes)
+            resolve(frontRoutes)
+          })
+        })
       }
     }
   })
@@ -124,4 +139,42 @@ export const loadView = (view) => {
   return res
 }
 
+function filterFrontRouter(asyncRouterMap) {
+  return asyncRouterMap.map(route => {
+    if (route.component) {
+      if (route.component === 'FrontLayout') {
+        route.component = FrontLayout
+      } else if (route.component === 'Layout') {
+        route.component = FrontLayout
+      } else if (route.component === 'ParentView') {
+        route.component = ParentView
+      } else if (route.component === 'InnerLink') {
+        route.component = InnerLink
+      } else {
+        route.component = loadView(route.component)
+      }
+    }
+    if (route.children != null && route.children && route.children.length) {
+      route.children = filterFrontRouter(route.children)
+    } else {
+      delete route['children']
+      delete route['redirect']
+    }
+    if (route.component && route.component !== FrontLayout) {
+      return {
+        path: route.path,
+        component: FrontLayout,
+        meta: route.meta,
+        children: [{
+          path: '',
+          component: route.component,
+          meta: route.meta,
+          name: route.name
+        }]
+      }
+    }
+    return route
+  })
+}
+
 export default usePermissionStore

+ 78 - 0
gw-ui/src/views/front/DataCommon.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>数据查看</h1>
+      <p>通用数据表格查询,支持单位切换、业务类型切换、站名搜索</p>
+    </div>
+    <div class="page-content">
+      <div class="data-placeholder">
+        <svg-icon icon-class="table" class="data-icon" />
+        <p>数据表格组件</p>
+        <p class="hint">单位选择 + 业务切换 + 站名搜索 + 动态表格列配置</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.data-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.data-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.data-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.data-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/DataStatistics.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>数据统计</h1>
+      <p>GIS地图 + ECharts水情/水质/工情统计图表</p>
+    </div>
+    <div class="page-content">
+      <div class="stats-placeholder">
+        <svg-icon icon-class="chart" class="stats-icon" />
+        <p>数据统计组件</p>
+        <p class="hint">水情统计、水质统计、工情统计图表</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.stats-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.stats-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.stats-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.stats-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/Document.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>河湖长制</h1>
+      <p>文档管理:目录树 + 文件搜索 + 下载</p>
+    </div>
+    <div class="page-content">
+      <div class="document-placeholder">
+        <svg-icon icon-class="documentation" class="document-icon" />
+        <p>河湖长制文档管理</p>
+        <p class="hint">目录树浏览、文件搜索、文档下载</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.document-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.document-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.document-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.document-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 690 - 0
gw-ui/src/views/front/Gis.vue

@@ -0,0 +1,690 @@
+<template>
+  <div class="gis-page">
+    <div class="gis-header">
+      <div class="header-left">
+        <el-select v-model="selectedUnit" placeholder="请选择单位" @change="handleUnitChange" class="unit-select">
+          <el-option v-for="unit in unitList" :key="unit.unitCode" :label="unit.unitName" :value="unit.unitCode" />
+        </el-select>
+      </div>
+      <div class="header-center">
+        <div class="biz-buttons">
+          <el-button 
+            v-for="biz in bizList" 
+            :key="biz.bizCode"
+            :class="{ active: selectedBiz === biz.bizCode }"
+            @click="handleBizClick(biz.bizCode)"
+            size="default"
+          >
+            {{ biz.bizName }}
+          </el-button>
+        </div>
+      </div>
+      <div class="header-right">
+        <span class="station-count" v-if="selectedBiz">共 {{ stationList.length }} 个站点</span>
+      </div>
+    </div>
+    
+    <div class="gis-content">
+      <div ref="mapContainer" class="map-container"></div>
+      
+      <div class="sidebar">
+        <div class="sidebar-header">
+          <span class="sidebar-title">站点列表</span>
+          <div class="search-box">
+            <el-input v-model="searchKeyword" placeholder="请输入站名" @keyup.enter="searchStation" clearable>
+              <template #prefix>
+                <el-icon><Search /></el-icon>
+              </template>
+            </el-input>
+          </div>
+        </div>
+        
+        <div class="station-list">
+          <div 
+            v-for="station in filteredStations" 
+            :key="station.id" 
+            class="station-item"
+            :class="{ active: selectedStation?.id === station.id }"
+            @click="selectStation(station)"
+          >
+            <div class="station-icon">
+              <el-icon class="icon-map-marker">
+                <Location />
+              </el-icon>
+            </div>
+            <div class="station-info">
+              <div class="station-name">{{ station.stnm }}</div>
+              <div class="station-extra">{{ station.stcd }} · {{ station.rvnm || station.hnnm }}</div>
+            </div>
+          </div>
+          
+          <div v-if="filteredStations.length === 0" class="empty-tip">
+            <el-icon class="empty-icon"><MapLocation /></el-icon>
+            <p>暂无站点数据</p>
+          </div>
+        </div>
+        
+        <div class="sidebar-footer">
+          <span>共 {{ filteredStations.length }} 个站点</span>
+        </div>
+      </div>
+    </div>
+    
+    <el-dialog 
+      v-model="dialogVisible" 
+      :title="selectedStation?.stnm + ' - 监测数据'" 
+      width="600px"
+      @close="closeDialog"
+    >
+      <div v-if="selectedStation" class="station-detail">
+        <div class="detail-section">
+          <div class="section-title">站点信息</div>
+          <div class="info-grid">
+            <div class="info-item">
+              <span class="label">站码:</span>
+              <span class="value">{{ selectedStation.stcd }}</span>
+            </div>
+            <div class="info-item">
+              <span class="label">站名:</span>
+              <span class="value">{{ selectedStation.stnm }}</span>
+            </div>
+            <div class="info-item">
+              <span class="label">站址:</span>
+              <span class="value">{{ selectedStation.stlc || '-' }}</span>
+            </div>
+            <div class="info-item">
+              <span class="label">经度:</span>
+              <span class="value">{{ selectedStation.lgtd }}</span>
+            </div>
+            <div class="info-item">
+              <span class="label">纬度:</span>
+              <span class="value">{{ selectedStation.lttd }}</span>
+            </div>
+            <div class="info-item">
+              <span class="label">所属业务:</span>
+              <span class="value">{{ selectedStation.bizName }}</span>
+            </div>
+          </div>
+        </div>
+        
+        <div class="detail-section">
+          <div class="section-title">监测数据</div>
+          <div v-if="qualityData.length > 0" class="quality-table">
+            <table>
+              <thead>
+                <tr>
+                  <th>分析日期</th>
+                  <th>评估类型</th>
+                  <th>评估值</th>
+                  <th>数据条数</th>
+                  <th>描述</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="item in qualityData" :key="item.id">
+                  <td>{{ formatDate(item.analysisDate) }}</td>
+                  <td>{{ item.type || '-' }}</td>
+                  <td>{{ item.value || '-' }}</td>
+                  <td>{{ item.cnt || 0 }}</td>
+                  <td>{{ item.description || '-' }}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+          <div v-else class="no-data">
+            <el-icon class="empty-icon"><DataAnalysis /></el-icon>
+            <p>暂无监测数据</p>
+          </div>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted, computed, watch, nextTick } from 'vue'
+import { Map as OlMap, View, Overlay } from 'ol'
+import { OSM, Vector as VectorSource } from 'ol/source'
+import TileLayer from 'ol/layer/Tile'
+import VectorLayer from 'ol/layer/Vector'
+import { fromLonLat, toLonLat } from 'ol/proj'
+import { Feature } from 'ol'
+import { Point } from 'ol/geom'
+import { Icon, Style } from 'ol/style'
+import { Search, MapLocation, Location, DataAnalysis } from '@element-plus/icons-vue'
+import request from '@/utils/request'
+import 'ol/ol.css'
+
+const mapContainer = ref(null)
+const selectedUnit = ref('')
+const selectedBiz = ref('')
+const searchKeyword = ref('')
+const dialogVisible = ref(false)
+const selectedStation = ref(null)
+const qualityData = ref([])
+
+const unitList = ref([])
+const bizList = ref([])
+const stationList = ref([])
+
+let olMap = null
+let markerLayer = null
+
+const filteredStations = computed(() => {
+  let stations = stationList.value
+  if (searchKeyword.value) {
+    const keyword = searchKeyword.value.toLowerCase()
+    stations = stations.filter(s => 
+      s.stnm.toLowerCase().includes(keyword) || 
+      s.stcd.toLowerCase().includes(keyword)
+    )
+  }
+  return stations
+})
+
+onMounted(() => {
+  loadUnits()
+  setTimeout(() => {
+    if (mapContainer.value) {
+      initMap()
+    } else {
+      console.error('Map container not found')
+    }
+  }, 100)
+})
+
+onUnmounted(() => {
+  if (olMap) {
+    olMap.dispose()
+  }
+})
+
+function loadUnits() {
+  request({
+    url: '/gx/unit/front/list',
+    method: 'get'
+  }).then(res => {
+    unitList.value = res.data || []
+  })
+}
+
+function handleUnitChange(val) {
+  selectedBiz.value = ''
+  stationList.value = []
+  clearMarkers()
+  
+  if (val) {
+    request({
+      url: '/gx/biz/front/list',
+      method: 'get',
+      params: { bizUnit: val }
+    }).then(res => {
+      bizList.value = res.data || []
+      if (bizList.value.length > 0) {
+        selectedBiz.value = bizList.value[0].bizCode
+        handleBizClick(bizList.value[0].bizCode)
+      }
+    })
+  } else {
+    bizList.value = []
+  }
+}
+
+function handleBizClick(val) {
+  selectedBiz.value = val
+  stationList.value = []
+  clearMarkers()
+  
+  if (val) {
+    request({
+      url: '/gx/station/front/listByBizCode/' + val,
+      method: 'get'
+    }).then(res => {
+      stationList.value = res.data || res.rows || []
+      addMarkers()
+      
+      if (stationList.value.length > 0) {
+        const firstStation = stationList.value[0]
+        if (firstStation.lgtd && firstStation.lttd) {
+          const coord = fromLonLat([Number(firstStation.lgtd), Number(firstStation.lttd)])
+          olMap.getView().animate({
+            center: coord,
+            zoom: 10,
+            duration: 1000
+          })
+        }
+      }
+    })
+  }
+}
+
+function searchStation() {
+}
+
+function selectStation(station) {
+  selectedStation.value = station
+  
+  if (station.lgtd && station.lttd) {
+    const coord = fromLonLat([Number(station.lgtd), Number(station.lttd)])
+    olMap.getView().animate({
+      center: coord,
+      zoom: 12,
+      duration: 800
+    })
+    
+    highlightMarker(station)
+  }
+  
+  loadQualityData(station.stcd)
+  dialogVisible.value = true
+}
+
+function loadQualityData(stcd) {
+  request({
+    url: '/gx/quality/front/list',
+    method: 'get',
+    params: { stcd: stcd }
+  }).then(res => {
+    qualityData.value = res.data || []
+  })
+}
+
+function closeDialog() {
+  dialogVisible.value = false
+  selectedStation.value = null
+  qualityData.value = []
+}
+
+function initMap() {
+  const tdtUrl = 'http://t0.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=5c5a468d333dbb827f134fb64818aa65'
+  
+  olMap = new OlMap({
+    target: mapContainer.value,
+    view: new View({
+      center: fromLonLat([120.5, 31.5]),
+      zoom: 9,
+      minZoom: 6,
+      maxZoom: 18
+    }),
+    layers: [
+      new TileLayer({
+        source: new OSM({
+          url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'
+        })
+      })
+    ]
+  })
+  
+  markerLayer = new VectorLayer({
+    source: new VectorSource()
+  })
+  olMap.addLayer(markerLayer)
+  
+  olMap.on('click', handleMapClick)
+}
+
+function addMarkers() {
+  const source = markerLayer.getSource()
+  source.clear()
+  
+  stationList.value.forEach(station => {
+    if (station.lgtd && station.lttd) {
+      const coord = fromLonLat([Number(station.lgtd), Number(station.lttd)])
+      const feature = new Feature({
+        geometry: new Point(coord),
+        station: station
+      })
+      
+      feature.setStyle(new Style({
+        image: new Icon({
+          src: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSI4IiBmaWxsPSIjMDA2NmNjIiBmaWxsLW9wYWNpdHk9IjAuOCIvPjxjaXJjbGUgY3g9IjEwIiBjeT0iMTAiIHI9IjQiIGZpbGw9IiNmZmQiIGZpbGwtb3BhY2l0eT0iMC45Ii8+PC9zdmc+',
+          anchor: [0.5, 1],
+          scale: 1.2
+        })
+      }))
+      
+      source.addFeature(feature)
+    }
+  })
+}
+
+function clearMarkers() {
+  if (markerLayer) {
+    markerLayer.getSource().clear()
+  }
+}
+
+function highlightMarker(station) {
+  const source = markerLayer.getSource()
+  const features = source.getFeatures()
+  
+  features.forEach(feature => {
+    const s = feature.get('station')
+    const isSelected = s && s.id === station.id
+    
+    feature.setStyle(new Style({
+      image: new Icon({
+        src: isSelected 
+          ? 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSI4IiBmaWxsPSIjZmY2MDAwIiBmaWxsLW9wYWNpdHk9IjAuOCIvPjxjaXJjbGUgY3g9IjEwIiBjeT0iMTAiIHI9IjQiIGZpbGw9IiNmZmQiIGZpbGwtb3BhY2l0eT0iMC45Ii8+PC9zdmc+'
+          : 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSI4IiBmaWxsPSIjMDA2NmNjIiBmaWxsLW9wYWNpdHk9IjAuOCIvPjxjaXJjbGUgY3g9IjEwIiBjeT0iMTAiIHI9IjQiIGZpbGw9IiNmZmQiIGZpbGwtb3BhY2l0eT0iMC45Ii8+PC9zdmc+',
+        anchor: [0.5, 1],
+        scale: isSelected ? 1.5 : 1.2
+      })
+    }))
+  })
+}
+
+function handleMapClick(event) {
+  const feature = olMap.forEachFeatureAtPixel(event.pixel, (feature) => feature)
+  
+  if (feature) {
+    const station = feature.get('station')
+    if (station) {
+      selectStation(station)
+    }
+  }
+}
+
+function formatDate(dateStr) {
+  if (!dateStr) return '-'
+  const date = new Date(dateStr)
+  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
+}
+</script>
+
+<style scoped>
+.gis-page {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  background: #f5f7fa;
+}
+
+.gis-header {
+  padding: 15px 20px;
+  background: #fff;
+  border-bottom: 1px solid #e4e7ed;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.header-left {
+  flex-shrink: 0;
+}
+
+.unit-select {
+  width: 220px;
+}
+
+.header-center {
+  flex: 1;
+  display: flex;
+  justify-content: center;
+  padding: 0 20px;
+}
+
+.biz-buttons {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+  justify-content: center;
+}
+
+.biz-buttons :deep(.el-button) {
+  padding: 6px 20px;
+  border-radius: 4px;
+  font-size: 14px;
+  background: #f5f7fa;
+  color: #606266;
+  border-color: #dcdfe6;
+  transition: all 0.3s ease;
+}
+
+.biz-buttons :deep(.el-button:hover) {
+  background: #ecf5ff;
+  border-color: #b3d8ff;
+  color: #1c97e7;
+}
+
+.biz-buttons :deep(.el-button.active) {
+  background: #1c97e7;
+  color: #fff;
+  border-color: #1c97e7;
+}
+
+.header-right {
+  flex-shrink: 0;
+}
+
+.station-count {
+  font-size: 14px;
+  color: #909399;
+  padding: 0 15px;
+}
+
+.gis-content {
+  flex: 1;
+  display: flex;
+  position: relative;
+  overflow: hidden;
+}
+
+.map-container {
+  flex: 1;
+  height: calc(100% - 70px);
+  position: relative;
+  min-height: 400px;
+}
+
+.sidebar {
+  width: 320px;
+  background: #fff;
+  border-left: 1px solid #e4e7ed;
+  display: flex;
+  flex-direction: column;
+  box-shadow: -2px 0 10px rgba(0, 0, 0, 0.05);
+}
+
+.sidebar-header {
+  padding: 15px;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.sidebar-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #303133;
+  display: block;
+  margin-bottom: 12px;
+}
+
+.search-box {
+  width: 100%;
+}
+
+.station-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: 10px;
+}
+
+.station-item {
+  display: flex;
+  align-items: center;
+  padding: 12px;
+  margin-bottom: 8px;
+  background: #fafafa;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  border: 2px solid transparent;
+}
+
+.station-item:hover {
+  background: #ecf5ff;
+  border-color: #b3d8ff;
+}
+
+.station-item.active {
+  background: #e6f7ff;
+  border-color: #1c97e7;
+}
+
+.station-icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background: #1c97e7;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 12px;
+  flex-shrink: 0;
+}
+
+.station-icon .icon-map-marker {
+  color: #fff;
+  font-size: 20px;
+}
+
+.station-info {
+  flex: 1;
+  overflow: hidden;
+}
+
+.station-name {
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 4px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.station-extra {
+  font-size: 12px;
+  color: #909399;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.empty-tip {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 60px 20px;
+  color: #909399;
+}
+
+.empty-icon {
+  font-size: 48px;
+  margin-bottom: 16px;
+  opacity: 0.5;
+}
+
+.empty-tip p {
+  margin: 0;
+  font-size: 14px;
+}
+
+.sidebar-footer {
+  padding: 12px 15px;
+  border-top: 1px solid #e4e7ed;
+  text-align: center;
+  font-size: 13px;
+  color: #909399;
+}
+
+.station-detail {
+  padding: 10px 0;
+}
+
+.detail-section {
+  margin-bottom: 20px;
+}
+
+.section-title {
+  font-size: 15px;
+  font-weight: bold;
+  color: #303133;
+  padding-bottom: 10px;
+  border-bottom: 2px solid #1c97e7;
+  margin-bottom: 15px;
+}
+
+.info-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 12px;
+}
+
+.info-item {
+  display: flex;
+  flex-direction: column;
+}
+
+.info-item .label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 4px;
+}
+
+.info-item .value {
+  font-size: 14px;
+  color: #303133;
+  font-weight: 500;
+}
+
+.quality-table {
+  max-height: 300px;
+  overflow-y: auto;
+}
+
+.quality-table table {
+  width: 100%;
+  border-collapse: collapse;
+}
+
+.quality-table th,
+.quality-table td {
+  padding: 10px;
+  text-align: left;
+  border-bottom: 1px solid #ebf0f5;
+  font-size: 13px;
+}
+
+.quality-table th {
+  background: #fafafa;
+  font-weight: 600;
+  color: #606266;
+  white-space: nowrap;
+}
+
+.quality-table td {
+  color: #303133;
+}
+
+.no-data {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 40px 20px;
+  color: #909399;
+}
+
+.no-data .empty-icon {
+  font-size: 40px;
+  margin-bottom: 12px;
+}
+
+.no-data p {
+  margin: 0;
+  font-size: 14px;
+}
+</style>

+ 471 - 0
gw-ui/src/views/front/Home.vue

@@ -0,0 +1,471 @@
+<template>
+  <div class="home-page">
+    <div class="page-header">
+      <h1>太湖流域水环境综合治理信息共享平台</h1>
+      <p class="subtitle">汇聚八家单位监测数据,实现多方数据共享与决策支持</p>
+    </div>
+
+    <div class="stats-row">
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="server" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">8</div>
+          <div class="stat-label">合作单位</div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="chart" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">40+</div>
+          <div class="stat-label">业务类型</div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="table" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">2000+</div>
+          <div class="stat-label">监测站点</div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="monitor" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">实时</div>
+          <div class="stat-label">数据更新</div>
+        </div>
+      </div>
+    </div>
+
+    <div class="section">
+      <h2>共享清单</h2>
+      <div class="unit-grid">
+        <div class="unit-card" v-for="unit in units" :key="unit.code">
+          <div class="unit-header">
+            <div class="unit-icon" :style="{ background: unit.color }">
+              <svg-icon :icon-class="unit.icon" />
+            </div>
+            <div class="unit-info">
+              <h3>{{ unit.name }}</h3>
+              <p class="unit-code">{{ unit.code }}</p>
+            </div>
+          </div>
+          <div class="unit-stats">
+            <div class="unit-stat">
+              <span class="stat-num">{{ unit.dataCount }}</span>
+              <span class="stat-text">共享数据集</span>
+            </div>
+            <div class="unit-stat">
+              <span class="stat-num">{{ unit.stationCount }}</span>
+              <span class="stat-text">监测站点</span>
+            </div>
+          </div>
+          <div class="unit-tags">
+            <el-tag v-for="tag in unit.tags" :key="tag" size="small" :type="getTagType(tag)">{{ tag }}</el-tag>
+          </div>
+          <el-button type="primary" size="small" @click="viewUnit(unit.code)">查看详情</el-button>
+        </div>
+      </div>
+    </div>
+
+    <div class="section">
+      <h2>实时数据概览</h2>
+      <div class="data-overview">
+        <div class="overview-card water-quality">
+          <h3>水质监测</h3>
+          <div class="overview-content">
+            <div class="indicator">
+              <span class="label">pH值</span>
+              <span class="value normal">7.2</span>
+            </div>
+            <div class="indicator">
+              <span class="label">溶解氧</span>
+              <span class="value normal">6.8 mg/L</span>
+            </div>
+            <div class="indicator">
+              <span class="label">高锰酸盐指数</span>
+              <span class="value normal">4.2 mg/L</span>
+            </div>
+            <div class="indicator">
+              <span class="label">氨氮</span>
+              <span class="value warning">0.55 mg/L</span>
+            </div>
+          </div>
+        </div>
+        <div class="overview-card water-level">
+          <h3>水情监测</h3>
+          <div class="overview-content">
+            <div class="indicator">
+              <span class="label">太湖水位</span>
+              <span class="value normal">3.12 m</span>
+            </div>
+            <div class="indicator">
+              <span class="label">洪泽湖水位</span>
+              <span class="value normal">12.35 m</span>
+            </div>
+            <div class="indicator">
+              <span class="label">长江流量</span>
+              <span class="value normal">23400 m³/s</span>
+            </div>
+            <div class="indicator">
+              <span class="label">入湖流量</span>
+              <span class="value normal">1250 m³/s</span>
+            </div>
+          </div>
+        </div>
+        <div class="overview-card ecology">
+          <h3>生态监测</h3>
+          <div class="overview-content">
+            <div class="indicator">
+              <span class="label">叶绿素a</span>
+              <span class="value warning">28 μg/L</span>
+            </div>
+            <div class="indicator">
+              <span class="label">蓝藻密度</span>
+              <span class="value danger">1.2×10⁶ cells/mL</span>
+            </div>
+            <div class="indicator">
+              <span class="label">透明度</span>
+              <span class="value normal">0.85 m</span>
+            </div>
+            <div class="indicator">
+              <span class="label">水色</span>
+              <span class="value normal">Ⅲ类</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const units = ref([
+  {
+    code: 'TBA',
+    name: '水利部太湖流域管理局',
+    icon: 'server',
+    color: '#217ff4',
+    dataCount: 12,
+    stationCount: 156,
+    tags: ['水文', '水质', '工情']
+  },
+  {
+    code: 'THDH',
+    name: '生态环境部太湖流域东海海域生态环境监督管理局',
+    icon: 'chart',
+    color: '#10b981',
+    dataCount: 8,
+    stationCount: 98,
+    tags: ['水质', '生态']
+  },
+  {
+    code: 'JS-SL',
+    name: '江苏省水利厅',
+    icon: 'table',
+    color: '#f59e0b',
+    dataCount: 15,
+    stationCount: 420,
+    tags: ['水文', '工情']
+  },
+  {
+    code: 'JS-ST',
+    name: '江苏省生态环境厅',
+    icon: 'monitor',
+    color: '#ef4444',
+    dataCount: 10,
+    stationCount: 280,
+    tags: ['水质', '生态']
+  },
+  {
+    code: 'ZJ-SL',
+    name: '浙江省水利厅',
+    icon: 'server',
+    color: '#8b5cf6',
+    dataCount: 11,
+    stationCount: 350,
+    tags: ['水文', '工情']
+  },
+  {
+    code: 'ZJ-ST',
+    name: '浙江省生态环境厅',
+    icon: 'chart',
+    color: '#ec4899',
+    dataCount: 9,
+    stationCount: 220,
+    tags: ['水质', '生态']
+  },
+  {
+    code: 'SH-SW',
+    name: '上海市水务局',
+    icon: 'table',
+    color: '#06b6d4',
+    dataCount: 6,
+    stationCount: 180,
+    tags: ['水文', '工情']
+  },
+  {
+    code: 'SH-ST',
+    name: '上海市生态环境局',
+    icon: 'monitor',
+    color: '#84cc16',
+    dataCount: 7,
+    stationCount: 150,
+    tags: ['水质', '生态']
+  }
+])
+
+function getTagType(tag) {
+  const types = {
+    '水文': 'primary',
+    '水质': 'success',
+    '工情': 'warning',
+    '生态': 'danger'
+  }
+  return types[tag] || 'info'
+}
+
+function viewUnit(code) {
+  console.log('查看单位:', code)
+}
+</script>
+
+<style scoped>
+.home-page {
+  max-width: 1400px;
+  margin: 0 auto;
+}
+
+.page-header {
+  text-align: center;
+  padding: 30px 0;
+  margin-bottom: 30px;
+  background: linear-gradient(135deg, #0066cc 0%, #0099ff 100%);
+  border-radius: 12px;
+  color: #fff;
+}
+
+.page-header h1 {
+  font-size: 32px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.subtitle {
+  font-size: 16px;
+  opacity: 0.9;
+  margin: 0;
+}
+
+.stats-row {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 20px;
+  margin-bottom: 30px;
+}
+
+.stat-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.stat-icon {
+  width: 60px;
+  height: 60px;
+  background: #e8f4ff;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.stat-icon :deep(svg) {
+  width: 30px;
+  height: 30px;
+  color: #0066cc;
+}
+
+.stat-content {
+  flex: 1;
+}
+
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+  color: #0066cc;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #666;
+}
+
+.section {
+  margin-bottom: 30px;
+}
+
+.section h2 {
+  font-size: 20px;
+  font-weight: bold;
+  margin: 0 0 20px 0;
+  padding-left: 10px;
+  border-left: 4px solid #0066cc;
+}
+
+.unit-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 20px;
+}
+
+.unit-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.unit-header {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-bottom: 15px;
+}
+
+.unit-icon {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.unit-icon :deep(svg) {
+  width: 25px;
+  height: 25px;
+  color: #fff;
+}
+
+.unit-info h3 {
+  font-size: 15px;
+  font-weight: bold;
+  margin: 0 0 5px 0;
+  color: #333;
+}
+
+.unit-code {
+  font-size: 12px;
+  color: #999;
+  margin: 0;
+}
+
+.unit-stats {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 15px;
+}
+
+.unit-stat {
+  text-align: center;
+}
+
+.stat-num {
+  display: block;
+  font-size: 20px;
+  font-weight: bold;
+  color: #0066cc;
+}
+
+.stat-text {
+  font-size: 12px;
+  color: #999;
+}
+
+.unit-tags {
+  margin-bottom: 15px;
+}
+
+.data-overview {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 20px;
+}
+
+.overview-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  border-top: 4px solid #0066cc;
+}
+
+.overview-card.water-quality {
+  border-top-color: #10b981;
+}
+
+.overview-card.water-level {
+  border-top-color: #3b82f6;
+}
+
+.overview-card.ecology {
+  border-top-color: #f59e0b;
+}
+
+.overview-card h3 {
+  font-size: 16px;
+  font-weight: bold;
+  margin: 0 0 15px 0;
+}
+
+.overview-content {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.indicator {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.indicator .label {
+  font-size: 14px;
+  color: #666;
+}
+
+.indicator .value {
+  font-size: 14px;
+  font-weight: bold;
+}
+
+.value.normal {
+  color: #10b981;
+}
+
+.value.warning {
+  color: #f59e0b;
+}
+
+.value.danger {
+  color: #ef4444;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/OneThreeLake.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>一河三湖</h1>
+      <p>太浦河、淀山湖、太湖、元荡湖四大水域专题分析</p>
+    </div>
+    <div class="page-content">
+      <div class="lake-placeholder">
+        <svg-icon icon-class="component" class="lake-icon" />
+        <p>一河三湖专题</p>
+        <p class="hint">GIS站点地图 + 水质分析 + 水情分析</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.lake-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.lake-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.lake-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.lake-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/Shl.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>水葫芦</h1>
+      <p>水葫芦监测专题(预留)</p>
+    </div>
+    <div class="page-content">
+      <div class="shl-placeholder">
+        <svg-icon icon-class="component" class="shl-icon" />
+        <p>水葫芦监测</p>
+        <p class="hint">水葫芦分布监测及防控信息</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.shl-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.shl-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.shl-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.shl-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/SystemManage.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>系统管理</h1>
+      <p>共享数据质量评估(雷达图)+ 帮助文档</p>
+    </div>
+    <div class="page-content">
+      <div class="system-placeholder">
+        <svg-icon icon-class="system" class="system-icon" />
+        <p>系统管理</p>
+        <p class="hint">共享质量雷达图、帮助文档、系统设置</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.system-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.system-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.system-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.system-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/Xaj.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>新安江流域</h1>
+      <p>GIS图层 + 数据查询 + 共享目录</p>
+    </div>
+    <div class="page-content">
+      <div class="xaj-placeholder">
+        <svg-icon icon-class="component" class="xaj-icon" />
+        <p>新安江流域专题</p>
+        <p class="hint">雨量站/蒸发站/径流站/水库 + 二级区划/县级区划面</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.xaj-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.xaj-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.xaj-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.xaj-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 78 - 0
gw-ui/src/views/front/Yjjt.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="page-container">
+    <div class="page-header">
+      <h1>引江济太</h1>
+      <p>引江济太工程专题(预留)</p>
+    </div>
+    <div class="page-content">
+      <div class="yjjt-placeholder">
+        <svg-icon icon-class="component" class="yjjt-icon" />
+        <p>引江济太专题</p>
+        <p class="hint">引江济太工程调度及水量信息</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+</script>
+
+<style scoped>
+.page-container {
+  height: 100%;
+}
+
+.page-header {
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 20px;
+}
+
+.page-header h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
+
+.page-header p {
+  font-size: 14px;
+  color: #666;
+  margin: 0;
+}
+
+.page-content {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 500px;
+}
+
+.yjjt-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 500px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.yjjt-icon {
+  width: 80px;
+  height: 80px;
+  color: #0066cc;
+  margin-bottom: 20px;
+}
+
+.yjjt-placeholder p {
+  font-size: 18px;
+  color: #333;
+  margin: 0 0 10px 0;
+}
+
+.yjjt-placeholder .hint {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 453 - 60
gw-ui/src/views/index.vue

@@ -1,78 +1,471 @@
 <template>
-  <div class="app-container home">
- 这是首页,版本号:{{ version }}
+  <div class="home-page">
+    <div class="page-header">
+      <h1>太湖流域水环境综合治理信息共享平台</h1>
+      <p class="subtitle">汇聚八家单位监测数据,实现多方数据共享与决策支持</p>
+    </div>
+
+    <div class="stats-row">
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="server" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">8</div>
+          <div class="stat-label">合作单位</div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="chart" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">40+</div>
+          <div class="stat-label">业务类型</div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="table" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">2000+</div>
+          <div class="stat-label">监测站点</div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-icon">
+          <svg-icon icon-class="monitor" />
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">实时</div>
+          <div class="stat-label">数据更新</div>
+        </div>
+      </div>
+    </div>
+
+    <div class="section">
+      <h2>共享清单</h2>
+      <div class="unit-grid">
+        <div class="unit-card" v-for="unit in units" :key="unit.code">
+          <div class="unit-header">
+            <div class="unit-icon" :style="{ background: unit.color }">
+              <svg-icon :icon-class="unit.icon" />
+            </div>
+            <div class="unit-info">
+              <h3>{{ unit.name }}</h3>
+              <p class="unit-code">{{ unit.code }}</p>
+            </div>
+          </div>
+          <div class="unit-stats">
+            <div class="unit-stat">
+              <span class="stat-num">{{ unit.dataCount }}</span>
+              <span class="stat-text">共享数据集</span>
+            </div>
+            <div class="unit-stat">
+              <span class="stat-num">{{ unit.stationCount }}</span>
+              <span class="stat-text">监测站点</span>
+            </div>
+          </div>
+          <div class="unit-tags">
+            <el-tag v-for="tag in unit.tags" :key="tag" size="small" :type="getTagType(tag)">{{ tag }}</el-tag>
+          </div>
+          <el-button type="primary" size="small" @click="viewUnit(unit.code)">查看详情</el-button>
+        </div>
+      </div>
+    </div>
+
+    <div class="section">
+      <h2>实时数据概览</h2>
+      <div class="data-overview">
+        <div class="overview-card water-quality">
+          <h3>水质监测</h3>
+          <div class="overview-content">
+            <div class="indicator">
+              <span class="label">pH值</span>
+              <span class="value normal">7.2</span>
+            </div>
+            <div class="indicator">
+              <span class="label">溶解氧</span>
+              <span class="value normal">6.8 mg/L</span>
+            </div>
+            <div class="indicator">
+              <span class="label">高锰酸盐指数</span>
+              <span class="value normal">4.2 mg/L</span>
+            </div>
+            <div class="indicator">
+              <span class="label">氨氮</span>
+              <span class="value warning">0.55 mg/L</span>
+            </div>
+          </div>
+        </div>
+        <div class="overview-card water-level">
+          <h3>水情监测</h3>
+          <div class="overview-content">
+            <div class="indicator">
+              <span class="label">太湖水位</span>
+              <span class="value normal">3.12 m</span>
+            </div>
+            <div class="indicator">
+              <span class="label">洪泽湖水位</span>
+              <span class="value normal">12.35 m</span>
+            </div>
+            <div class="indicator">
+              <span class="label">长江流量</span>
+              <span class="value normal">23400 m³/s</span>
+            </div>
+            <div class="indicator">
+              <span class="label">入湖流量</span>
+              <span class="value normal">1250 m³/s</span>
+            </div>
+          </div>
+        </div>
+        <div class="overview-card ecology">
+          <h3>生态监测</h3>
+          <div class="overview-content">
+            <div class="indicator">
+              <span class="label">叶绿素a</span>
+              <span class="value warning">28 μg/L</span>
+            </div>
+            <div class="indicator">
+              <span class="label">蓝藻密度</span>
+              <span class="value danger">1.2×10⁶ cells/mL</span>
+            </div>
+            <div class="indicator">
+              <span class="label">透明度</span>
+              <span class="value normal">0.85 m</span>
+            </div>
+            <div class="indicator">
+              <span class="label">水色</span>
+              <span class="value normal">Ⅲ类</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
-<script setup name="Index">
-const version = ref('3.9.2')
+<script setup>
+import { ref } from 'vue'
 
-function goTarget(url) {
-  window.open(url, '__blank')
+const units = ref([
+  {
+    code: 'TBA',
+    name: '水利部太湖流域管理局',
+    icon: 'server',
+    color: '#217ff4',
+    dataCount: 12,
+    stationCount: 156,
+    tags: ['水文', '水质', '工情']
+  },
+  {
+    code: 'THDH',
+    name: '生态环境部太湖流域东海海域生态环境监督管理局',
+    icon: 'chart',
+    color: '#10b981',
+    dataCount: 8,
+    stationCount: 98,
+    tags: ['水质', '生态']
+  },
+  {
+    code: 'JS-SL',
+    name: '江苏省水利厅',
+    icon: 'table',
+    color: '#f59e0b',
+    dataCount: 15,
+    stationCount: 420,
+    tags: ['水文', '工情']
+  },
+  {
+    code: 'JS-ST',
+    name: '江苏省生态环境厅',
+    icon: 'monitor',
+    color: '#ef4444',
+    dataCount: 10,
+    stationCount: 280,
+    tags: ['水质', '生态']
+  },
+  {
+    code: 'ZJ-SL',
+    name: '浙江省水利厅',
+    icon: 'server',
+    color: '#8b5cf6',
+    dataCount: 11,
+    stationCount: 350,
+    tags: ['水文', '工情']
+  },
+  {
+    code: 'ZJ-ST',
+    name: '浙江省生态环境厅',
+    icon: 'chart',
+    color: '#ec4899',
+    dataCount: 9,
+    stationCount: 220,
+    tags: ['水质', '生态']
+  },
+  {
+    code: 'SH-SW',
+    name: '上海市水务局',
+    icon: 'table',
+    color: '#06b6d4',
+    dataCount: 6,
+    stationCount: 180,
+    tags: ['水文', '工情']
+  },
+  {
+    code: 'SH-ST',
+    name: '上海市生态环境局',
+    icon: 'monitor',
+    color: '#84cc16',
+    dataCount: 7,
+    stationCount: 150,
+    tags: ['水质', '生态']
+  }
+])
+
+function getTagType(tag) {
+  const types = {
+    '水文': 'primary',
+    '水质': 'success',
+    '工情': 'warning',
+    '生态': 'danger'
+  }
+  return types[tag] || 'info'
+}
+
+function viewUnit(code) {
+  console.log('查看单位:', code)
 }
 </script>
 
-<style scoped lang="scss">
-.home {
-  blockquote {
-    padding: 10px 20px;
-    margin: 0 0 20px;
-    font-size: 17.5px;
-    border-left: 5px solid #eee;
-  }
-  hr {
-    margin-top: 20px;
-    margin-bottom: 20px;
-    border: 0;
-    border-top: 1px solid #eee;
-  }
-  .col-item {
-    margin-bottom: 20px;
-  }
+<style scoped>
+.home-page {
+  max-width: 1400px;
+  margin: 0 auto;
+}
 
-  ul {
-    padding: 0;
-    margin: 0;
-  }
+.page-header {
+  text-align: center;
+  padding: 30px 0;
+  margin-bottom: 30px;
+  background: linear-gradient(135deg, #0066cc 0%, #0099ff 100%);
+  border-radius: 12px;
+  color: #fff;
+}
 
-  font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
-  font-size: 13px;
-  color: #676a6c;
-  overflow-x: hidden;
+.page-header h1 {
+  font-size: 32px;
+  font-weight: bold;
+  margin: 0 0 10px 0;
+}
 
-  ul {
-    list-style-type: none;
-  }
+.subtitle {
+  font-size: 16px;
+  opacity: 0.9;
+  margin: 0;
+}
 
-  h4 {
-    margin-top: 0px;
-  }
+.stats-row {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 20px;
+  margin-bottom: 30px;
+}
 
-  h2 {
-    margin-top: 10px;
-    font-size: 26px;
-    font-weight: 100;
-  }
+.stat-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
 
-  p {
-    margin-top: 10px;
+.stat-icon {
+  width: 60px;
+  height: 60px;
+  background: #e8f4ff;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
 
-    b {
-      font-weight: 700;
-    }
-  }
+.stat-icon :deep(svg) {
+  width: 30px;
+  height: 30px;
+  color: #0066cc;
+}
 
-  .update-log {
-    ol {
-      display: block;
-      list-style-type: decimal;
-      margin-block-start: 1em;
-      margin-block-end: 1em;
-      margin-inline-start: 0;
-      margin-inline-end: 0;
-      padding-inline-start: 40px;
-    }
-  }
+.stat-content {
+  flex: 1;
+}
+
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+  color: #0066cc;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #666;
+}
+
+.section {
+  margin-bottom: 30px;
+}
+
+.section h2 {
+  font-size: 20px;
+  font-weight: bold;
+  margin: 0 0 20px 0;
+  padding-left: 10px;
+  border-left: 4px solid #0066cc;
+}
+
+.unit-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 20px;
+}
+
+.unit-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.unit-header {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-bottom: 15px;
+}
+
+.unit-icon {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
 }
-</style>
 
+.unit-icon :deep(svg) {
+  width: 25px;
+  height: 25px;
+  color: #fff;
+}
+
+.unit-info h3 {
+  font-size: 15px;
+  font-weight: bold;
+  margin: 0 0 5px 0;
+  color: #333;
+}
+
+.unit-code {
+  font-size: 12px;
+  color: #999;
+  margin: 0;
+}
+
+.unit-stats {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 15px;
+}
+
+.unit-stat {
+  text-align: center;
+}
+
+.stat-num {
+  display: block;
+  font-size: 20px;
+  font-weight: bold;
+  color: #0066cc;
+}
+
+.stat-text {
+  font-size: 12px;
+  color: #999;
+}
+
+.unit-tags {
+  margin-bottom: 15px;
+}
+
+.data-overview {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 20px;
+}
+
+.overview-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  border-top: 4px solid #0066cc;
+}
+
+.overview-card.water-quality {
+  border-top-color: #10b981;
+}
+
+.overview-card.water-level {
+  border-top-color: #3b82f6;
+}
+
+.overview-card.ecology {
+  border-top-color: #f59e0b;
+}
+
+.overview-card h3 {
+  font-size: 16px;
+  font-weight: bold;
+  margin: 0 0 15px 0;
+}
+
+.overview-content {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.indicator {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10px;
+  background: #f8fafc;
+  border-radius: 8px;
+}
+
+.indicator .label {
+  font-size: 14px;
+  color: #666;
+}
+
+.indicator .value {
+  font-size: 14px;
+  font-weight: bold;
+}
+
+.value.normal {
+  color: #10b981;
+}
+
+.value.warning {
+  color: #f59e0b;
+}
+
+.value.danger {
+  color: #ef4444;
+}
+</style>