Lin Qilong 1 周之前
父节点
当前提交
c6b2d07696

+ 3 - 3
gw-system/src/main/resources/mapper/system/SysLogininforMapper.xml

@@ -17,18 +17,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	</resultMap>
 
 	<insert id="insertLogininfor" parameterType="SysLogininfor">
-		insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time)
+		insert into sys_logininfor (user_name, "STATUS", ipaddr, login_location, browser, os, msg, login_time)
 		values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate())
 	</insert>
 	
 	<select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult">
-		select info_id, user_name, ipaddr, login_location, browser, os, status, msg, login_time from sys_logininfor
+		select info_id, user_name, ipaddr, login_location, browser, os, "STATUS", msg, login_time from sys_logininfor
 		<where>
 			<if test="ipaddr != null and ipaddr != ''">
 				AND ipaddr like concat('%', #{ipaddr}, '%')
 			</if>
 			<if test="status != null and status != ''">
-				AND status = #{status}
+				AND "STATUS" = #{status}
 			</if>
 			<if test="userName != null and userName != ''">
 				AND user_name like concat('%', #{userName}, '%')

+ 3 - 3
gw-system/src/main/resources/mapper/system/SysOperLogMapper.xml

@@ -25,12 +25,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	</resultMap>
 
 	<sql id="selectOperLogVo">
-        select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time
+        select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, "STATUS", error_msg, oper_time, cost_time
         from sys_oper_log
     </sql>
     
 	<insert id="insertOperlog" parameterType="SysOperLog">
-		insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time)
+		insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, "STATUS", error_msg, cost_time, oper_time)
         values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())
 	</insert>
 	
@@ -53,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		        </foreach> 
 			</if>
 			<if test="status != null">
-				AND status = #{status}
+				AND "STATUS" = #{status}
 			</if>
 			<if test="operName != null and operName != ''">
 				AND oper_name like concat('%', #{operName}, '%')

+ 1 - 1
gw-ui/.env.development

@@ -6,7 +6,7 @@ VITE_APP_ENV = 'development'
 #vue启动端口
 VITE_APP_PORT = 25022
 # 前端基础路径
-VITE_APP_BASE_TITLE = '/gw'
+VITE_APP_BASE_TITLE = '/hhgl'
 # 太湖流域河湖管理系统/开发环境
 VITE_SERVICE_BASE_TITLE = '/gw-api'
 

+ 172 - 18
gw-ui/src/frontLayout/systemHeader.vue

@@ -8,43 +8,197 @@
       <systemMenu @addSystemMenuView='menuItemClick'></systemMenu>
     </div>
     <div class="user-area">
-      <span class="user-name" @click="loginHzz">{{ name || '登录' }}</span>
+      <el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
+        <div class="avatar-wrapper">
+          <img :src="userStore.avatar" class="user-avatar"/>
+          <span class="user-nickname"> {{ userStore.nickName }} </span>
+        </div>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <router-link to="/user/profile">
+              <el-dropdown-item>个人中心</el-dropdown-item>
+            </router-link>
+            <router-link to="/hlgl/bankline">
+              <el-dropdown-item>后台管理</el-dropdown-item>
+            </router-link>
+            <el-dropdown-item divided command="logout">
+              <span>退出登录</span>
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref, onMounted } from 'vue'
-import { useRouter } from 'vue-router'
+import {onMounted, ref} from 'vue'
+import {useRouter} from 'vue-router'
 import Cookies from 'js-cookie'
 import systemMenu from './systemMenuBar.vue'
+import useUserStore from '@/store/modules/user'
+import {ElMessageBox} from "element-plus";
 
 const router = useRouter()
+const userStore = useUserStore()
 const name = ref('')
-const loginHzz = () => { if (Cookies.get('isLogin') !== 'true') router.push('/login') }
 const emit = defineEmits(['addSystemMenuView'])
-const menuItemClick = (menuItem) => { emit('addSystemMenuView', { viewItem: menuItem.viewItem }) }
-onMounted(() => { if (Cookies.get('isLogin') === 'true') name.value = Cookies.get('name') })
+const menuItemClick = (menuItem) => {
+  emit('addSystemMenuView', {viewItem: menuItem.viewItem})
+}
+
+function handleCommand(command) {
+  switch (command) {
+    case "logout":
+      logout()
+      break
+    default:
+      break
+  }
+}
+
+function logout() {
+  ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    userStore.logOut().then(() => {
+      location.href = '/index'
+    })
+  }).catch(() => {
+  })
+}
+
+onMounted(() => {
+  if (Cookies.get('isLogin') === 'true') name.value = Cookies.get('name')
+})
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 #systemHeader {
-  height: 56px; width: 100%;
+  height: 56px;
+  width: 100%;
   background: linear-gradient(135deg, #e8f4fd 0%, #d4ecfb 50%, #c5e5fa 100%);
-  display: flex; align-items: center; color: #1a3a5c;
-  box-shadow: 0 1px 6px rgba(0,0,0,.06); position: relative; z-index: 500;
+  display: flex;
+  align-items: center;
+  color: #1a3a5c;
+  box-shadow: 0 1px 6px rgba(0, 0, 0, .06);
+  position: relative;
+  z-index: 500;
   border-bottom: 2px solid #b8d8f0;
+
+  .right-menu-item {
+    display: inline-block;
+    padding: 0 8px;
+    height: 100%;
+    font-size: 18px;
+    color: #5a5e66;
+    vertical-align: text-bottom;
+
+    &.hover-effect {
+      cursor: pointer;
+      transition: background 0.3s;
+
+      &:hover {
+        background: rgba(0, 0, 0, 0.025);
+      }
+    }
+
+    &.theme-switch-wrapper {
+      display: flex;
+      align-items: center;
+
+      svg {
+        transition: transform 0.3s;
+
+        &:hover {
+          transform: scale(1.15);
+        }
+      }
+    }
+  }
+
+  .avatar-container {
+    margin-right: 0px;
+    padding-right: 0px;
+
+    .avatar-wrapper {
+      margin-top: 10px;
+      right: 8px;
+      position: relative;
+
+      .user-avatar {
+        cursor: pointer;
+        width: 30px;
+        height: 30px;
+        margin-right: 8px;
+        border-radius: 50%;
+      }
+
+      .user-nickname {
+        position: relative;
+        left: 0px;
+        bottom: 10px;
+        font-size: 14px;
+        font-weight: bold;
+      }
+
+      i {
+        cursor: pointer;
+        position: absolute;
+        right: -20px;
+        top: 25px;
+        font-size: 12px;
+      }
+    }
+  }
+
 }
+
 #systemInformation {
-  display: flex; align-items: center; gap: 10px; padding: 0 16px;
-  min-width: 340px; flex-shrink: 0; font-size: 17px; font-weight: 600; color: #0d4b80;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 0 16px;
+  min-width: 340px;
+  flex-shrink: 0;
+  font-size: 17px;
+  font-weight: 600;
+  color: #0d4b80;
+}
+
+.logoImg {
+  width: 36px;
+  height: 36px;
+  border-radius: 8px;
 }
-.logoImg { width: 36px; height: 36px; border-radius: 8px; }
-#systemMenuContainer { flex: 1; height: 100%; display: flex; align-items: center; }
-.user-area { flex-shrink: 0; padding: 0 20px; }
+
+#systemMenuContainer {
+  flex: 1;
+  height: 100%;
+  display: flex;
+  align-items: center;
+}
+
+.user-area {
+  flex-shrink: 0;
+  padding: 0 20px;
+}
+
 .user-name {
-  font-size: 13px; cursor: pointer; color: #4682b4;
-  padding: 6px 18px; border-radius: 20px; border: 1px solid #b0d0e8; background: #fff;
+  font-size: 13px;
+  cursor: pointer;
+  color: #4682b4;
+  padding: 6px 18px;
+  border-radius: 20px;
+  border: 1px solid #b0d0e8;
+  background: #fff;
+}
+
+.user-name:hover {
+  color: #0d4b80;
+  border-color: #6baed6;
+  background: #f0f8ff;
 }
-.user-name:hover { color: #0d4b80; border-color: #6baed6; background: #f0f8ff; }
 </style>

+ 7 - 2
gw-ui/src/frontLayout/systemMenuBar.vue

@@ -36,7 +36,7 @@ const keepShow = ref(false)
 const dropdownChildren = ref([])
 
 const menuInfos = ref([
-  { menuName: '首页', name: '首页', href: '/hlgl/onemap', path: '/hlgl/onemap', haveView: true, id: 'sy' },
+  { menuName: '首页', name: '首页', href: '/onemap', path: '/onemap', haveView: true, id: 'sy' },
   { menuName: '河湖长制', name: '河湖长制', haveView: true, id: 'hhzz',
     children: [{ menuName: '河湖长制', children: [
       { menuName: '工作制度', href: '/gzfa', path: '/gzfa', haveView: true },
@@ -67,7 +67,12 @@ const menuInfos = ref([
 const onHover = (item) => { if (item.children) { dropdownChildren.value = item.children; showDropdown.value = true } }
 const onLeave = () => { keepShow.value = false; setTimeout(() => { if (!keepShow.value) showDropdown.value = false }, 150) }
 const keepOpen = () => { keepShow.value = true }
-const onClick = (item) => { showDropdown.value = false; if (item.href) { router.push(item.href) } }
+const onClick = (item) => {
+  showDropdown.value = false
+  if (item.href) {
+    router.push(item.href)
+  }
+}
 </script>
 
 <style scoped>

+ 3 - 1
gw-ui/src/layout/components/TopNavbar/index.vue

@@ -2,7 +2,7 @@
 <template>
   <div class="top-navbar">
     <div class="logo-area">
-      <span class="logo-text">若依管理系统</span>
+      <span class="logo-text">{{ title }}</span>
     </div>
 
     <div class="menu-area">
@@ -90,6 +90,7 @@ export default {
   },
   emits: ["menu-click", "setLayout"],
   setup(props, { emit }) {
+    const title = import.meta.env.VITE_APP_TITLE
     const route = useRoute();
     const router = useRouter();
     const permissionStore = usePermissionStore();
@@ -145,6 +146,7 @@ export default {
     };
 
     return {
+      title,
       topMenus,
       userStore,
       settingsStore,

+ 183 - 162
gw-ui/src/router/index.js

@@ -1,4 +1,4 @@
-import { createWebHistory, createRouter } from 'vue-router'
+import {createRouter, createWebHistory} from 'vue-router'
 /* Layout */
 import Layout from '@/layout'
 import FrontLayout from '@/frontLayout'
@@ -17,178 +17,199 @@ import FrontLayout from '@/frontLayout'
  * 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'      // 当路由设置了该属性,则会高亮相对应的侧边栏。
-  }
+ 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',
-    component: Layout,
-    hidden: true,
-    children: [
-      {
-        path: '/redirect/:path(.*)',
-        component: () => import('@/views/redirect/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/login',
-    component: () => import('@/views/login'),
-    hidden: true
-  },
-  {
-    path: '/register',
-    component: () => import('@/views/register'),
-    hidden: true
-  },
-  {
-    path: "/:pathMatch(.*)*",
-    component: () => import('@/views/error/404'),
-    hidden: true
-  },
-  {
-    path: '/401',
-    component: () => import('@/views/error/401'),
-    hidden: true
-  },
-  {
-    path: '',
-    component: Layout,
-    redirect: '/hlgl/onemap',
-  },
-  // ==================== 一张图(FrontLayout) ====================
-  {
-    path: '/hlgl/onemap', component: FrontLayout, name: 'HzzOnemap',
-    meta: { title: '一张图', icon: 'map' }, permissions: ['hzz:onemap:list'],
-    children: [{ path: '', component: () => import('@/views/hlgl/onemap/index') }]
-  },
-  // ==================== 旧页面迁移(FrontLayout) ====================
-  {
-    path: '/gzfa', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/gzfa/index'), meta: { title: '工作制度' } }]
-  },
-  {
-    path: '/shjsxm', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/riverproject/index'), meta: { title: '涉河项目' } }]
-  },
-  {
-    path: '/syaxgl', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/syaxgl/index'), meta: { title: '水域岸线' } }]
-  },
-  {
-    path: '/khpg', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/assess/index'), meta: { title: '考核结果' } }]
-  },
-  {
-    path: '/khwj', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/khwj/index'), meta: { title: '相关文件' } }]
-  },
-  {
-    path: '/ddlb', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/wps/index'), meta: { title: '涉河项目督察' } }]
-  },
-  {
-    path: '/ddsh', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/supervision/index'), meta: { title: '河湖管理督察' } }]
-  },
-  {
-    path: '/sjml', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/eventclear/index'), meta: { title: '一湖两河清四乱' } }]
-  },
-  {
-    path: '/mcaf', component: FrontLayout, children: [{ path: '', component: () => import('@/views/hlgl/mcaf/index'), meta: { title: '明察暗访管理' } }]
-  },
-
-  {
-    path: '/lock',
-    component: () => import('@/views/lock'),
-    hidden: true,
-    meta: { title: '锁定屏幕' }
-  },
-  {
-    path: '/user',
-    component: Layout,
-    hidden: true,
-    redirect: 'noredirect',
-    children: [
-      {
-        path: 'profile/:activeTab?',
-        component: () => import('@/views/system/user/profile/index'),
-        name: 'Profile',
-        meta: { title: '个人中心', icon: 'user' }
-      }
-    ]
-  }
+    {
+        path: '/redirect',
+        component: Layout,
+        hidden: true,
+        children: [
+            {
+                path: '/redirect/:path(.*)',
+                component: () => import('@/views/redirect/index.vue')
+            }
+        ]
+    },
+    {
+        path: '/login',
+        component: () => import('@/views/login'),
+        hidden: true
+    },
+    {
+        path: '/register',
+        component: () => import('@/views/register'),
+        hidden: true
+    },
+    {
+        path: "/:pathMatch(.*)*",
+        component: () => import('@/views/error/404'),
+        hidden: true
+    },
+    {
+        path: '/401',
+        component: () => import('@/views/error/401'),
+        hidden: true
+    },
+    {
+        path: '',
+        redirect: '/onemap',
+        hidden: true
+    },
+    {
+        path: '/index',
+        redirect: '/onemap',
+        hidden: true
+    },
+    // ==================== 一张图(FrontLayout) ====================
+    {
+        path: '/onemap', component: FrontLayout, name: 'HzzOnemap',
+        meta: {title: '一张图', icon: 'map'}, permissions: ['hzz:onemap:list'],
+        children: [{path: '', component: () => import('@/views/hlgl/onemap/index')}]
+    },
+    // ==================== 旧页面迁移(FrontLayout) ====================
+    {
+        path: '/gzfa',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/gzfa/index'), meta: {title: '工作制度'}}]
+    },
+    {
+        path: '/shjsxm',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/riverproject/index'), meta: {title: '涉河项目'}}]
+    },
+    {
+        path: '/syaxgl',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/syaxgl/index'), meta: {title: '水域岸线'}}]
+    },
+    {
+        path: '/khpg',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/assess/index'), meta: {title: '考核结果'}}]
+    },
+    {
+        path: '/khwj',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/khwj/index'), meta: {title: '相关文件'}}]
+    },
+    {
+        path: '/ddlb',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/wps/index'), meta: {title: '涉河项目督察'}}]
+    },
+    {
+        path: '/ddsh',
+        component: FrontLayout,
+        children: [{path: '', component: () => import('@/views/hlgl/supervision/index'), meta: {title: '河湖管理督察'}}]
+    },
+    {
+        path: '/sjml',
+        component: FrontLayout,
+        children: [{
+            path: '',
+            component: () => import('@/views/hlgl/eventclear/index'),
+            meta: {title: '一湖两河清四乱'}
+        }]
+    },
+    {
+        path: '/lock',
+        component: () => import('@/views/lock'),
+        hidden: true,
+        meta: {title: '锁定屏幕'}
+    },
+    {
+        path: '/user',
+        component: Layout,
+        hidden: true,
+        redirect: 'noredirect',
+        children: [
+            {
+                path: 'profile/:activeTab?',
+                component: () => import('@/views/system/user/profile/index'),
+                name: 'Profile',
+                meta: {title: '个人中心', icon: 'user'}
+            }
+        ]
+    }
 ]
 
 // 动态路由,基于用户权限动态去加载
 export const dynamicRoutes = [
-  {
-    path: '/system/user-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:user:edit'],
-    children: [
-      {
-        path: 'role/:userId(\\d+)',
-        component: () => import('@/views/system/user/authRole'),
-        name: 'AuthRole',
-        meta: { title: '分配角色', activeMenu: '/system/user' }
-      }
-    ]
-  },
-  {
-    path: '/system/role-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:role:edit'],
-    children: [
-      {
-        path: 'user/:roleId(\\d+)',
-        component: () => import('@/views/system/role/authUser'),
-        name: 'AuthUser',
-        meta: { title: '分配用户', activeMenu: '/system/role' }
-      }
-    ]
-  },
-  {
-    path: '/system/dict-data',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:dict:list'],
-    children: [
-      {
-        path: 'index/:dictId(\\d+)',
-        component: () => import('@/views/system/dict/data'),
-        name: 'Data',
-        meta: { title: '字典数据', activeMenu: '/system/dict' }
-      }
-    ]
-  },
-  {
-    path: '/monitor/job-log',
-    component: Layout,
-    hidden: true,
-    permissions: ['monitor:job:list'],
-    children: [
-      {
-        path: 'index/:jobId(\\d+)',
-        component: () => import('@/views/monitor/job/log'),
-        name: 'JobLog',
-        meta: { title: '调度日志', activeMenu: '/monitor/job' }
-      }
-    ]
-  },
+    {
+        path: '/system/user-auth',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:user:edit'],
+        children: [
+            {
+                path: 'role/:userId(\\d+)',
+                component: () => import('@/views/system/user/authRole'),
+                name: 'AuthRole',
+                meta: {title: '分配角色', activeMenu: '/system/user'}
+            }
+        ]
+    },
+    {
+        path: '/system/role-auth',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:role:edit'],
+        children: [
+            {
+                path: 'user/:roleId(\\d+)',
+                component: () => import('@/views/system/role/authUser'),
+                name: 'AuthUser',
+                meta: {title: '分配用户', activeMenu: '/system/role'}
+            }
+        ]
+    },
+    {
+        path: '/system/dict-data',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:dict:list'],
+        children: [
+            {
+                path: 'index/:dictId(\\d+)',
+                component: () => import('@/views/system/dict/data'),
+                name: 'Data',
+                meta: {title: '字典数据', activeMenu: '/system/dict'}
+            }
+        ]
+    },
+    {
+        path: '/monitor/job-log',
+        component: Layout,
+        hidden: true,
+        permissions: ['monitor:job:list'],
+        children: [
+            {
+                path: 'index/:jobId(\\d+)',
+                component: () => import('@/views/monitor/job/log'),
+                name: 'JobLog',
+                meta: {title: '调度日志', activeMenu: '/monitor/job'}
+            }
+        ]
+    },
 ]
 
 const router = createRouter({
-  history: createWebHistory(import.meta.env.VITE_APP_BASE_TITLE),
-  routes: constantRoutes,
-  scrollBehavior(to, from, savedPosition) {
-    if (savedPosition) {
-      return savedPosition
-    }
-    return { top: 0 }
-  },
+    history: createWebHistory(import.meta.env.VITE_APP_BASE_TITLE),
+    routes: constantRoutes,
+    scrollBehavior(to, from, savedPosition) {
+        if (savedPosition) {
+            return savedPosition
+        }
+        return {top: 0}
+    },
 })
 
 export default router

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

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

+ 482 - 94
gw-ui/src/views/hlgl/bankline/index.vue

@@ -1,99 +1,283 @@
 <template>
-  <div class="app-container">
-    <el-tabs v-model="activeTab" type="border-card">
-      <!-- 工作方案 Tab -->
-      <el-tab-pane label="工作方案" name="workplan">
-        <el-table v-loading="wpLoading" :data="workPlanList" border>
-          <el-table-column label="方案编号" prop="wpCode" width="160" />
-          <el-table-column label="方案名称" prop="wpName" :show-overflow-tooltip="true" />
-          <el-table-column label="文号" prop="fileNum" width="140" />
-          <el-table-column label="发布日期" prop="rlsTm" width="120" />
-          <el-table-column label="发布单位" prop="releWiunName" width="180" />
-          <el-table-column label="附件" prop="attc" width="100">
-            <template #default="scope">
-              <el-link v-if="scope.row.attc" :href="scope.row.attc" target="_blank" type="primary">查看</el-link>
-              <span v-else>-</span>
-            </template>
-          </el-table-column>
-        </el-table>
-      </el-tab-pane>
-
-      <!-- 制度文件 Tab -->
-      <el-tab-pane label="制度文件" name="files">
-        <el-form :model="fileQuery" :inline="true">
-          <el-form-item label="文件名称">
-            <el-input v-model="fileQuery.fileName" placeholder="请输入文件名称" clearable style="width:200px" />
-          </el-form-item>
-          <el-form-item label="文件类型">
-            <el-input v-model="fileQuery.fileType" placeholder="请输入文件类型" clearable style="width:150px" />
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" icon="Search" @click="getFileList">搜索</el-button>
-            <el-button icon="Refresh" @click="resetFileQuery">重置</el-button>
-          </el-form-item>
-        </el-form>
-        <el-table v-loading="fileLoading" :data="fileList" border>
-          <el-table-column label="文件名称" prop="FILE_NAME" :show-overflow-tooltip="true" />
-          <el-table-column label="文件类型" prop="FILE_TYPE" width="120" />
-          <el-table-column label="文号" prop="WH" width="150" />
-          <el-table-column label="日期" prop="START_DATE" width="120" />
-          <el-table-column label="附件" width="100">
-            <template #default="scope">
-              <el-link v-if="scope.row.URL" :href="scope.row.URL" target="_blank" type="primary">下载</el-link>
-              <span v-else>-</span>
-            </template>
-          </el-table-column>
-        </el-table>
-        <pagination v-show="fileTotal > 0" :total="fileTotal" v-model:page="filePageNum" v-model:limit="filePageSize" @pagination="getFileList" />
-      </el-tab-pane>
-
-      <!-- 工作动态 Tab -->
-      <el-tab-pane label="工作动态" name="articles">
-        <el-form :model="articleQuery" :inline="true">
-          <el-form-item label="标题">
-            <el-input v-model="articleQuery.title" placeholder="请输入标题" clearable style="width:200px" />
-          </el-form-item>
-          <el-form-item label="类型">
-            <el-select v-model="articleQuery.type" placeholder="动态类型" clearable style="width:150px">
-              <el-option label="水利动态" value="0" />
-              <el-option label="水利要闻" value="1" />
-            </el-select>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" icon="Search" @click="getArticleList">搜索</el-button>
-            <el-button icon="Refresh" @click="resetArticleQuery">重置</el-button>
-          </el-form-item>
-        </el-form>
-        <el-table v-loading="articleLoading" :data="articleList" border>
-          <el-table-column label="标题" prop="TITLE" :show-overflow-tooltip="true" />
-          <el-table-column label="类型" prop="TYPE" width="100">
-            <template #default="scope">{{ scope.row.TYPE === '0' ? '水利动态' : '水利要闻' }}</template>
-          </el-table-column>
-          <el-table-column label="发布人" prop="PUBMAN" width="120" />
-          <el-table-column label="发布时间" prop="PUBTIME" width="160" />
-        </el-table>
-        <pagination v-show="articleTotal > 0" :total="articleTotal" v-model:page="articlePageNum" v-model:limit="articlePageSize" @pagination="getArticleList" />
-      </el-tab-pane>
-    </el-tabs>
+  <div class="bankline-page">
+    <!-- 左侧行政区划树 -->
+    <div class="left-tree">
+      <div class="tree-title">行政区划</div>
+      <el-input v-model="treeFilter" placeholder="搜索" size="small" clearable style="margin:8px"/>
+      <el-tree
+          :data="treeData" :props="treeProps" node-key="adCode"
+          :filter-node-method="filterNode" ref="treeRef"
+          highlight-current lazy :load="loadTreeNode"
+          @node-click="onNodeClick"
+      />
+    </div>
+
+    <!-- 右侧内容 -->
+    <div class="right-content" v-loading="loading">
+      <!-- 当前选中区域 -->
+      <div class="current-area" v-if="currentAdName">
+        当前区域:<strong>{{ currentAdName }}</strong>
+        <el-button type="primary" size="small" style="float:right" @click="handleAdd" v-if="!isNational">
+          <el-icon><Plus /></el-icon> 新增工作方案
+        </el-button>
+      </div>
+
+      <el-tabs v-model="activeTab" type="border-card">
+        <!-- 工作方案 Tab -->
+        <el-tab-pane label="工作方案" name="workplan">
+          <el-table :data="workPlans" border @row-dblclick="handleEdit">
+            <el-table-column label="方案名称" prop="wpName" show-overflow-tooltip/>
+            <el-table-column label="文号" prop="fileNum" width="200"/>
+            <el-table-column label="发布日期" prop="rlsTm" width="140"/>
+            <el-table-column label="发布单位" prop="releWiunName" width="180"/>
+            <el-table-column label="操作" width="150" fixed="right" v-if="!isNational">
+              <template #default="scope">
+                <el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
+                <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-empty v-if="!workPlans.length" description="暂无工作方案"/>
+        </el-tab-pane>
+
+        <!-- 制度文件 Tab -->
+        <el-tab-pane label="制度文件" name="files">
+          <el-form :model="fileQuery" :inline="true" style="margin-bottom:10px">
+            <el-form-item label="文件名称">
+              <el-input v-model="fileQuery.fileName" placeholder="请输入文件名称" clearable style="width:200px" />
+            </el-form-item>
+            <el-form-item label="文件类型">
+              <el-select v-model="fileQuery.fileType" placeholder="请选择" clearable style="width:150px">
+                <el-option label="中央文件" value="0" />
+                <el-option label="国务院文件" value="1" />
+                <el-option label="水利部文件" value="2" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="getFileList">搜索</el-button>
+              <el-button icon="Refresh" @click="resetFileQuery">重置</el-button>
+              <el-button type="success" icon="Plus" @click="handleAddFile">新增</el-button>
+            </el-form-item>
+          </el-form>
+          <el-table :data="fileList" border>
+            <el-table-column label="文件名称" prop="fileName" show-overflow-tooltip />
+            <el-table-column label="文件类型" prop="fileType" width="120">
+              <template #default="scope">
+                {{ fileTypeLabel(scope.row.fileType) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="文号" prop="wh" width="150" />
+            <el-table-column label="日期" prop="startDate" width="120" />
+            <el-table-column label="操作" width="180" fixed="right">
+              <template #default="scope">
+                <el-link v-if="scope.row.url" :href="scope.row.url" target="_blank" type="primary">下载</el-link>
+                <el-button link type="primary" size="small" @click="handleEditFile(scope.row)">编辑</el-button>
+                <el-button link type="danger" size="small" @click="handleDeleteFile(scope.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination v-show="fileTotal > 0" :total="fileTotal" v-model:page="filePageNum" v-model:limit="filePageSize" @pagination="getFileList" />
+        </el-tab-pane>
+
+        <!-- 工作动态 Tab -->
+        <el-tab-pane label="工作动态" name="articles">
+          <el-form :model="articleQuery" :inline="true" style="margin-bottom:10px">
+            <el-form-item label="标题">
+              <el-input v-model="articleQuery.title" placeholder="请输入标题" clearable style="width:200px" />
+            </el-form-item>
+            <el-form-item label="类型">
+              <el-select v-model="articleQuery.type" placeholder="动态类型" clearable style="width:150px">
+                <el-option label="水利动态" value="0" />
+                <el-option label="水利要闻" value="1" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="getArticleList">搜索</el-button>
+              <el-button icon="Refresh" @click="resetArticleQuery">重置</el-button>
+              <el-button type="success" icon="Plus" @click="handleAddArticle">新增</el-button>
+            </el-form-item>
+          </el-form>
+          <el-table :data="articleList" border>
+            <el-table-column label="标题" prop="title" show-overflow-tooltip />
+            <el-table-column label="类型" prop="type" width="100">
+              <template #default="scope">{{ scope.row.type === '0' ? '水利动态' : '水利要闻' }}</template>
+            </el-table-column>
+            <el-table-column label="发布人" prop="pubman" width="120" />
+            <el-table-column label="发布时间" prop="pubtime" width="160" />
+            <el-table-column label="操作" width="150" fixed="right">
+              <template #default="scope">
+                <el-button link type="primary" size="small" @click="handleEditArticle(scope.row)">编辑</el-button>
+                <el-button link type="danger" size="small" @click="handleDeleteArticle(scope.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination v-show="articleTotal > 0" :total="articleTotal" v-model:page="articlePageNum" v-model:limit="articlePageSize" @pagination="getArticleList" />
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+
+    <!-- 工作方案编辑弹窗 -->
+    <el-dialog v-model="wpDialogVisible" :title="wpDialogTitle" width="700px">
+      <el-form :model="wpForm" :rules="wpRules" ref="wpFormRef" label-width="100px">
+        <el-form-item label="方案名称" prop="wpName">
+          <el-input v-model="wpForm.wpName" placeholder="请输入方案名称" />
+        </el-form-item>
+        <el-form-item label="文号" prop="fileNum">
+          <el-input v-model="wpForm.fileNum" placeholder="请输入文号" />
+        </el-form-item>
+        <el-form-item label="发布日期" prop="rlsTm">
+          <el-date-picker v-model="wpForm.rlsTm" type="date" placeholder="选择日期" style="width:100%" value-format="YYYY-MM-DD" />
+        </el-form-item>
+        <el-form-item label="发布单位" prop="releWiunName">
+          <el-input v-model="wpForm.releWiunName" placeholder="请输入发布单位" />
+        </el-form-item>
+        <el-form-item label="附件">
+          <file-upload v-model="wpForm.attc" :limit="1" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="wpDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitWpForm">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 制度文件编辑弹窗 -->
+    <el-dialog v-model="fileDialogVisible" :title="fileDialogTitle" width="700px">
+      <el-form :model="fileForm" :rules="fileRules" ref="fileFormRef" label-width="100px">
+        <el-form-item label="文件名称" prop="fileName">
+          <el-input v-model="fileForm.fileName" placeholder="请输入文件名称" />
+        </el-form-item>
+        <el-form-item label="文件类型" prop="fileType">
+          <el-select v-model="fileForm.fileType" placeholder="请选择" style="width:100%">
+            <el-option label="中央文件" value="0" />
+            <el-option label="国务院文件" value="1" />
+            <el-option label="水利部文件" value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="文号" prop="wh">
+          <el-input v-model="fileForm.wh" placeholder="请输入文号" />
+        </el-form-item>
+        <el-form-item label="日期" prop="startDate">
+          <el-date-picker v-model="fileForm.startDate" type="date" placeholder="选择日期" style="width:100%" value-format="YYYY-MM-DD" />
+        </el-form-item>
+        <el-form-item label="附件">
+          <file-upload v-model="fileForm.url" :limit="1" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="fileDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitFileForm">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 工作动态编辑弹窗 -->
+    <el-dialog v-model="articleDialogVisible" :title="articleDialogTitle" width="700px">
+      <el-form :model="articleForm" :rules="articleRules" ref="articleFormRef" label-width="100px">
+        <el-form-item label="标题" prop="title">
+          <el-input v-model="articleForm.title" placeholder="请输入标题" />
+        </el-form-item>
+        <el-form-item label="类型" prop="type">
+          <el-select v-model="articleForm.type" placeholder="请选择" style="width:100%">
+            <el-option label="水利动态" value="0" />
+            <el-option label="水利要闻" value="1" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="发布人" prop="pubman">
+          <el-input v-model="articleForm.pubman" placeholder="请输入发布人" />
+        </el-form-item>
+        <el-form-item label="发布时间" prop="pubtime">
+          <el-date-picker v-model="articleForm.pubtime" type="datetime" placeholder="选择时间" style="width:100%" value-format="YYYY-MM-DD HH:mm:ss" />
+        </el-form-item>
+        <el-form-item label="内容">
+          <el-input v-model="articleForm.content" type="textarea" :rows="5" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="articleDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitArticleForm">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, onMounted } from 'vue'
+import { ref, reactive, computed, onMounted, watch } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus } from '@element-plus/icons-vue'
 import { listWorkPlan } from '@/api/hzz/workplan'
-import { listFile, listArticle } from '@/api/hzz/rule'
+import { listFile, addFile, updateFile, delFile, listArticle, addArticle, updateArticle, delArticle } from '@/api/hzz/rule'
+import { getChildren } from '@/api/hzz/adtree'
+import FileUpload from '@/components/FileUpload'
 
+const loading = ref(false)
 const activeTab = ref('workplan')
 
+// ========== 左侧树 ========== 
+const treeRef = ref(null)
+const treeFilter = ref('')
+const treeData = ref([])
+const treeProps = { label: 'adName', children: 'children' }
+const currentAdCode = ref('000000000000')
+const currentAdName = ref('太湖流域')
+const isNational = computed(() => currentAdCode.value === '000000000000')
+
 // ========== 工作方案 ==========
-const wpLoading = ref(false)
-const workPlanList = ref([])
-const getWorkPlanList = async () => {
-  wpLoading.value = true
+const workPlans = ref([])
+const wpDialogVisible = ref(false)
+const wpDialogTitle = ref('')
+const wpFormRef = ref(null)
+const wpForm = reactive({
+  guid: '',
+  wpName: '',
+  fileNum: '',
+  rlsTm: '',
+  releWiunName: '',
+  attc: '',
+  adCode: ''
+})
+const wpRules = {
+  wpName: [{ required: true, message: '方案名称不能为空', trigger: 'blur' }]
+}
+
+const loadWorkPlans = async () => {
+  if (isNational.value) {
+    workPlans.value = []
+    return
+  }
   try {
-    const res = await listWorkPlan('')
-    workPlanList.value = res.data || []
-  } finally { wpLoading.value = false }
+    const res = await listWorkPlan(currentAdCode.value)
+    workPlans.value = res.data || []
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+const handleAdd = () => {
+  wpDialogTitle.value = '新增工作方案'
+  Object.assign(wpForm, { guid: '', wpName: '', fileNum: '', rlsTm: '', releWiunName: '', attc: '', adCode: currentAdCode.value })
+  wpDialogVisible.value = true
+}
+
+const handleEdit = (row) => {
+  wpDialogTitle.value = '编辑工作方案'
+  Object.assign(wpForm, { ...row })
+  wpDialogVisible.value = true
+}
+
+const submitWpForm = async () => {
+  await wpFormRef.value.validate()
+  // TODO: 调用后端API保存
+  ElMessage.success('保存成功(待实现后端接口)')
+  wpDialogVisible.value = false
+  loadWorkPlans()
+}
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm('确认删除该工作方案吗?', '警告', { type: 'warning' }).then(async () => {
+    // TODO: 调用后端API删除
+    ElMessage.success('删除成功(待实现后端接口)')
+    loadWorkPlans()
+  })
 }
 
 // ========== 制度文件 ==========
@@ -102,16 +286,82 @@ const fileList = ref([])
 const fileTotal = ref(0)
 const filePageNum = ref(1)
 const filePageSize = ref(10)
-const fileQuery = ref({ fileName: '', fileType: '' })
+const fileQuery = reactive({ fileName: '', fileType: '' })
+const fileDialogVisible = ref(false)
+const fileDialogTitle = ref('')
+const fileFormRef = ref(null)
+const fileForm = reactive({
+  guid: '',
+  fileName: '',
+  fileType: '',
+  wh: '',
+  startDate: '',
+  url: ''
+})
+const fileRules = {
+  fileName: [{ required: true, message: '文件名称不能为空', trigger: 'blur' }],
+  fileType: [{ required: true, message: '文件类型不能为空', trigger: 'change' }]
+}
+
 const getFileList = async () => {
   fileLoading.value = true
   try {
-    const res = await listFile({ ...fileQuery.value, types: '0', pageNum: filePageNum.value, pageSize: filePageSize.value })
+    const res = await listFile({ ...fileQuery, types: 'gzzd', pageNum: filePageNum.value, pageSize: filePageSize.value })
     fileList.value = res.rows || []
     fileTotal.value = res.total || 0
   } finally { fileLoading.value = false }
 }
-const resetFileQuery = () => { fileQuery.value = { fileName: '', fileType: '' }; getFileList() }
+
+const resetFileQuery = () => {
+  Object.assign(fileQuery, { fileName: '', fileType: '' })
+  getFileList()
+}
+
+const handleAddFile = () => {
+  fileDialogTitle.value = '新增制度文件'
+  Object.assign(fileForm, { guid: '', fileName: '', fileType: '', wh: '', startDate: '', url: '' })
+  fileDialogVisible.value = true
+}
+
+const handleEditFile = (row) => {
+  fileDialogTitle.value = '编辑制度文件'
+  Object.assign(fileForm, { ...row })
+  fileDialogVisible.value = true
+}
+
+const submitFileForm = async () => {
+  await fileFormRef.value.validate()
+  try {
+    if (fileForm.guid) {
+      await updateFile(fileForm)
+      ElMessage.success('修改成功')
+    } else {
+      await addFile(fileForm)
+      ElMessage.success('新增成功')
+    }
+    fileDialogVisible.value = false
+    getFileList()
+  } catch (e) {
+    ElMessage.error('操作失败')
+  }
+}
+
+const handleDeleteFile = (row) => {
+  ElMessageBox.confirm('确认删除该文件吗?', '警告', { type: 'warning' }).then(async () => {
+    try {
+      await delFile(row.guid)
+      ElMessage.success('删除成功')
+      getFileList()
+    } catch (e) {
+      ElMessage.error('删除失败')
+    }
+  })
+}
+
+const fileTypeLabel = (type) => {
+  const map = { '0': '中央文件', '1': '国务院文件', '2': '水利部文件' }
+  return map[type] || '其他'
+}
 
 // ========== 工作动态 ==========
 const articleLoading = ref(false)
@@ -119,20 +369,158 @@ const articleList = ref([])
 const articleTotal = ref(0)
 const articlePageNum = ref(1)
 const articlePageSize = ref(10)
-const articleQuery = ref({ title: '', type: '' })
+const articleQuery = reactive({ title: '', type: '' })
+const articleDialogVisible = ref(false)
+const articleDialogTitle = ref('')
+const articleFormRef = ref(null)
+const articleForm = reactive({
+  guid: '',
+  title: '',
+  type: '',
+  pubman: '',
+  pubtime: '',
+  content: ''
+})
+const articleRules = {
+  title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
+  type: [{ required: true, message: '类型不能为空', trigger: 'change' }]
+}
+
 const getArticleList = async () => {
   articleLoading.value = true
   try {
-    const res = await listArticle({ ...articleQuery.value, pageNum: articlePageNum.value, pageSize: articlePageSize.value })
+    const res = await listArticle({ ...articleQuery, pageNum: articlePageNum.value, pageSize: articlePageSize.value })
     articleList.value = res.rows || []
     articleTotal.value = res.total || 0
   } finally { articleLoading.value = false }
 }
-const resetArticleQuery = () => { articleQuery.value = { title: '', type: '' }; getArticleList() }
 
-onMounted(() => {
-  getWorkPlanList()
-  getFileList()
+const resetArticleQuery = () => {
+  Object.assign(articleQuery, { title: '', type: '' })
   getArticleList()
+}
+
+const handleAddArticle = () => {
+  articleDialogTitle.value = '新增工作动态'
+  Object.assign(articleForm, { guid: '', title: '', type: '', pubman: '', pubtime: '', content: '' })
+  articleDialogVisible.value = true
+}
+
+const handleEditArticle = (row) => {
+  articleDialogTitle.value = '编辑工作动态'
+  Object.assign(articleForm, { ...row })
+  articleDialogVisible.value = true
+}
+
+const submitArticleForm = async () => {
+  await articleFormRef.value.validate()
+  try {
+    if (articleForm.guid) {
+      await updateArticle(articleForm)
+      ElMessage.success('修改成功')
+    } else {
+      await addArticle(articleForm)
+      ElMessage.success('新增成功')
+    }
+    articleDialogVisible.value = false
+    getArticleList()
+  } catch (e) {
+    ElMessage.error('操作失败')
+  }
+}
+
+const handleDeleteArticle = (row) => {
+  ElMessageBox.confirm('确认删除该动态吗?', '警告', { type: 'warning' }).then(async () => {
+    try {
+      await delArticle(row.guid)
+      ElMessage.success('删除成功')
+      getArticleList()
+    } catch (e) {
+      ElMessage.error('删除失败')
+    }
+  })
+}
+
+// ========== 树相关 ==========
+const loadTree = async () => {
+  const res = await getChildren('000000000000')
+  treeData.value = (res.data || []).map(item => ({ ...item }))
+}
+
+const loadTreeNode = async (node, resolve) => {
+  if (node.level === 0) {
+    return resolve(treeData.value)
+  }
+  try {
+    const res = await getChildren(node.data.adCode)
+    const children = (res.data || []).map(item => ({
+      ...item,
+      leaf: item.adGrad >= '5'
+    }))
+    resolve(children)
+  } catch {
+    resolve([])
+  }
+}
+
+const onNodeClick = (node) => {
+  currentAdCode.value = node.adCode
+  currentAdName.value = node.adName
+  if (!isNational.value) {
+    loadWorkPlans()
+  }
+}
+
+const filterNode = (value, data) => {
+  if (!value) return true
+  return data.adName?.includes(value)
+}
+
+watch(treeFilter, (val) => {
+  treeRef.value?.filter(val)
+})
+
+onMounted(async () => {
+  loading.value = true
+  await Promise.all([loadTree(), getFileList(), getArticleList()])
+  loading.value = false
 })
 </script>
+
+<style scoped>
+.bankline-page {
+  display: flex;
+  height: calc(100vh - 60px);
+}
+
+.left-tree {
+  width: 260px;
+  flex-shrink: 0;
+  border-right: 1px solid #e8e8e8;
+  overflow-y: auto;
+  background: #fafbfc;
+}
+
+.tree-title {
+  padding: 12px 16px;
+  font-weight: 600;
+  font-size: 15px;
+  color: #333;
+  border-bottom: 1px solid #eee;
+}
+
+.right-content {
+  flex: 1;
+  overflow-y: auto;
+  padding: 20px;
+}
+
+.current-area {
+  margin-bottom: 20px;
+  padding: 10px;
+  background: #f0f7ff;
+  border-radius: 6px;
+  font-size: 13px;
+  color: #555;
+}
+</style>

+ 10 - 8
gw-ui/src/views/hlgl/eventclear/index.vue

@@ -49,14 +49,16 @@
       <div class="detail-grid" v-if="cur">
         <div class="detail-left">
           <table class="info-table">
-            <tr><td class="lbl">河段名称</td><td>{{ cur.objectName }}</td></tr>
-            <tr><td class="lbl">问题描述</td><td>{{ cur.eventDesc }}</td></tr>
-            <tr><td class="lbl">当前情况</td><td>{{ cur.curs || '-' }}</td></tr>
-            <tr><td class="lbl">整改情况</td><td>{{ cur.dealDesc || '-' }}</td></tr>
-            <tr><td class="lbl">检查情况</td><td>{{ cur.checkQk || '-' }}</td></tr>
-            <tr><td class="lbl">所属省份</td><td>{{ cur.adName }}</td><td class="lbl">事件类型</td><td>{{ evTypeMap[cur.evType] }}</td></tr>
-            <tr><td class="lbl">发现日期</td><td>{{ cur.findTs }}</td><td class="lbl">完成日期</td><td>{{ cur.doneDate }}</td></tr>
-            <tr><td class="lbl">经度</td><td>{{ cur.longitude }}</td><td class="lbl">纬度</td><td>{{ cur.latitude }}</td></tr>
+            <tbody>
+              <tr><td class="lbl">河段名称</td><td>{{ cur.objectName }}</td></tr>
+              <tr><td class="lbl">问题描述</td><td>{{ cur.eventDesc }}</td></tr>
+              <tr><td class="lbl">当前情况</td><td>{{ cur.curs || '-' }}</td></tr>
+              <tr><td class="lbl">整改情况</td><td>{{ cur.dealDesc || '-' }}</td></tr>
+              <tr><td class="lbl">检查情况</td><td>{{ cur.checkQk || '-' }}</td></tr>
+              <tr><td class="lbl">所属省份</td><td>{{ cur.adName }}</td><td class="lbl">事件类型</td><td>{{ evTypeMap[cur.evType] }}</td></tr>
+              <tr><td class="lbl">发现日期</td><td>{{ cur.findTs }}</td><td class="lbl">完成日期</td><td>{{ cur.doneDate }}</td></tr>
+              <tr><td class="lbl">经度</td><td>{{ cur.longitude }}</td><td class="lbl">纬度</td><td>{{ cur.latitude }}</td></tr>
+            </tbody>
           </table>
           <!-- 图片轮播 -->
           <div v-if="cur.images?.length" style="margin-top:12px">

+ 17 - 13
gw-ui/src/views/hlgl/gzfa/index.vue

@@ -75,8 +75,9 @@
     </div>
 
     <!-- 文件详情弹窗 -->
-    <el-dialog v-model="detailVisible" :title="detailItem?.fileName || detailItem?.wpName || detailItem?.sysName" width="700px">
-      <iframe-view :url="detailItem?.url" style="height: 79vh;"></iframe-view>
+    <el-dialog v-model="detailVisible" :title="detailItem?.fileName || detailItem?.wpName || detailItem?.sysName"
+               width="700px">
+      <i-frame :src="detailItem?.url" style="height: 79vh;"></i-frame>
     </el-dialog>
   </div>
 </template>
@@ -86,7 +87,7 @@ import {computed, onMounted, reactive, ref, watch} from 'vue'
 import {listFile, listRule} from '@/api/hzz/rule'
 import {getChildren} from '@/api/hzz/adtree'
 import {listWorkPlan} from '@/api/hzz/workplan'
-import IframeView from "@/views/hlgl/IframeView.vue";
+import iFrame from '@/components/iFrame'
 
 const loading = ref(false)
 const treeRef = ref(null)
@@ -195,16 +196,19 @@ const sysTypeFilter = (item) => {
 }
 
 const ruleProgressFilter = (item) => {
-  if (item.ruleProgress === '1') {
-    return '研讨'
-  } else if (item.ruleProgress === '2') {
-    return '起草'
-  } else if (item.ruleProgress === '3') {
-    return '征求意见'
-  } else if (item.ruleProgress === '4') {
-    return '报批'
-  } else if (item.ruleProgress === '5') {
-    return '印发'
+  switch (item.ruleProgress) {
+    case '1':
+      return '研讨'
+    case '2':
+      return '起草'
+    case '3':
+      return '征求意见'
+    case '4':
+      return '报批'
+    case '5':
+      return '印发'
+    default:
+      return ''
   }
 }
 

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

@@ -16,7 +16,7 @@
 
     <!-- 文件详情弹窗 -->
     <el-dialog v-model="detailVisible" :title="detailItem?.fileName || detailItem?.wpName || detailItem?.sysName" width="700px">
-      <iframe-view :url="detailItem?.url" style="height: 79vh;"></iframe-view>
+      <i-frame :src="detailItem?.url" style="height: 79vh;"></i-frame>
     </el-dialog>
   </div>
 </template>
@@ -24,7 +24,7 @@
 <script setup>
 import {onMounted, ref} from 'vue'
 import {listFile} from '@/api/hzz/rule'
-import IframeView from "@/views/hlgl/IframeView.vue";
+import iFrame from '@/components/iFrame'
 
 const loading = ref(false)
 const list = ref([])

+ 3 - 5
sql/gw_dm.sql

@@ -569,9 +569,7 @@ create table sys_logininfor (
   status         char(1)        default '0'              ,
   msg            VARCHAR2(255)   default ''               ,
   login_time     TIMESTAMP                                ,
-  primary key (info_id),
-  key idx_sys_logininfor_s  (status),
-  key idx_sys_logininfor_lt (login_time)
+  primary key (info_id)
 ) ;
 
 
@@ -656,7 +654,7 @@ create table sys_notice_read (
   user_id          BIGINT       not null                  ,
   read_time        TIMESTAMP         not null                  ,
   primary key (read_id),
-  unique key uk_user_notice (user_id, notice_id)  
+  unique key uk_user_notice (user_id, notice_id)
 ) ;
 
 
@@ -718,4 +716,4 @@ create table gen_table_column (
   update_by         VARCHAR2(64)     default ''                ,
   update_time       TIMESTAMP                                  ,
   primary key (column_id)
-) ;
+) ;