zhuj123456 1 месяц назад
Родитель
Сommit
6e3d75c8c0

+ 311 - 65
gw-ui/src/frontLayout/systemMenuBar.vue

@@ -1,22 +1,54 @@
 <template>
   <div id="systemMenuBar">
-    <div class="menu-item" :class="{ active: menuItem.id === 'sy' }"
-      v-for="(menuItem, idx) in menuInfos" :key="idx"
+    <div
+      class="menu-item"
+      :class="{ active: activeMenuId === menuItem.id }"
+      v-for="(menuItem, idx) in menuInfos"
+      :key="idx"
+      :ref="(el) => setMenuRef(menuItem.id, el)"
       v-show="menuItem && menuItem.menuName != '数据维护'"
-      @mouseenter="onHover(menuItem)" @mouseleave="onLeave" @click="onClick(menuItem)">
+      @mouseenter="onHover(menuItem, $event)"
+      @mouseleave="onLeave"
+      @click="onClick(menuItem)"
+    >
       <span class="menu-label">{{ menuItem.menuName }}</span>
       <span class="menu-arrow" v-if="menuItem.children">▾</span>
     </div>
-    <div class="menu-item" v-for="(menuItem, idx) in menuInfos" :key="'m-'+idx"
+    <div
+      class="menu-item"
+      v-for="(menuItem, idx) in menuInfos"
+      :key="'m-' + idx"
       v-show="menuItem && menuItem.menuName == '数据维护' && isLogin == 'true'"
-      @click="onClick(menuItem)">
+      @click="onClick(menuItem)"
+    >
       <span class="menu-label">{{ menuItem.menuName }}</span>
     </div>
+
+    <!-- 下拉面板 -->
     <transition name="fade-drop">
-      <div class="dropdown-panel" v-show="showDropdown" @mouseenter="keepOpen" @mouseleave="onLeave">
-        <div class="dropdown-col" v-for="sub in dropdownChildren" :key="sub.menuName">
-          <div class="dropdown-title" @click="onClick(sub)">{{ sub.menuName }}</div>
-          <div class="dropdown-link" v-for="ch in sub.children" :key="ch.menuName" @click="onClick(ch)">{{ ch.menuName }}</div>
+      <div
+        class="dropdown-panel"
+        v-show="showDropdown && dropdownChildren.length > 0"
+        :style="dropdownStyle"
+        @mouseenter="keepOpen"
+        @mouseleave="onLeave"
+      >
+        <div
+          class="dropdown-col"
+          v-for="sub in dropdownChildren"
+          :key="sub.menuName"
+        >
+          <div class="dropdown-title" @click="onClick(sub)">
+            {{ sub.menuName }}
+          </div>
+          <div
+            class="dropdown-link"
+            v-for="ch in sub.children"
+            :key="ch.menuName"
+            @click="onClick(ch)"
+          >
+            {{ ch.menuName }}
+          </div>
         </div>
       </div>
     </transition>
@@ -24,77 +56,291 @@
 </template>
 
 <script setup>
-import { ref } from 'vue'
-import { useRouter } from 'vue-router'
-import Cookies from 'js-cookie'
+import { ref, reactive } from "vue";
+import { useRouter, useRoute } from "vue-router";
+import Cookies from "js-cookie";
+
+const router = useRouter();
+const route = useRoute();
+const isLogin = ref(Cookies.get("isLogin") || "");
+const showDropdown = ref(false);
+const keepShow = ref(false);
+const dropdownChildren = ref([]);
+const activeMenuId = ref("sy");
+const menuRefs = reactive({});
 
-const router = useRouter()
-const emit = defineEmits(['addSystemMenuView'])
-const isLogin = ref(Cookies.get('isLogin') || '')
-const showDropdown = ref(false)
-const keepShow = ref(false)
-const dropdownChildren = ref([])
+let leaveTimer = null;
+
+const dropdownStyle = ref({});
 
 const menuInfos = ref([
-  { 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 },
-    ]}]
+  {
+    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,
+          },
+        ],
+      },
+    ],
   },
-  { menuName: '水域岸线', name: '水域岸线', haveView: true, id: 'syax',
-    children: [{ menuName: '水域岸线', children: [
-      { menuName: '涉河项目', href: '/shjsxm', path: '/shjsxm', haveView: true },
-      { menuName: '水域岸线', href: '/syaxgl', path: '/syaxgl', haveView: true },
-    ]}]
+  {
+    menuName: "水域岸线",
+    name: "水域岸线",
+    haveView: true,
+    id: "syax",
+    children: [
+      {
+        menuName: "水域岸线",
+        children: [
+          {
+            menuName: "涉河项目",
+            href: "/shjsxm",
+            path: "/shjsxm",
+            haveView: true,
+          },
+          {
+            menuName: "水域岸线",
+            href: "/syaxgl",
+            path: "/syaxgl",
+            haveView: true,
+          },
+        ],
+      },
+    ],
   },
-  { menuName: '考核评估', name: '考核评估', haveView: true, id: 'khpg',
-    children: [{ menuName: '考核评估', children: [
-      { menuName: '考核结果', href: '/khpg', path: '/khpg', haveView: true },
-      { menuName: '相关文件', href: '/khwj', path: '/khwj', haveView: true },
-    ]}]
+  {
+    menuName: "考核评估",
+    name: "考核评估",
+    haveView: true,
+    id: "khpg",
+    children: [
+      {
+        menuName: "考核评估",
+        children: [
+          {
+            menuName: "考核结果",
+            href: "/khpg",
+            path: "/khpg",
+            haveView: true,
+          },
+          {
+            menuName: "相关文件",
+            href: "/khwj",
+            path: "/khwj",
+            haveView: true,
+          },
+        ],
+      },
+    ],
   },
-  { menuName: '督导检查', name: '督导检查', haveView: true, id: 'ddjc',
-    children: [{ menuName: '督导检查', children: [
-      { menuName: '涉河项目督察', href: '/ddlb', path: '/ddlb', haveView: true },
-      { menuName: '河湖管理督察', href: '/ddsh', path: '/ddsh', haveView: true },
-      { menuName: '一湖两河清四乱', href: '/sjml', path: '/sjml', haveView: true },
-    ]}]
+  {
+    menuName: "督导检查",
+    name: "督导检查",
+    haveView: true,
+    id: "ddjc",
+    children: [
+      {
+        menuName: "督导检查",
+        children: [
+          {
+            menuName: "涉河项目督察",
+            href: "/ddlb",
+            path: "/ddlb",
+            haveView: true,
+          },
+          {
+            menuName: "河湖管理督察",
+            href: "/ddsh",
+            path: "/ddsh",
+            haveView: true,
+          },
+          {
+            menuName: "一湖两河清四乱",
+            href: "/sjml",
+            path: "/sjml",
+            haveView: true,
+          },
+        ],
+      },
+    ],
   },
-  { menuName: '数据维护', name: '数据维护', href: '/sjwh', path: '/sjwh', haveView: true, id: 'sjwh' }
-])
+  {
+    menuName: "数据维护",
+    name: "数据维护",
+    href: "/sjwh",
+    path: "/sjwh",
+    haveView: true,
+    id: "sjwh",
+  },
+]);
+
+const setMenuRef = (id, el) => {
+  if (el) menuRefs[id] = el;
+};
+
+const onHover = (item) => {
+  if (leaveTimer) {
+    clearTimeout(leaveTimer);
+    leaveTimer = null;
+  }
+
+  if (item.children) {
+    keepShow.value = true;
+    dropdownChildren.value = item.children;
+
+    // 计算下拉面板位置
+    const menuEl = menuRefs[item.id];
+    if (menuEl) {
+      const rect = menuEl.getBoundingClientRect();
+      dropdownStyle.value = {
+        left: rect.left + "px",
+        top: rect.bottom + "px",
+      };
+    }
+
+    showDropdown.value = true;
+  } else {
+    showDropdown.value = false;
+  }
+};
+
+const onLeave = () => {
+  keepShow.value = false;
+  if (leaveTimer) clearTimeout(leaveTimer);
+  leaveTimer = setTimeout(() => {
+    if (!keepShow.value) {
+      showDropdown.value = false;
+    }
+  }, 200);
+};
+
+const keepOpen = () => {
+  keepShow.value = true;
+  if (leaveTimer) {
+    clearTimeout(leaveTimer);
+    leaveTimer = null;
+  }
+};
 
-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)
+  if (leaveTimer) {
+    clearTimeout(leaveTimer);
+    leaveTimer = null;
   }
-}
+  showDropdown.value = false;
+  keepShow.value = false;
+  if (item.id) activeMenuId.value = item.id;
+  if (item.href) router.push(item.href);
+};
 </script>
 
 <style scoped>
-#systemMenuBar { height: 100%; display: flex; align-items: center; gap: 2px; padding: 0 8px; }
+#systemMenuBar {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  gap: 2px;
+  padding: 0 8px;
+}
+
 .menu-item {
-  height: 38px; padding: 0 16px; border-radius: 6px; display: flex; align-items: center; gap: 4px;
-  cursor: pointer; transition: all .2s; color: #3a5f80; font-size: 13px; font-weight: 500; white-space: nowrap;
+  height: 38px;
+  padding: 0 16px;
+  border-radius: 6px;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  cursor: pointer;
+  transition:
+    background-color 0.2s,
+    color 0.2s;
+  color: #3a5f80;
+  font-size: 13px;
+  font-weight: 500;
+  white-space: nowrap;
+  user-select: none;
+}
+.menu-item:hover {
+  background: #cde4f7;
+  color: #0d4b80;
 }
-.menu-item:hover { background: #cde4f7; color: #0d4b80; }
-.menu-item.active { background: #b5d8f0; color: #0a3a6e; font-weight: 600; }
-.menu-arrow { font-size: 9px; opacity: .4; }
+.menu-item.active {
+  background: #b5d8f0;
+  color: #0a3a6e;
+  font-weight: 600;
+}
+.menu-arrow {
+  font-size: 9px;
+  opacity: 0.4;
+}
+
+/* 下拉面板 - 固定定位 */
 .dropdown-panel {
-  position: fixed; top: 56px; left: 0; right: 0; z-index: 499; background: #fff;
-  box-shadow: 0 6px 20px rgba(0,0,0,.08); display: flex; gap: 40px; padding: 18px 60px;
-  animation: slideDown .18s ease; border-bottom: 2px solid #e0eef8;
+  position: fixed;
+  z-index: 999;
+  background: #fff;
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+  display: flex;
+  gap: 24px;
+  padding: 14px 20px;
+  border-radius: 8px;
+  border: 1px solid #e8e8e8;
+  white-space: nowrap;
+}
+.dropdown-col {
+  min-width: 100px;
+}
+.dropdown-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #0d4b80;
+  padding: 5px 0;
+  margin-bottom: 4px;
+  border-bottom: 2px solid #6baed6;
+  cursor: pointer;
+}
+.dropdown-link {
+  font-size: 13px;
+  color: #5a7a95;
+  padding: 5px 4px;
+  cursor: pointer;
+  border-radius: 4px;
+  transition: all 0.15s ease;
+}
+.dropdown-link:hover {
+  color: #0d6efd;
+  background: #eef6fc;
+  padding-left: 8px;
+}
+
+/* 过渡动画 */
+.fade-drop-enter-active {
+  transition: all 0.2s ease;
+}
+.fade-drop-leave-active {
+  transition: all 0.15s ease;
+}
+.fade-drop-enter-from,
+.fade-drop-leave-to {
+  opacity: 0;
+  transform: translateY(-4px);
 }
-@keyframes slideDown { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
-.dropdown-col { min-width: 100px; }
-.dropdown-title { font-size: 14px; font-weight: 600; color: #0d4b80; padding: 5px 0; margin-bottom: 4px; border-bottom: 2px solid #6baed6; }
-.dropdown-link { font-size: 13px; color: #5a7a95; padding: 5px 4px; cursor: pointer; border-radius: 4px; }
-.dropdown-link:hover { color: #0d6efd; background: #eef6fc; padding-left: 8px; }
-.fade-drop-enter-active { transition: all .18s ease; }
-.fade-drop-leave-active { transition: all .12s ease; }
-.fade-drop-enter-from, .fade-drop-leave-to { opacity: 0; transform: translateY(-4px); }
 </style>

+ 341 - 79
gw-ui/src/views/hlgl/assess/index.vue

@@ -1,110 +1,372 @@
 <template>
   <div class="assess-page">
-    <h1 class="page-title">考核评估</h1>
-
     <!-- 5 个统计卡片 -->
     <div class="stat-cards">
-      <div class="stat-card" v-for="tab in tabs" :key="tab.name" :class="{ active: activeStat === tab.name }" @click="activeStat = tab.name">
-        <div class="stat-num">{{ tab.total }}<span class="stat-unit">{{ tab.unit }}</span></div>
+      <div
+        class="stat-card"
+        v-for="tab in tabs"
+        :key="tab.name"
+        :class="{ active: activeStat === tab.name }"
+        @click="activeStat = tab.name"
+      >
+        <div class="stat-num">
+          {{ tab.total }}<span class="stat-unit">{{ tab.unit }}</span>
+        </div>
         <div class="stat-label">{{ tab.label }}</div>
         <div class="stat-desc">{{ tab.desc }}</div>
       </div>
     </div>
 
-    <!-- 选中卡片的分级表格 -->
-    <el-card shadow="never" class="detail-card" v-if="activeRows">
-      <template #header>{{ activeStatLabel }} 分级统计</template>
-      <el-table :data="activeRows" border size="small" style="max-width:500px">
-        <el-table-column prop="label" label="级别" width="100" />
-        <el-table-column prop="value" label="数量" />
-      </el-table>
-    </el-card>
+    <!-- 内容区域 -->
+    <div class="content-area">
+      <!-- 选中卡片的分级表格 -->
+      <div class="detail-section" v-if="activeRows">
+        <div class="section-title">{{ activeStatLabel }} 分级统计</div>
+        <div class="table-wrap">
+          <el-table
+            :data="activeRows"
+            border
+            stripe
+            size="small"
+            :header-cell-style="tableHeaderStyle"
+          >
+            <el-table-column
+              prop="label"
+              label="级别"
+              width="80"
+              align="center"
+            />
+            <el-table-column prop="value" label="数量" align="center" />
+          </el-table>
+        </div>
+      </div>
 
-    <!-- 中期评估图表 -->
-    <el-card shadow="never" class="chart-card">
-      <template #header>中期评估定量赋分结果</template>
-      <div class="chart-row">
-        <div ref="scoreMap" class="chart-half"></div>
-        <div ref="provinceBar" class="chart-half"></div>
+      <!-- 中期评估图表 -->
+      <div class="chart-section">
+        <div class="section-title">中期评估定量赋分结果</div>
+        <div class="chart-row">
+          <div class="chart-panel map-panel">
+            <div ref="scoreMap" class="chart-box"></div>
+          </div>
+          <div class="chart-panel bar-panel">
+            <div ref="provinceBar" class="chart-box"></div>
+          </div>
+        </div>
       </div>
-    </el-card>
+    </div>
   </div>
 </template>
 
 <script setup>
-import {computed, nextTick, onMounted, ref} from 'vue'
-import * as echarts from 'echarts'
-import chinaGeo from '@/assets/geoData/china_xh.json'
+import { computed, nextTick, onMounted, ref } from "vue";
+import * as echarts from "echarts";
+import chinaGeo from "@/assets/geoData/china_xh.json";
 
-const activeStat = ref('plan')
+const activeStat = ref("plan");
+const tableHeaderStyle = {
+  background: "#f5f7fa",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "13px",
+  textAlign: "center",
+};
 
 const tabs = [
-  {name:'plan',label:'工作方案', total:44107,unit:'份',desc:'省、市、县、乡四级共正式印发',
-    rows:[{label:'省级',value:31},{label:'市级',value:359},{label:'县级',value:3244},{label:'乡级',value:40472}]},
-  {name:'chief',label:'河长湖长', total:947974,unit:'名',desc:'设立了省、市、县、乡、村五级河长',
-    rows:[{label:'省级',value:336},{label:'市级',value:5435},{label:'县级',value:51935},{label:'乡级',value:262966},{label:'村级',value:627302}]},
-  {name:'office',label:'河长办', total:13571,unit:'人',desc:'河长制办公室人员情况',rows:null},
-  {name:'signboard',label:'公示牌', total:680000,unit:'个',desc:'河长公示牌统计',rows:null},
-  {name:'rule',label:'出台规定制度', total:6863,unit:'项',desc:'出台规定制度统计',
-    rows:[{label:'省级',value:31},{label:'市级',value:245},{label:'县级',value:2951},{label:'乡级',value:3636}]},
-]
+  {
+    name: "plan",
+    label: "工作方案",
+    total: 44107,
+    unit: "份",
+    desc: "省、市、县、乡四级共正式印发",
+    rows: [
+      { label: "省级", value: 31 },
+      { label: "市级", value: 359 },
+      { label: "县级", value: 3244 },
+      { label: "乡级", value: 40472 },
+    ],
+  },
+  {
+    name: "chief",
+    label: "河长湖长",
+    total: 947974,
+    unit: "名",
+    desc: "设立了省、市、县、乡、村五级河长",
+    rows: [
+      { label: "省级", value: 336 },
+      { label: "市级", value: 5435 },
+      { label: "县级", value: 51935 },
+      { label: "乡级", value: 262966 },
+      { label: "村级", value: 627302 },
+    ],
+  },
+  {
+    name: "office",
+    label: "河长办",
+    total: 13571,
+    unit: "人",
+    desc: "河长制办公室人员情况",
+    rows: null,
+  },
+  {
+    name: "signboard",
+    label: "公示牌",
+    total: 680000,
+    unit: "个",
+    desc: "河长公示牌统计",
+    rows: null,
+  },
+  {
+    name: "rule",
+    label: "出台规定制度",
+    total: 6863,
+    unit: "项",
+    desc: "出台规定制度统计",
+    rows: [
+      { label: "省级", value: 31 },
+      { label: "市级", value: 245 },
+      { label: "县级", value: 2951 },
+      { label: "乡级", value: 3636 },
+    ],
+  },
+];
 
-const activeRows = computed(() => tabs.find(t=>t.name===activeStat.value)?.rows || null)
-const activeStatLabel = computed(() => tabs.find(t=>t.name===activeStat.value)?.label || '')
+const activeRows = computed(
+  () => tabs.find((t) => t.name === activeStat.value)?.rows || null,
+);
+const activeStatLabel = computed(
+  () => tabs.find((t) => t.name === activeStat.value)?.label || "",
+);
 
-const scoreMap = ref(null), provinceBar = ref(null)
-echarts.registerMap('taihu', chinaGeo)
+const scoreMap = ref(null);
+const provinceBar = ref(null);
+echarts.registerMap("taihu", chinaGeo);
 
 const provinceData = [
-  {name:'江苏省',total:99.95},{name:'上海市',total:99},{name:'浙江省',total:98.95},{name:'福建省',total:99},{name:'安徽省',total:99},
-].sort((a,b)=>b.total-a.total)
+  { name: "江苏省", total: 99.95 },
+  { name: "上海市", total: 99 },
+  { name: "浙江省", total: 98.95 },
+  { name: "福建省", total: 99 },
+  { name: "安徽省", total: 99 },
+].sort((a, b) => b.total - a.total);
 
-const initCharts = () => { nextTick(() => {
-  if (scoreMap.value) {
-    const c0 = echarts.init(scoreMap.value)
-    c0.setOption({
-      title:{text:'太湖流域各省总分分布',left:'center',textStyle:{fontSize:14}},
-      tooltip:{trigger:'item',formatter:(p)=>`${p.name}: ${p.value} 分`},
-      visualMap:{min:94,max:100,left:'left',bottom:10,text:['高','低'],inRange:{color:['#e0f3f8','#abd9e9','#74add1','#4575b4','#313695']}},
-      series:[{type:'map',map:'taihu',nameProperty:'name',roam:false,label:{show:true,fontSize:12},data:provinceData.map(d=>({name:d.name,value:d.total}))}]
-    })
-    window.addEventListener('resize',()=>c0.resize())
-  }
-  if (provinceBar.value) {
-    const c = echarts.init(provinceBar.value)
-    c.setOption({
-      title:{text:'太湖流域各省总分排名',left:'center',textStyle:{fontSize:14}},
-      grid:{left:60,right:40,top:40,bottom:20},
-      yAxis:{type:'category',data:provinceData.map(d=>d.name.replace('省','').replace('市','')),inverse:true},
-      xAxis:{type:'value',name:'分',min:94,max:100},
-      series:[{type:'bar',data:provinceData.map(d=>d.total),itemStyle:{color:'#409EFF'},barMaxWidth:24,label:{show:true,position:'right',formatter:'{c}分'}}]
-    })
-    window.addEventListener('resize',()=>c.resize())
-  }
-})}
-onMounted(()=>initCharts())
+const initCharts = () => {
+  nextTick(() => {
+    if (scoreMap.value) {
+      const c0 = echarts.init(scoreMap.value);
+      c0.setOption({
+        title: {
+          text: "太湖流域各省总分分布",
+          left: "center",
+          top: 5,
+          textStyle: { fontSize: 13 },
+        },
+        tooltip: {
+          trigger: "item",
+          formatter: (p) => `${p.name}: ${p.value} 分`,
+        },
+        visualMap: {
+          min: 94,
+          max: 100,
+          left: 8,
+          bottom: 8,
+          text: ["高", "低"],
+          calculable: false,
+          itemWidth: 12,
+          itemHeight: 80,
+          textStyle: { fontSize: 10 },
+          inRange: {
+            color: ["#e0f3f8", "#abd9e9", "#74add1", "#4575b4", "#313695"],
+          },
+        },
+        series: [
+          {
+            type: "map",
+            map: "taihu",
+            nameProperty: "name",
+            roam: false,
+            layoutCenter: ["50%", "40%"],
+            layoutSize: "65%",
+            label: { show: true, fontSize: 11, color: "#333" },
+            itemStyle: { borderColor: "#fff", borderWidth: 1 },
+            data: provinceData.map((d) => ({ name: d.name, value: d.total })),
+          },
+        ],
+      });
+      window.addEventListener("resize", () => c0.resize());
+    }
+    if (provinceBar.value) {
+      const c = echarts.init(provinceBar.value);
+      c.setOption({
+        title: {
+          text: "太湖流域各省总分排名",
+          left: "center",
+          top: 10,
+          textStyle: { fontSize: 13 },
+        },
+        grid: { left: 15, right: 70, top: 45, bottom: 20, containLabel: true },
+        yAxis: {
+          type: "category",
+          data: provinceData.map((d) => d.name),
+          inverse: true,
+          axisLabel: { fontSize: 11 },
+        },
+        xAxis: {
+          type: "value",
+          name: "分",
+          min: 94,
+          max: 100,
+          nameTextStyle: { fontSize: 10, padding: [0, 0, 0, -10] },
+          axisLabel: { fontSize: 10 },
+        },
+        series: [
+          {
+            type: "bar",
+            data: provinceData.map((d) => d.total),
+            itemStyle: { color: "#409EFF", borderRadius: [0, 3, 3, 0] },
+            barMaxWidth: 22,
+            label: {
+              show: true,
+              position: "right",
+              formatter: "{c} 分",
+              fontSize: 11,
+              color: "#333",
+            },
+          },
+        ],
+      });
+      window.addEventListener("resize", () => c.resize());
+    }
+  });
+};
+onMounted(() => initCharts());
 </script>
 
 <style scoped>
-.assess-page { padding:20px 24px; height:calc(100vh - 84px); overflow-y:auto; background:#f0f2f5; }
-.page-title { font-size:20px; color:#1a1a2e; margin:0 0 16px; font-weight:600; }
-.page-title::before { content:''; display:inline-block; width:4px; height:20px; background:#409EFF; margin-right:10px; vertical-align:middle; border-radius:2px; }
+.assess-page {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 12px;
+  gap: 10px;
+  overflow: hidden;
+}
 
 /* 统计卡片 */
-.stat-cards { display:flex; gap:12px; margin-bottom:16px; }
-.stat-card { flex:1; background:#fff; border-radius:8px; padding:16px; text-align:center; cursor:pointer; transition:all .2s; box-shadow:0 1px 4px rgba(0,0,0,.06); border:2px solid transparent; }
-.stat-card:hover { box-shadow:0 4px 12px rgba(0,0,0,.1); }
-.stat-card.active { border-color:#409EFF; }
-.stat-num { font-size:28px; font-weight:700; color:#409EFF; }
-.stat-unit { font-size:14px; color:#999; margin-left:2px; font-weight:400; }
-.stat-label { font-size:13px; color:#333; margin:4px 0; font-weight:500; }
-.stat-desc { font-size:11px; color:#aaa; }
+.stat-cards {
+  display: grid;
+  grid-template-columns: repeat(5, 1fr);
+  gap: 10px;
+  flex-shrink: 0;
+}
+.stat-card {
+  background: #fff;
+  border-radius: 8px;
+  padding: 14px 12px;
+  text-align: center;
+  cursor: pointer;
+  transition: all 0.2s;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  border: 2px solid transparent;
+}
+.stat-card:hover {
+  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
+  transform: translateY(-1px);
+}
+.stat-card.active {
+  border-color: #409eff;
+  background: #f0f7ff;
+}
+.stat-num {
+  font-size: 24px;
+  font-weight: 700;
+  color: #409eff;
+  line-height: 1.2;
+}
+.stat-unit {
+  font-size: 12px;
+  color: #999;
+  margin-left: 2px;
+  font-weight: 400;
+}
+.stat-label {
+  font-size: 13px;
+  color: #333;
+  margin: 2px 0;
+  font-weight: 500;
+}
+.stat-desc {
+  font-size: 11px;
+  color: #aaa;
+}
+
+/* 内容区域 */
+.content-area {
+  flex: 1;
+  display: flex;
+  gap: 10px;
+  min-height: 0;
+}
 
-.detail-card { margin-bottom:16px; border-radius:8px; }
-.detail-card :deep(.el-card__header) { font-weight:600; padding:10px 16px; }
+/* 左侧表格 */
+.detail-section {
+  width: 240px;
+  flex-shrink: 0;
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.section-title {
+  font-size: 13px;
+  font-weight: 600;
+  color: #333;
+  padding: 12px 14px 8px;
+  flex-shrink: 0;
+}
+.table-wrap {
+  flex: 1;
+  padding: 0 12px 12px;
+  overflow: hidden;
+}
 
-.chart-card { border-radius:8px; }
-.chart-card :deep(.el-card__header) { font-weight:600; }
-.chart-row { display:flex; gap:16px; }
-.chart-half { flex:1; height:380px; min-width:0; }
+/* 右侧图表 */
+.chart-section {
+  flex: 1;
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  min-width: 0;
+}
+.chart-section .section-title {
+  padding: 12px 14px 8px;
+}
+.chart-row {
+  flex: 1;
+  display: flex;
+  gap: 10px;
+  padding: 0 12px 12px;
+  min-height: 0;
+}
+.chart-panel {
+  background: #fafbfc;
+  border-radius: 6px;
+  min-width: 0;
+}
+.map-panel {
+  flex: 1;
+}
+.bar-panel {
+  width: 45%;
+  flex-shrink: 0;
+}
+.chart-box {
+  width: 100%;
+  height: 100%;
+}
 </style>

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

@@ -3,7 +3,7 @@
     <!-- 左侧行政区划树 -->
     <div class="left-tree">
       <div class="tree-title">行政区划</div>
-      <el-input v-model="treeFilter" placeholder="搜索" size="small" clearable style="margin:8px"/>
+      <el-input v-model="treeFilter" placeholder="搜索" size="small" clearable style="padding:8px"/>
       <el-tree
           :data="treeData" :props="treeProps" node-key="adCode"
           :filter-node-method="filterNode" ref="treeRef"

+ 503 - 85
gw-ui/src/views/hlgl/eventclear/index.vue

@@ -1,86 +1,253 @@
 <template>
   <div class="eventclear-page">
-    <!-- 搜索 -->
-    <el-card shadow="never" class="search-card">
-      <el-form :model="q" :inline="true" size="small">
+    <!-- 搜索区域 -->
+    <div class="search-wrapper">
+      <el-form :model="q" :inline="true" size="default" class="search-form">
         <el-form-item label="所在河湖">
-          <el-select v-model="q.objectType" placeholder="全部" clearable style="width:130px" @change="getList">
-            <el-option label="太湖" value="0" /><el-option label="望虞河" value="1" /><el-option label="太浦河" value="2" />
+          <el-select
+            v-model="q.objectType"
+            placeholder="全部"
+            clearable
+            style="width: 130px"
+            @change="getList"
+          >
+            <el-option label="太湖" value="0" /><el-option
+              label="望虞河"
+              value="1"
+            /><el-option label="太浦河" value="2" />
           </el-select>
         </el-form-item>
         <el-form-item label="处理状态">
-          <el-select v-model="q.status" placeholder="全部" clearable style="width:110px" @change="getList">
-            <el-option label="待处理" value="0" /><el-option label="处理中" value="1" /><el-option label="完成" value="2" /><el-option label="完结" value="3" />
+          <el-select
+            v-model="q.status"
+            placeholder="全部"
+            clearable
+            style="width: 110px"
+            @change="getList"
+          >
+            <el-option label="待处理" value="0" /><el-option
+              label="处理中"
+              value="1"
+            /><el-option label="完成" value="2" /><el-option
+              label="完结"
+              value="3"
+            />
           </el-select>
         </el-form-item>
         <el-form-item label="事件类型">
-          <el-select v-model="q.evType" placeholder="全部" clearable style="width:110px" @change="getList">
-            <el-option label="乱占" value="1" /><el-option label="乱采" value="2" /><el-option label="乱堆" value="3" /><el-option label="乱建" value="4" /><el-option label="其他" value="5" />
+          <el-select
+            v-model="q.evType"
+            placeholder="全部"
+            clearable
+            style="width: 110px"
+            @change="getList"
+          >
+            <el-option label="乱占" value="1" /><el-option
+              label="乱采"
+              value="2"
+            /><el-option label="乱堆" value="3" /><el-option
+              label="乱建"
+              value="4"
+            /><el-option label="其他" value="5" />
           </el-select>
         </el-form-item>
-        <el-form-item label="关键词"><el-input v-model="q.keyWord" placeholder="河段名/描述" clearable style="width:160px" @keyup.enter="getList" /></el-form-item>
-        <el-form-item><el-button type="primary" icon="Search" @click="getList">搜索</el-button></el-form-item>
-        <el-form-item><el-button @click="resetQ">重置</el-button></el-form-item>
+        <el-form-item label="关键词">
+          <el-input
+            v-model="q.keyWord"
+            placeholder="河段名/描述"
+            clearable
+            style="width: 160px"
+            @keyup.enter="getList"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="Search" @click="getList"
+            >搜索</el-button
+          >
+          <el-button icon="Refresh" @click="resetQ">重置</el-button>
+        </el-form-item>
       </el-form>
-    </el-card>
-
-    <!-- 列表 -->
-    <el-card shadow="never">
-      <el-table :data="list" border size="small" v-loading="loading" @row-click="showDetail">
-        <el-table-column type="index" label="#" width="50" />
-        <el-table-column prop="objectName" label="河段名称" show-overflow-tooltip width="140" />
-        <el-table-column prop="adName" label="所属地区" width="100" />
-        <el-table-column label="事件类型" width="80">
-          <template #default="{row}">{{ evTypeMap[row.evType] || row.evType }}</template>
+    </div>
+
+    <!-- 表格区域 -->
+    <div class="table-wrapper">
+      <el-table
+        :data="list"
+        border
+        stripe
+        size="default"
+        v-loading="loading"
+        :header-cell-style="headerCellStyle"
+        class="data-table"
+        @row-click="showDetail"
+      >
+        <el-table-column type="index" label="序号" width="55" align="center" />
+        <el-table-column
+          prop="objectName"
+          label="河段名称"
+          show-overflow-tooltip
+          width="140"
+          align="center"
+        />
+        <el-table-column
+          prop="adName"
+          label="所属地区"
+          width="100"
+          align="center"
+        />
+        <el-table-column label="事件类型" width="90" align="center">
+          <template #default="{ row }">
+            <el-tag :type="evTypeTag(row.evType)" size="small" effect="plain">
+              {{ evTypeMap[row.evType] || row.evType }}
+            </el-tag>
+          </template>
         </el-table-column>
-        <el-table-column prop="eventDesc" label="问题描述" show-overflow-tooltip min-width="200" />
-        <el-table-column label="状态" width="80">
-          <template #default="{row}">{{ statusMap[row.status] || row.status }}</template>
+        <el-table-column
+          prop="eventDesc"
+          label="问题描述"
+          show-overflow-tooltip
+          min-width="200"
+          align="center"
+        />
+        <el-table-column label="状态" width="90" align="center">
+          <template #default="{ row }">
+            <el-tag :type="statusTag(row.status)" size="small" effect="plain">
+              {{ statusMap[row.status] || row.status }}
+            </el-tag>
+          </template>
         </el-table-column>
-        <el-table-column prop="dealDesc" label="整改情况" show-overflow-tooltip width="150" />
-        <el-table-column prop="findTs" label="发现时间" width="120" />
-        <el-table-column prop="userName" label="上报人" width="100" />
+        <el-table-column
+          prop="dealDesc"
+          label="整改情况"
+          show-overflow-tooltip
+          width="160"
+          align="center"
+        />
+        <el-table-column
+          prop="findTs"
+          label="发现时间"
+          width="120"
+          align="center"
+        />
+        <el-table-column
+          prop="userName"
+          label="上报人"
+          width="100"
+          align="center"
+        />
       </el-table>
-      <pagination v-show="total>0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
-    </el-card>
+      <div class="pagination-wrap" v-show="total > 0">
+        <pagination
+          :total="total"
+          v-model:page="pageNum"
+          v-model:limit="pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
 
     <!-- 详情弹窗 -->
-    <el-dialog v-model="dlg" :title="cur?.objectName || '事件详情'" width="900px" top="5vh">
+    <el-dialog
+      v-model="dlg"
+      :title="cur?.objectName || '事件详情'"
+      width="950px"
+      top="3vh"
+      :close-on-click-modal="false"
+      class="detail-dialog"
+    >
       <div class="detail-grid" v-if="cur">
+        <!-- 左侧信息 -->
         <div class="detail-left">
-          <table class="info-table">
-            <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 class="section-title">基本信息</div>
+          <div class="info-grid">
+            <div class="info-item">
+              <span class="info-label">河段名称</span
+              ><span class="info-value">{{ cur.objectName }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">问题描述</span
+              ><span class="info-value">{{ cur.eventDesc }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">当前情况</span
+              ><span class="info-value">{{ cur.curs || "-" }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">整改情况</span
+              ><span class="info-value">{{ cur.dealDesc || "-" }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">检查情况</span
+              ><span class="info-value">{{ cur.checkQk || "-" }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">所属省份</span
+              ><span class="info-value">{{ cur.adName }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">事件类型</span
+              ><span class="info-value">
+                <el-tag :type="evTypeTag(cur.evType)" size="small">{{
+                  evTypeMap[cur.evType]
+                }}</el-tag>
+              </span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">发现日期</span
+              ><span class="info-value">{{ cur.findTs }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">完成日期</span
+              ><span class="info-value">{{ cur.doneDate || "-" }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">经度</span
+              ><span class="info-value">{{ cur.longitude }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">纬度</span
+              ><span class="info-value">{{ cur.latitude }}</span>
+            </div>
+          </div>
+
           <!-- 图片轮播 -->
-          <div v-if="cur.images?.length" style="margin-top:12px">
-            <el-carousel height="220px" indicator-position="outside">
-              <el-carousel-item v-for="(img,i) in cur.images" :key="i">
-                <img :src="img" style="width:100%;height:100%;object-fit:contain;cursor:pointer" @click="previewImg(img)" />
+          <div class="section-title" style="margin-top: 16px">现场图片</div>
+          <div v-if="cur.images?.length" class="image-carousel">
+            <el-carousel
+              height="240px"
+              indicator-position="outside"
+              arrow="always"
+            >
+              <el-carousel-item v-for="(img, i) in cur.images" :key="i">
+                <img :src="img" class="carousel-img" @click="previewImg(img)" />
               </el-carousel-item>
             </el-carousel>
           </div>
+          <div v-else class="empty-block">暂无现场图片</div>
         </div>
+
+        <!-- 右侧检查记录 -->
         <div class="detail-right">
-          <h4>检查记录</h4>
-          <div v-if="cur.checks?.length">
-            <div v-for="(c,i) in cur.checks" :key="i" class="check-item">
-              <p><strong>检查人:</strong>{{ c.fPerson }} &nbsp; <strong>日期:</strong>{{ c.cDate }}</p>
-              <p><strong>情况:</strong>{{ c.cState }}</p>
-              <div v-if="c.files?.length" style="display:flex;gap:6px;flex-wrap:wrap">
-                <img v-for="(f,j) in c.files" :key="j" :src="f" style="width:80px;height:60px;object-fit:cover;cursor:pointer" @click="previewImg(f)" />
+          <div class="section-title">检查记录</div>
+          <div class="check-list" v-if="cur.checks?.length">
+            <div v-for="(c, i) in cur.checks" :key="i" class="check-card">
+              <div class="check-header">
+                <span class="check-person">{{ c.fPerson }}</span>
+                <span class="check-date">{{ c.cDate }}</span>
+              </div>
+              <div class="check-state">{{ c.cState }}</div>
+              <div v-if="c.files?.length" class="check-files">
+                <img
+                  v-for="(f, j) in c.files"
+                  :key="j"
+                  :src="f"
+                  class="check-img"
+                  @click="previewImg(f)"
+                />
               </div>
             </div>
           </div>
-          <el-empty v-else description="暂无检查记录" :image-size="40" />
+          <el-empty v-else description="暂无检查记录" :image-size="60" />
         </div>
       </div>
     </el-dialog>
@@ -88,40 +255,291 @@
 </template>
 
 <script setup>
-import {ref, onMounted} from 'vue'
-import {listEventClear} from '@/api/hzz/eventclear'
+import { ref, onMounted } from "vue";
+import { listEventClear } from "@/api/hzz/eventclear";
 
-const loading=ref(false),list=ref([]),total=ref(0),pageNum=ref(1),pageSize=ref(10)
-const q=ref({objectType:'',status:'',evType:'',keyWord:''})
-const dlg=ref(false),cur=ref(null)
+const loading = ref(false);
+const list = ref([]);
+const total = ref(0);
+const pageNum = ref(1);
+const pageSize = ref(10);
+const q = ref({ objectType: "", status: "", evType: "", keyWord: "" });
+const dlg = ref(false);
+const cur = ref(null);
 
-const evTypeMap={1:'乱占',2:'乱采',3:'乱堆',4:'乱建',5:'其他'}
-const statusMap={0:'待处理',1:'处理中',2:'完成',3:'完结'}
+const evTypeMap = { 1: "乱占", 2: "乱采", 3: "乱堆", 4: "乱建", 5: "其他" };
+const statusMap = { 0: "待处理", 1: "处理中", 2: "完成", 3: "完结" };
 
-const getList=async()=>{
-  loading.value=true
-  try{const r=await listEventClear({...q.value,pageNum:pageNum.value,pageSize:pageSize.value});list.value=r.rows||[];total.value=r.total||0}
-  finally{loading.value=false}
-}
-const resetQ=()=>{q.value={objectType:'',status:'',evType:'',keyWord:''};getList()}
-const showDetail=(row)=>{
-  cur.value={...row,images:row.images||[],checks:row.checks||[]}
-  dlg.value=true
-}
-const previewImg=(url)=>{window.open(url)}
-onMounted(()=>getList())
+const headerCellStyle = {
+  background: "#f5f7fa",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "14px",
+  textAlign: "center",
+};
+
+const evTypeTag = (type) => {
+  const map = {
+    1: "danger",
+    2: "warning",
+    3: "warning",
+    4: "danger",
+    5: "info",
+  };
+  return map[type] || "info";
+};
+
+const statusTag = (status) => {
+  const map = { 0: "info", 1: "warning", 2: "primary", 3: "success" };
+  return map[status] || "info";
+};
+
+const getList = async () => {
+  loading.value = true;
+  try {
+    const r = await listEventClear({
+      ...q.value,
+      pageNum: pageNum.value,
+      pageSize: pageSize.value,
+    });
+    list.value = r.rows || [];
+    total.value = r.total || 0;
+  } finally {
+    loading.value = false;
+  }
+};
+
+const resetQ = () => {
+  q.value = { objectType: "", status: "", evType: "", keyWord: "" };
+  getList();
+};
+
+const showDetail = (row) => {
+  cur.value = { ...row, images: row.images || [], checks: row.checks || [] };
+  dlg.value = true;
+};
+
+const previewImg = (url) => {
+  window.open(url);
+};
+
+onMounted(() => getList());
 </script>
 
 <style scoped>
-.eventclear-page{padding:16px 20px;height:calc(100vh - 84px);overflow-y:auto;background:#f0f2f5}
-.search-card{margin-bottom:12px;border-radius:8px}
-.detail-grid{display:flex;gap:20px}
-.detail-left{flex:6}
-.detail-right{flex:4;border-left:1px solid #eee;padding-left:16px}
-.detail-right h4{margin:0 0 10px;font-size:14px;color:#333}
-.info-table{width:100%;border-collapse:collapse}
-.info-table td{padding:6px 8px;border:1px solid #e8e8e8;font-size:13px}
-.info-table .lbl{background:#f5f7fa;width:80px;text-align:right;color:#888}
-.check-item{background:#fafbfc;padding:10px;border-radius:6px;margin-bottom:8px}
-.check-item p{margin:4px 0;font-size:13px}
+.eventclear-page {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 16px;
+  gap: 12px;
+  overflow: hidden;
+}
+
+/* 搜索区域 */
+.search-wrapper {
+  background: #fff;
+  padding: 16px 20px;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  flex-shrink: 0;
+}
+.search-form {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+}
+.search-form :deep(.el-form-item) {
+  margin-bottom: 0;
+  margin-right: 10px;
+}
+.search-form :deep(.el-input__wrapper) {
+  border-radius: 6px;
+}
+.search-form :deep(.el-button) {
+  border-radius: 6px;
+}
+
+/* 表格区域 */
+.table-wrapper {
+  flex: 1;
+  background: #fff;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.data-table {
+  flex: 1;
+  border-radius: 6px;
+  overflow: hidden;
+  cursor: pointer;
+}
+.data-table :deep(.el-table__body-wrapper) .el-table__row:hover {
+  background-color: #f0f7ff !important;
+}
+.data-table :deep(.el-tooltip__popper) {
+  max-width: 200px !important;
+}
+.pagination-wrap {
+  padding-top: 12px;
+  flex-shrink: 0;
+}
+
+/* 详情弹窗 */
+.detail-dialog :deep(.el-dialog) {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.detail-dialog :deep(.el-dialog__header) {
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  padding: 16px 24px;
+  margin: 0;
+  text-align: center;
+}
+.detail-dialog :deep(.el-dialog__title) {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+}
+.detail-dialog :deep(.el-dialog__headerbtn) {
+  top: 16px;
+}
+.detail-dialog :deep(.el-dialog__close) {
+  color: #fff;
+  font-size: 18px;
+}
+.detail-dialog :deep(.el-dialog__close:hover) {
+  color: #f0f0f0;
+}
+.detail-dialog :deep(.el-dialog__body) {
+  padding: 20px 24px;
+  max-height: 70vh;
+  overflow-y: auto;
+}
+
+/* 详情布局 */
+.detail-grid {
+  display: flex;
+  gap: 24px;
+}
+.detail-left {
+  flex: 6;
+  min-width: 0;
+}
+.detail-right {
+  flex: 4;
+  min-width: 0;
+  border-left: 1px solid #eee;
+  padding-left: 20px;
+}
+.section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 12px;
+  padding-bottom: 8px;
+  border-bottom: 2px solid #667eea;
+}
+
+/* 信息网格 */
+.info-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 8px 16px;
+}
+.info-item {
+  display: flex;
+  gap: 8px;
+  padding: 8px 10px;
+  background: #fafbfc;
+  border-radius: 6px;
+}
+.info-label {
+  font-size: 12px;
+  color: #999;
+  flex-shrink: 0;
+  min-width: 56px;
+}
+.info-value {
+  font-size: 13px;
+  color: #333;
+  word-break: break-all;
+}
+
+/* 图片轮播 */
+.image-carousel {
+  border-radius: 8px;
+  overflow: hidden;
+}
+.carousel-img {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  cursor: pointer;
+  background: #f5f5f5;
+}
+
+/* 检查记录 */
+.check-list {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+.check-card {
+  background: #fafbfc;
+  border-radius: 8px;
+  padding: 12px;
+  border: 1px solid #f0f0f0;
+  transition: all 0.2s;
+}
+.check-card:hover {
+  border-color: #d6e4ff;
+  background: #f5f9ff;
+}
+.check-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 6px;
+}
+.check-person {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+}
+.check-date {
+  font-size: 12px;
+  color: #999;
+}
+.check-state {
+  font-size: 13px;
+  color: #666;
+  margin-bottom: 8px;
+}
+.check-files {
+  display: flex;
+  gap: 6px;
+  flex-wrap: wrap;
+}
+.check-img {
+  width: 80px;
+  height: 60px;
+  object-fit: cover;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: transform 0.2s;
+}
+.check-img:hover {
+  transform: scale(1.05);
+}
+.empty-block {
+  text-align: center;
+  color: #bbb;
+  font-size: 13px;
+  padding: 30px 0;
+}
 </style>

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

@@ -3,7 +3,7 @@
     <!-- 左侧行政区划树 -->
     <div class="left-tree">
       <div class="tree-title">行政区划</div>
-      <el-input v-model="treeFilter" placeholder="搜索" size="small" clearable style="margin:8px"/>
+      <el-input v-model="treeFilter" placeholder="搜索" size="small" clearable style="padding:8px"/>
       <el-tree
           :data="treeData" :props="treeProps" node-key="adCode"
           :filter-node-method="filterNode" ref="treeRef"

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

@@ -1,52 +1,314 @@
 <template>
-  <div class="app-container">
-    <el-table v-loading="loading" :data="list" border size="small">
-      <el-table-column label="文件名称" prop="fileName" show-overflow-tooltip>
-        <template #default="{row}">
-          <el-link type="primary" @click="showDetail(row)">{{ row.fileName || row.FILE_NAME }}</el-link>
-        </template>
-      </el-table-column>
-      <el-table-column label="文号" prop="wh" width="200">
-        <template #default="{row}">{{ row.wh || row.WH }}</template>
-      </el-table-column>
-      <el-table-column label="出台时间" width="140">
-        <template #default="{row}">{{ fmtDate(row.startDate || row.START_DATE) }}</template>
-      </el-table-column>
-    </el-table>
+  <div class="file-page">
+    <!-- 页面标题 -->
+    <div class="page-header">
+      <h2>相关文件</h2>
+      <span class="file-count">共 {{ list.length }} 个文件</span>
+    </div>
+
+    <!-- 文件列表 -->
+    <div class="file-list" v-loading="loading">
+      <template v-for="(item, idx) in list" :key="idx">
+        <div class="file-item" @click="showDetail(item)">
+          <div class="file-num">{{ idx + 1 }}</div>
+          <div class="file-icon">
+            <svg viewBox="0 0 24 24" width="26" height="26" fill="currentColor">
+              <path
+                d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"
+              />
+            </svg>
+          </div>
+          <div class="file-info">
+            <div class="file-name">{{ item.fileName || item.FILE_NAME }}</div>
+            <div class="file-meta">
+              <span v-if="item.wh || item.WH" class="meta-item">
+                <span class="meta-label">文号</span>
+                <span class="meta-value">{{ item.wh || item.WH }}</span>
+              </span>
+              <span class="meta-item">
+                <span class="meta-label">出台时间</span>
+                <span class="meta-value">{{
+                  fmtDate(item.startDate || item.START_DATE)
+                }}</span>
+              </span>
+            </div>
+          </div>
+          <div class="file-arrow">
+            <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
+              <path
+                d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"
+              />
+            </svg>
+          </div>
+        </div>
+        <div class="file-divider" v-if="idx < list.length - 1"></div>
+      </template>
+
+      <!-- 空状态 -->
+      <div class="empty-state" v-if="!loading && list.length === 0">
+        <svg viewBox="0 0 24 24" width="56" height="56" fill="#dce0e6">
+          <path
+            d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"
+          />
+        </svg>
+        <p class="empty-text">暂无相关文件</p>
+        <p class="empty-hint">文件数据将在后续更新</p>
+      </div>
+    </div>
 
     <!-- 文件详情弹窗 -->
-    <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
+      v-model="detailVisible"
+      :title="detailItem?.fileName || detailItem?.wpName || detailItem?.sysName"
+      width="800px"
+      :close-on-click-modal="false"
+      class="file-dialog"
+    >
+      <i-frame :src="detailItem?.url" style="height: 75vh"></i-frame>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import {onMounted, ref} from 'vue'
-import {listFile} from '@/api/hzz/rule'
-import iFrame from '@/components/iFrame'
+import { onMounted, ref } from "vue";
+import { listFile } from "@/api/hzz/rule";
+import iFrame from "@/components/iFrame";
 
-const loading = ref(false)
-const list = ref([])
-const detailVisible = ref(false)
-const detailItem = ref(null)
+const loading = ref(false);
+const list = ref([]);
+const detailVisible = ref(false);
+const detailItem = ref(null);
 
 const showDetail = (item) => {
-  detailVisible.value = true
-  detailItem.value = item
-}
+  detailVisible.value = true;
+  detailItem.value = item;
+};
 
-const fmtDate = (d) => d ? String(d).substring(0, 10) : '-'
+const fmtDate = (d) => (d ? String(d).substring(0, 10) : "-");
 
 onMounted(async () => {
-  loading.value = true
+  loading.value = true;
   try {
-    // 默认查询太湖流域考核文件
-    const res = await listFile({types: 'xgwj', pageNum: 1, pageSize: 500})
-    list.value = res.rows || []
-  } catch { /* 接口可能未就绪 */
+    const res = await listFile({ types: "xgwj", pageNum: 1, pageSize: 500 });
+    list.value = res.rows || [];
+  } catch {
+    /* 接口可能未就绪 */
   } finally {
-    loading.value = false
+    loading.value = false;
   }
-})
+});
 </script>
+
+<style scoped>
+.file-page {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 16px;
+  overflow: hidden;
+}
+
+/* 标题栏 */
+.page-header {
+  background: #fff;
+  border-radius: 10px;
+  padding: 16px 20px;
+  margin-bottom: 12px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  flex-shrink: 0;
+}
+.page-header h2 {
+  font-size: 17px;
+  font-weight: 600;
+  color: #1a1a2e;
+  margin: 0;
+  display: flex;
+  align-items: center;
+}
+.page-header h2::before {
+  content: "";
+  display: inline-block;
+  width: 3px;
+  height: 18px;
+  background: #409eff;
+  margin-right: 10px;
+  border-radius: 2px;
+}
+.file-count {
+  font-size: 13px;
+  color: #999;
+  background: #f5f7fa;
+  padding: 4px 12px;
+  border-radius: 12px;
+}
+
+/* 文件列表 */
+.file-list {
+  flex: 1;
+  background: #fff;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  padding: 12px 16px;
+  overflow-y: auto;
+}
+
+/* 文件项 */
+.file-item {
+  display: flex;
+  align-items: center;
+  gap: 14px;
+  padding: 14px 8px;
+  cursor: pointer;
+  transition: all 0.2s;
+  border-radius: 8px;
+  position: relative;
+}
+.file-item:hover {
+  background: #f5f9ff;
+}
+.file-item:hover .file-arrow {
+  color: #409eff;
+  transform: translateX(3px);
+}
+
+/* 序号 */
+.file-num {
+  width: 28px;
+  height: 28px;
+  border-radius: 50%;
+  background: #f5f7fa;
+  color: #999;
+  font-size: 12px;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+.file-item:hover .file-num {
+  background: #409eff;
+  color: #fff;
+}
+
+/* 图标 */
+.file-icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 8px;
+  background: #ecf5ff;
+  color: #409eff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+
+/* 文件信息 */
+.file-info {
+  flex: 1;
+  min-width: 0;
+}
+.file-name {
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  margin-bottom: 6px;
+}
+.file-meta {
+  display: flex;
+  gap: 16px;
+}
+.meta-item {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+.meta-label {
+  font-size: 11px;
+  color: #bbb;
+  background: #fafbfc;
+  padding: 1px 6px;
+  border-radius: 3px;
+  border: 1px solid #f0f0f0;
+}
+.meta-value {
+  font-size: 12px;
+  color: #888;
+}
+
+/* 箭头 */
+.file-arrow {
+  color: #d0d0d0;
+  flex-shrink: 0;
+  transition: all 0.2s;
+  display: flex;
+  align-items: center;
+}
+
+/* 分割线 */
+.file-divider {
+  height: 1px;
+  background: linear-gradient(
+    to right,
+    transparent,
+    #e8ecf1 10%,
+    #e8ecf1 90%,
+    transparent
+  );
+  margin: 0 8px;
+}
+
+/* 空状态 */
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 80px 0;
+  gap: 8px;
+}
+.empty-text {
+  color: #bbb;
+  font-size: 15px;
+  margin: 8px 0 0;
+}
+.empty-hint {
+  color: #d0d0d0;
+  font-size: 12px;
+  margin: 0;
+}
+
+/* 弹窗 */
+.file-dialog :deep(.el-dialog) {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.file-dialog :deep(.el-dialog__header) {
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  padding: 14px 20px;
+  margin: 0;
+  text-align: center;
+}
+.file-dialog :deep(.el-dialog__title) {
+  color: #fff;
+  font-size: 15px;
+  font-weight: 600;
+}
+.file-dialog :deep(.el-dialog__headerbtn) {
+  top: 14px;
+}
+.file-dialog :deep(.el-dialog__close) {
+  color: #fff;
+  font-size: 18px;
+}
+.file-dialog :deep(.el-dialog__body) {
+  padding: 0;
+}
+</style>

+ 1 - 1
gw-ui/src/views/hlgl/mcaf/index.vue

@@ -19,7 +19,7 @@
       </el-form>
 
       <el-table :data="list" border size="small" v-loading="loading" @row-click="editRow">
-        <el-table-column type="index" label="#" width="50" />
+        <el-table-column type="index" label="xu" width="50" />
         <el-table-column prop="objectName" label="河段名称" show-overflow-tooltip width="140" />
         <el-table-column prop="adName" label="所属地区" width="100" />
         <el-table-column prop="evType" label="事件类型" width="80" />

+ 556 - 95
gw-ui/src/views/hlgl/riverproject/index.vue

@@ -1,116 +1,577 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" :inline="true" v-show="showSearch">
-      <el-form-item label="项目名称">
-        <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width:200px" @keyup.enter="getList" />
-      </el-form-item>
-      <el-form-item label="建设阶段">
-        <el-input v-model="queryParams.buildStage" placeholder="请输入建设阶段" clearable style="width:200px" @keyup.enter="getList" />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="getList">搜索</el-button>
-        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
-    </el-row>
-
-    <el-table v-loading="loading" :data="projectList" border>
-      <el-table-column label="序号" type="index" width="55" align="center" />
-      <el-table-column label="项目名称" prop="sProjectName" :show-overflow-tooltip="true" min-width="180" />
-      <el-table-column label="建设单位" prop="sDept" :show-overflow-tooltip="true" width="160" />
-      <el-table-column label="法人代表" prop="projectLegalPerson" width="120" />
-      <el-table-column label="联系人" prop="sLinkman" width="100" />
-      <el-table-column label="联系电话" prop="sPhone" width="130" />
-      <el-table-column label="建设阶段" prop="sPBuildStage" width="110" />
-      <el-table-column label="申请日期" prop="sPgoaDate" width="120" />
-      <el-table-column label="项目地点" prop="sLocation" :show-overflow-tooltip="true" width="160" />
-      <el-table-column label="操作" align="center" width="150" fixed="right">
-        <template #default="scope">
-          <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
-          <el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)">修改</el-button>
-          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
+  <div class="river-project">
+    <!-- 搜索区域 + 操作按钮 -->
+    <div class="search-wrapper" v-show="showSearch">
+      <div class="search-row">
+        <el-form :model="queryParams" :inline="true" class="search-form">
+          <el-form-item label="项目名称">
+            <el-input
+              v-model="queryParams.projectName"
+              placeholder="请输入项目名称"
+              clearable
+              style="width: 200px"
+              @keyup.enter="getList"
+            />
+          </el-form-item>
+          <el-form-item label="建设阶段">
+            <el-input
+              v-model="queryParams.buildStage"
+              placeholder="请输入建设阶段"
+              clearable
+              style="width: 200px"
+              @keyup.enter="getList"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="getList"
+              >搜索</el-button
+            >
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="right-actions">
+          <el-button type="primary" icon="Plus" @click="handleAdd"
+            >新增项目</el-button
+          >
+          <right-toolbar
+            v-model:showSearch="showSearch"
+            @queryTable="getList"
+          />
+        </div>
+      </div>
+    </div>
+
+    <!-- 表格区域 -->
+    <div class="table-wrapper">
+      <el-table
+        v-loading="loading"
+        :data="projectList"
+        border
+        stripe
+        :header-cell-style="headerCellStyle"
+        class="data-table"
+      >
+        <el-table-column label="序号" type="index" width="55" align="center" />
+        <el-table-column
+          label="项目名称"
+          prop="sProjectName"
+          :show-overflow-tooltip="true"
+          min-width="180"
+          align="center"
+        />
+        <el-table-column
+          label="建设单位"
+          prop="sDept"
+          :show-overflow-tooltip="true"
+          min-width="150"
+          align="center"
+        />
+        <el-table-column
+          label="法人代表"
+          prop="projectLegalPerson"
+          width="90"
+          align="center"
+        />
+        <el-table-column
+          label="联系人"
+          prop="sLinkman"
+          width="90"
+          align="center"
+        />
+        <el-table-column
+          label="联系电话"
+          prop="sPhone"
+          width="120"
+          align="center"
+        />
+        <el-table-column
+          label="建设阶段"
+          prop="sPBuildStage"
+          width="100"
+          align="center"
+        />
+        <el-table-column
+          label="申请日期"
+          prop="sPgoaDate"
+          width="110"
+          align="center"
+        />
+        <el-table-column
+          label="项目地点"
+          prop="sLocation"
+          :show-overflow-tooltip="true"
+          min-width="160"
+          align="center"
+        />
+        <el-table-column label="操作" align="center" width="220" fixed="right">
+          <template #default="scope">
+            <div class="action-buttons">
+              <el-button
+                link
+                type="primary"
+                icon="View"
+                @click="handleDetail(scope.row)"
+                >详情</el-button
+              >
+              <el-button
+                link
+                type="primary"
+                icon="Edit"
+                @click="handleEdit(scope.row)"
+                >修改</el-button
+              >
+              <el-button
+                link
+                type="danger"
+                icon="Delete"
+                @click="handleDelete(scope.row)"
+                >删除</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="pageNum"
+        v-model:limit="pageSize"
+        @pagination="getList"
+      />
+    </div>
 
     <!-- 编辑对话框 -->
-    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="700px" append-to-body>
-      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
-        <el-row :gutter="20">
-          <el-col :span="12"><el-form-item label="项目名称" prop="sProjectName"><el-input v-model="form.sProjectName" placeholder="请输入项目名称" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="建设单位" prop="sDept"><el-input v-model="form.sDept" placeholder="请输入建设单位" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="法人代表" prop="projectLegalPerson"><el-input v-model="form.projectLegalPerson" placeholder="请输入法人代表" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="联系人" prop="sLinkman"><el-input v-model="form.sLinkman" placeholder="请输入联系人" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="联系电话" prop="sPhone"><el-input v-model="form.sPhone" placeholder="请输入联系电话" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="建设阶段" prop="sPBuildStage"><el-input v-model="form.sPBuildStage" placeholder="请输入建设阶段" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="项目性质" prop="sType"><el-input v-model="form.sType" placeholder="请输入项目性质" /></el-form-item></el-col>
-          <el-col :span="12"><el-form-item label="申请日期" prop="sPgoaDate"><el-date-picker v-model="form.sPgoaDate" type="date" placeholder="选择申请日期" style="width: 100%" /></el-form-item></el-col>
-          <el-col :span="24"><el-form-item label="项目地点" prop="sLocation"><el-input v-model="form.sLocation" placeholder="请输入项目地点" /></el-form-item></el-col>
-        </el-row>
+    <el-dialog
+      :title="dialogTitle"
+      v-model="dialogVisible"
+      width="720px"
+      append-to-body
+      :close-on-click-modal="false"
+      class="form-dialog"
+    >
+      <el-form
+        ref="formRef"
+        :model="form"
+        :rules="rules"
+        label-width="100px"
+        class="project-form"
+      >
+        <div class="form-section">
+          <div class="section-title">基本信息</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <el-form-item label="项目名称" prop="sProjectName">
+                <el-input
+                  v-model="form.sProjectName"
+                  placeholder="请输入项目名称"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="建设单位" prop="sDept">
+                <el-input v-model="form.sDept" placeholder="请输入建设单位" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="法人代表">
+                <el-input
+                  v-model="form.projectLegalPerson"
+                  placeholder="请输入法人代表"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="联系人">
+                <el-input v-model="form.sLinkman" placeholder="请输入联系人" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+
+        <div class="form-section">
+          <div class="section-title">项目信息</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <el-form-item label="联系电话">
+                <el-input v-model="form.sPhone" placeholder="请输入联系电话" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="建设阶段">
+                <el-input
+                  v-model="form.sPBuildStage"
+                  placeholder="请输入建设阶段"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="项目性质">
+                <el-input v-model="form.sType" placeholder="请输入项目性质" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="申请日期">
+                <el-date-picker
+                  v-model="form.sPgoaDate"
+                  type="date"
+                  placeholder="选择申请日期"
+                  style="width: 100%"
+                  format="YYYY-MM-DD"
+                  value-format="YYYY-MM-DD"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+
+        <div class="form-section">
+          <div class="section-title">位置信息</div>
+          <el-row :gutter="20">
+            <el-col :span="24">
+              <el-form-item label="项目地点">
+                <el-input
+                  v-model="form.sLocation"
+                  placeholder="请输入项目地点"
+                  type="textarea"
+                  :rows="2"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
       </el-form>
+
       <template #footer>
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitForm">确定</el-button>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="submitForm" :loading="submitLoading"
+            >确定</el-button
+          >
+        </div>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue'
-import { listProject, addProject, updateProject, delProject } from '@/api/hzz/riverproject'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-const showSearch = ref(true)
-const loading = ref(false)
-const total = ref(0)
-const pageNum = ref(1)
-const pageSize = ref(10)
-const projectList = ref([])
-const queryParams = ref({ projectName: '', buildStage: '' })
-
-const dialogVisible = ref(false)
-const dialogTitle = ref('')
-const formRef = ref(null)
-const form = reactive({})
-const rules = { sProjectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }] }
+import { ref, reactive, onMounted } from "vue";
+import {
+  listProject,
+  addProject,
+  updateProject,
+  delProject,
+} from "@/api/hzz/riverproject";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+const showSearch = ref(true);
+const loading = ref(false);
+const submitLoading = ref(false);
+const total = ref(0);
+const pageNum = ref(1);
+const pageSize = ref(10);
+const projectList = ref([]);
+const queryParams = ref({ projectName: "", buildStage: "" });
+
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const form = reactive({});
+const rules = {
+  sProjectName: [
+    { required: true, message: "项目名称不能为空", trigger: "blur" },
+  ],
+  sDept: [{ required: true, message: "建设单位不能为空", trigger: "blur" }],
+};
+
+const headerCellStyle = {
+  background: "#f5f7fa",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "13px",
+  textAlign: "center",
+};
 
 const getList = async () => {
-  loading.value = true
+  loading.value = true;
   try {
-    const res = await listProject({ ...queryParams.value, pageNum: pageNum.value, pageSize: pageSize.value })
-    projectList.value = res.rows || []
-    total.value = res.total || 0
-  } finally { loading.value = false }
-}
+    const res = await listProject({
+      ...queryParams.value,
+      pageNum: pageNum.value,
+      pageSize: pageSize.value,
+    });
+    projectList.value = res.rows || [];
+    total.value = res.total || 0;
+  } finally {
+    loading.value = false;
+  }
+};
+
+const resetQuery = () => {
+  queryParams.value = { projectName: "", buildStage: "" };
+  getList();
+};
+
+const handleAdd = () => {
+  dialogTitle.value = "新增涉河项目";
+  Object.keys(form).forEach((k) => delete form[k]);
+  dialogVisible.value = true;
+};
 
-const resetQuery = () => { queryParams.value = { projectName: '', buildStage: '' }; getList() }
+const handleEdit = (row) => {
+  dialogTitle.value = "修改涉河项目";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const handleDetail = (row) => {
+  dialogTitle.value = "项目详情";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
 
-const handleAdd = () => { dialogTitle.value = '新增涉河项目'; Object.keys(form).forEach(k => delete form[k]); dialogVisible.value = true }
-const handleEdit = (row) => { dialogTitle.value = '修改涉河项目'; Object.assign(form, row); dialogVisible.value = true }
-const handleDetail = (row) => { dialogTitle.value = '项目详情'; Object.assign(form, row); dialogVisible.value = true }
 const handleDelete = async (row) => {
-  await ElMessageBox.confirm('确认删除该项目?', '提示', { type: 'warning' })
-  await delProject(row.GUID)
-  ElMessage.success('删除成功')
-  getList()
-}
+  try {
+    await ElMessageBox.confirm(
+      `确认删除项目"${row.sProjectName}"?删除后不可恢复。`,
+      "删除确认",
+      {
+        type: "warning",
+        confirmButtonText: "确认删除",
+        cancelButtonText: "取消",
+      },
+    );
+    await delProject(row.GUID);
+    ElMessage.success("删除成功");
+    getList();
+  } catch {
+    // 取消删除
+  }
+};
 
 const submitForm = async () => {
-  if (!formRef.value) return
-  await formRef.value.validate()
-  if (form.GUID) { await updateProject(form) } else { await addProject(form) }
-  ElMessage.success('操作成功')
-  dialogVisible.value = false
-  getList()
-}
+  if (!formRef.value) return;
+  try {
+    await formRef.value.validate();
+  } catch {
+    ElMessage.warning("请完善必填信息");
+    return;
+  }
+
+  submitLoading.value = true;
+  try {
+    if (form.GUID) {
+      await updateProject(form);
+      ElMessage.success("修改成功");
+    } else {
+      await addProject(form);
+      ElMessage.success("新增成功");
+    }
+    dialogVisible.value = false;
+    getList();
+  } catch {
+    ElMessage.error(form.GUID ? "修改失败" : "新增失败");
+  } finally {
+    submitLoading.value = false;
+  }
+};
 
-onMounted(() => getList())
+onMounted(() => getList());
 </script>
+
+<style lang="scss" scoped>
+.river-project {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 16px;
+  gap: 12px;
+  overflow: hidden;
+}
+
+// 搜索区域
+.search-wrapper {
+  background: #fff;
+  padding: 14px 18px;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  flex-shrink: 0;
+
+  .search-row {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    flex-wrap: wrap;
+    gap: 10px;
+  }
+
+  .search-form {
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+
+    :deep(.el-form-item) {
+      margin-bottom: 0;
+      margin-right: 10px;
+    }
+
+    :deep(.el-input__wrapper) {
+      border-radius: 6px;
+    }
+
+    :deep(.el-button) {
+      border-radius: 6px;
+    }
+  }
+
+  .right-actions {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+
+    .el-button {
+      border-radius: 6px;
+      font-weight: 500;
+    }
+  }
+}
+
+// 表格区域
+.table-wrapper {
+  flex: 1;
+  background: #fff;
+  border-radius: 10px;
+  padding: 14px 16px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+
+  .data-table {
+    flex: 1;
+    border-radius: 6px;
+    overflow: hidden;
+
+    :deep(.el-table__body-wrapper) .el-table__row {
+      transition: background-color 0.2s;
+      &:hover {
+        background-color: #f0f7ff !important;
+      }
+    }
+
+    :deep(.el-tooltip__popper) {
+      max-width: 200px !important;
+    }
+  }
+
+  .action-buttons {
+    display: flex;
+    justify-content: center;
+    gap: 12px;
+    padding: 4px 0;
+
+    .el-button {
+      font-size: 13px;
+      padding: 4px 0px;
+    }
+  }
+
+  :deep(.pagination-container) {
+    padding: 12px 0 0;
+    flex-shrink: 0;
+  }
+}
+
+// 对话框
+.form-dialog {
+  :deep(.el-dialog) {
+    border-radius: 12px;
+    overflow: hidden;
+
+    .el-dialog__header {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      padding: 14px 24px;
+      margin: 0;
+      text-align: center;
+
+      .el-dialog__title {
+        color: #fff;
+        font-size: 16px;
+        font-weight: 600;
+      }
+
+      .el-dialog__headerbtn {
+        top: 14px;
+        .el-dialog__close {
+          color: #fff;
+          font-size: 18px;
+          &:hover {
+            color: #f0f0f0;
+          }
+        }
+      }
+    }
+
+    .el-dialog__body {
+      padding: 20px 24px;
+      max-height: 60vh;
+      overflow-y: auto;
+    }
+
+    .el-dialog__footer {
+      padding: 14px 24px;
+      border-top: 1px solid #f0f0f0;
+      text-align: center;
+    }
+  }
+}
+
+// 表单样式
+.project-form {
+  .form-section {
+    margin-bottom: 18px;
+    &:last-child {
+      margin-bottom: 0;
+    }
+
+    .section-title {
+      font-size: 14px;
+      font-weight: 600;
+      color: #333;
+      margin-bottom: 14px;
+      padding-bottom: 8px;
+      border-bottom: 2px solid #667eea;
+      text-align: center;
+    }
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 500;
+    color: #555;
+  }
+
+  :deep(.el-input__wrapper),
+  :deep(.el-textarea__inner) {
+    border-radius: 6px;
+    transition: all 0.2s;
+  }
+
+  :deep(.el-date-editor) .el-input__wrapper {
+    border-radius: 6px;
+  }
+}
+
+// 对话框底部按钮
+.dialog-footer {
+  display: flex;
+  justify-content: center;
+  gap: 12px;
+
+  .el-button {
+    border-radius: 6px;
+    font-weight: 500;
+    padding: 10px 24px;
+  }
+}
+</style>

+ 407 - 87
gw-ui/src/views/hlgl/supervision/index.vue

@@ -1,133 +1,453 @@
 <template>
-  <div class="app-container">
-    <el-table v-loading="loading" :data="supervisionList" border>
-      <el-table-column label="序号" type="index" width="55" align="center"/>
-      <el-table-column label="GUID" prop="crCode" width="100" :show-overflow-tooltip="true"/>
-      <el-table-column label="状态" prop="crState" width="100">
-        <template #default="scope">
-          {{ crStateFilter(scope.row) }}
-        </template>
-      </el-table-column>
-      <el-table-column label="审核信息" prop="crContent" :show-overflow-tooltip="true" min-width="180"/>
-      <el-table-column label="操作" align="center" width="150" fixed="right">
-        <template #default="scope">
-          <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
-          <el-button link type="primary" icon="Check" @click="handleAudit(scope.row)">审核</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize"
-                @pagination="getList"/>
+  <div class="supervision-page">
+    <!-- 表格区域 -->
+    <div class="table-wrapper">
+      <el-table
+        v-loading="loading"
+        :data="supervisionList"
+        border
+        stripe
+        :header-cell-style="headerCellStyle"
+        class="data-table"
+      >
+        <el-table-column label="序号" type="index" width="60" align="center" />
+        <el-table-column
+          label="编号"
+          prop="crCode"
+          width="120"
+          :show-overflow-tooltip="true"
+          align="center"
+        />
+        <el-table-column label="状态" prop="crState" width="100" align="center">
+          <template #default="scope">
+            <el-tag
+              :type="getStateType(scope.row.crState)"
+              size="small"
+              effect="plain"
+            >
+              {{ crStateFilter(scope.row) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="审核信息"
+          prop="crContent"
+          :show-overflow-tooltip="true"
+          min-width="200"
+          align="center"
+        />
+        <el-table-column label="操作" align="center" width="160" fixed="right">
+          <template #default="scope">
+            <div class="action-buttons">
+              <el-button
+                link
+                type="primary"
+                icon="View"
+                @click="handleDetail(scope.row)"
+                >详情</el-button
+              >
+              <el-button
+                link
+                type="primary"
+                icon="Check"
+                @click="handleAudit(scope.row)"
+                >审核</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="pagination-wrap" v-show="total > 0">
+        <pagination
+          :total="total"
+          v-model:page="pageNum"
+          v-model:limit="pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
 
     <!-- 详情对话框 -->
-    <el-dialog title="督导详情" v-model="detailVisible" width="800px" append-to-body>
-      <el-tabs>
-        <el-tab-pane label="附件">
-          <el-table :data="attachments" border size="small">
-            <el-table-column label="GUID" prop="guid" :show-overflow-tooltip="true"/>
-            <el-table-column label="文件名" prop="fileName" :show-overflow-tooltip="true"/>
+    <el-dialog
+      title="督导详情"
+      v-model="detailVisible"
+      width="850px"
+      append-to-body
+      :close-on-click-modal="false"
+      class="detail-dialog"
+    >
+      <el-tabs v-model="activeTab" class="detail-tabs">
+        <el-tab-pane label="附件" name="attachments">
+          <el-table
+            :data="attachments"
+            border
+            stripe
+            size="small"
+            :header-cell-style="detailHeaderStyle"
+          >
+            <el-table-column
+              label="GUID"
+              prop="guid"
+              :show-overflow-tooltip="true"
+              min-width="200"
+              align="center"
+            />
+            <el-table-column
+              label="文件名"
+              prop="fileName"
+              :show-overflow-tooltip="true"
+              min-width="300"
+              align="center"
+            />
           </el-table>
+          <div class="empty-tab" v-if="attachments.length === 0">
+            暂无附件数据
+          </div>
         </el-tab-pane>
-        <el-tab-pane label="分工">
-          <el-table :data="divisions" border size="small">
-            <el-table-column label="GUID" prop="guid" :show-overflow-tooltip="true"/>
+        <el-tab-pane label="分工" name="divisions">
+          <el-table
+            :data="divisions"
+            border
+            stripe
+            size="small"
+            :header-cell-style="detailHeaderStyle"
+          >
+            <el-table-column
+              label="GUID"
+              prop="guid"
+              :show-overflow-tooltip="true"
+              align="center"
+            />
           </el-table>
+          <div class="empty-tab" v-if="divisions.length === 0">
+            暂无分工数据
+          </div>
         </el-tab-pane>
-        <el-tab-pane label="结果">
-          <el-table :data="results" border size="small">
-            <el-table-column label="GUID" prop="guid" :show-overflow-tooltip="true"/>
+        <el-tab-pane label="结果" name="results">
+          <el-table
+            :data="results"
+            border
+            stripe
+            size="small"
+            :header-cell-style="detailHeaderStyle"
+          >
+            <el-table-column
+              label="GUID"
+              prop="guid"
+              :show-overflow-tooltip="true"
+              align="center"
+            />
           </el-table>
+          <div class="empty-tab" v-if="results.length === 0">暂无结果数据</div>
         </el-tab-pane>
       </el-tabs>
     </el-dialog>
 
     <!-- 审核对话框 -->
-    <el-dialog title="审核督导" v-model="auditVisible" width="500px" append-to-body>
-      <el-form :model="auditForm" label-width="80px">
+    <el-dialog
+      title="审核督导"
+      v-model="auditVisible"
+      width="520px"
+      append-to-body
+      :close-on-click-modal="false"
+      class="audit-dialog"
+    >
+      <el-form :model="auditForm" label-width="80px" class="audit-form">
         <el-form-item label="审核结果">
-          <el-select v-model="auditForm.audit" style="width:100%">
-            <el-option label="通过" value="1"/>
-            <el-option label="不通过" value="0"/>
+          <el-select v-model="auditForm.audit" style="width: 100%">
+            <el-option label="通过" value="1" />
+            <el-option label="不通过" value="0" />
           </el-select>
         </el-form-item>
         <el-form-item label="审核意见">
-          <el-input v-model="auditForm.auditInfo" type="textarea" :rows="3"/>
+          <el-input
+            v-model="auditForm.auditInfo"
+            type="textarea"
+            :rows="4"
+            placeholder="请输入审核意见"
+          />
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button @click="auditVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitAudit">确定</el-button>
+        <div class="dialog-footer">
+          <el-button @click="auditVisible = false">取消</el-button>
+          <el-button
+            type="primary"
+            @click="submitAudit"
+            :loading="submitLoading"
+            >确定</el-button
+          >
+        </div>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import {onMounted, reactive, ref} from 'vue'
-import {auditSupervision, getSupervisionDetail, listSupervision} from '@/api/hzz/supervision'
-import {ElMessage} from 'element-plus'
+import { onMounted, reactive, ref } from "vue";
+import {
+  auditSupervision,
+  getSupervisionDetail,
+  listSupervision,
+} from "@/api/hzz/supervision";
+import { ElMessage } from "element-plus";
 
-const loading = ref(false)
-const total = ref(0)
-const pageNum = ref(1)
-const pageSize = ref(10)
-const supervisionList = ref([])
+const loading = ref(false);
+const submitLoading = ref(false);
+const total = ref(0);
+const pageNum = ref(1);
+const pageSize = ref(10);
+const supervisionList = ref([]);
+const activeTab = ref("attachments");
 
-const detailVisible = ref(false)
-const attachments = ref([])
-const divisions = ref([])
-const results = ref([])
+const detailVisible = ref(false);
+const attachments = ref([]);
+const divisions = ref([]);
+const results = ref([]);
 
-const auditVisible = ref(false)
-const auditForm = reactive({guid: '', audit: '1', auditInfo: ''})
+const auditVisible = ref(false);
+const auditForm = reactive({ guid: "", audit: "1", auditInfo: "" });
+
+const headerCellStyle = {
+  background: "#f5f7fa",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "14px",
+  textAlign: "center",
+};
+
+const detailHeaderStyle = {
+  background: "#fafbfc",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "13px",
+  textAlign: "center",
+};
+
+const getStateType = (state) => {
+  const map = {
+    5: "warning",
+    6: "info",
+    7: "warning",
+    8: "danger",
+    9: "success",
+  };
+  return map[state] || "info";
+};
 
 const getList = async () => {
-  loading.value = true
+  loading.value = true;
   try {
-    const res = await listSupervision({pageNum: pageNum.value, pageSize: pageSize.value})
-    supervisionList.value = res.rows || []
-    total.value = res.total || 0
+    const res = await listSupervision({
+      pageNum: pageNum.value,
+      pageSize: pageSize.value,
+    });
+    supervisionList.value = res.rows || [];
+    total.value = res.total || 0;
   } finally {
-    loading.value = false
+    loading.value = false;
   }
-}
+};
 
 const handleDetail = async (row) => {
-  const res = await getSupervisionDetail(row.GUID)
-  attachments.value = res.data?.attachments || []
-  divisions.value = res.data?.divisions || []
-  results.value = res.data?.results || []
-  detailVisible.value = true
-}
+  try {
+    const res = await getSupervisionDetail(row.GUID);
+    attachments.value = res.data?.attachments || [];
+    divisions.value = res.data?.divisions || [];
+    results.value = res.data?.results || [];
+    activeTab.value = "attachments";
+    detailVisible.value = true;
+  } catch {
+    ElMessage.error("获取详情失败");
+  }
+};
 
 const handleAudit = (row) => {
-  auditForm.guid = row.GUID
-  auditForm.audit = '1'
-  auditForm.auditInfo = ''
-  auditVisible.value = true
-}
+  auditForm.guid = row.GUID;
+  auditForm.audit = "1";
+  auditForm.auditInfo = "";
+  auditVisible.value = true;
+};
 
 const crStateFilter = (row) => {
   switch (row.crState) {
-    case '5':
-      return '进行中'
-    case '6':
-      return '未提交'
-    case '7':
-      return '未审批'
-    case '8':
-      return '已退回'
-    case '9':
-      return '已通过'
+    case "5":
+      return "进行中";
+    case "6":
+      return "未提交";
+    case "7":
+      return "未审批";
+    case "8":
+      return "已退回";
+    case "9":
+      return "已通过";
+    default:
+      return "未知";
   }
-}
+};
 
 const submitAudit = async () => {
-  await auditSupervision(auditForm.guid, auditForm.audit, auditForm.auditInfo)
-  ElMessage.success('审核完成')
-  auditVisible.value = false
-  getList()
-}
+  submitLoading.value = true;
+  try {
+    await auditSupervision(
+      auditForm.guid,
+      auditForm.audit,
+      auditForm.auditInfo,
+    );
+    ElMessage.success("审核完成");
+    auditVisible.value = false;
+    getList();
+  } catch {
+    ElMessage.error("审核失败");
+  } finally {
+    submitLoading.value = false;
+  }
+};
 
-onMounted(() => getList())
+onMounted(() => getList());
 </script>
+
+<style scoped>
+.supervision-page {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 16px;
+  overflow: hidden;
+}
+
+/* 表格区域 */
+.table-wrapper {
+  flex: 1;
+  background: #fff;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.data-table {
+  flex: 1;
+  border-radius: 6px;
+  overflow: hidden;
+}
+.data-table :deep(.el-table__body-wrapper) .el-table__row:hover {
+  background-color: #f0f7ff !important;
+}
+.data-table :deep(.el-tooltip__popper) {
+  max-width: 200px !important;
+}
+.action-buttons {
+  display: flex;
+  justify-content: center;
+  gap: 8px;
+}
+.action-buttons .el-button {
+  font-size: 13px;
+  padding: 4px 10px;
+}
+.pagination-wrap {
+  padding-top: 12px;
+  flex-shrink: 0;
+}
+
+/* 详情对话框 */
+.detail-dialog :deep(.el-dialog) {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.detail-dialog :deep(.el-dialog__header) {
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  padding: 16px 24px;
+  margin: 0;
+  text-align: center;
+}
+.detail-dialog :deep(.el-dialog__title) {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+}
+.detail-dialog :deep(.el-dialog__headerbtn) {
+  top: 16px;
+}
+.detail-dialog :deep(.el-dialog__close) {
+  color: #fff;
+  font-size: 18px;
+}
+.detail-dialog :deep(.el-dialog__close:hover) {
+  color: #f0f0f0;
+}
+.detail-dialog :deep(.el-dialog__body) {
+  padding: 20px 24px;
+}
+.detail-tabs :deep(.el-tabs__header) {
+  margin-bottom: 16px;
+}
+.detail-tabs :deep(.el-tabs__item) {
+  font-size: 14px;
+  font-weight: 500;
+}
+.empty-tab {
+  text-align: center;
+  color: #bbb;
+  font-size: 13px;
+  padding: 40px 0;
+}
+
+/* 审核对话框 */
+.audit-dialog :deep(.el-dialog) {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.audit-dialog :deep(.el-dialog__header) {
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  padding: 16px 24px;
+  margin: 0;
+  text-align: center;
+}
+.audit-dialog :deep(.el-dialog__title) {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+}
+.audit-dialog :deep(.el-dialog__headerbtn) {
+  top: 16px;
+}
+.audit-dialog :deep(.el-dialog__close) {
+  color: #fff;
+  font-size: 18px;
+}
+.audit-dialog :deep(.el-dialog__close:hover) {
+  color: #f0f0f0;
+}
+.audit-dialog :deep(.el-dialog__body) {
+  padding: 24px;
+}
+.audit-dialog :deep(.el-dialog__footer) {
+  padding: 16px 24px;
+  border-top: 1px solid #f0f0f0;
+  text-align: center;
+}
+.audit-form :deep(.el-form-item__label) {
+  font-weight: 500;
+  color: #555;
+}
+.audit-form :deep(.el-input__wrapper) {
+  border-radius: 6px;
+}
+.audit-form :deep(.el-textarea__inner) {
+  border-radius: 6px;
+}
+.dialog-footer {
+  display: flex;
+  justify-content: center;
+  gap: 12px;
+}
+.dialog-footer .el-button {
+  border-radius: 6px;
+  font-weight: 500;
+  padding: 10px 24px;
+}
+</style>

+ 423 - 121
gw-ui/src/views/hlgl/syaxgl/index.vue

@@ -1,162 +1,464 @@
 <template>
   <div class="syax-page" v-loading="loading">
-    <!-- 标题 -->
-    <div class="page-header">
-      <h1>水域岸线功能区统计</h1>
-    </div>
-
     <!-- 数据总览卡片 -->
     <div class="overview-cards">
-      <div class="card" v-for="rv in overview" :key="rv.name">
-        <div class="card-name">{{ rv.name }}</div>
-        <div class="card-value">{{ rv.total }} <span class="card-unit">km</span></div>
-        <div class="card-sub">{{ rv.zones }} 个功能区</div>
+      <div class="card" v-for="(rv, idx) in overview" :key="rv.name">
+        <div class="card-icon" :class="'icon-' + idx">
+          <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor">
+            <path
+              d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"
+            />
+          </svg>
+        </div>
+        <div class="card-info">
+          <div class="card-name">{{ rv.name }}</div>
+          <div class="card-stats">
+            <div class="stat-item">
+              <span class="stat-value">{{ rv.total }}</span>
+              <span class="stat-unit">km</span>
+            </div>
+            <div class="stat-divider"></div>
+            <div class="stat-item">
+              <span class="stat-value">{{ rv.zones }}</span>
+              <span class="stat-unit">个功能区</span>
+            </div>
+          </div>
+        </div>
       </div>
     </div>
 
-    <!-- 详细表格 + 图表区 -->
-    <div class="content-grid">
-      <!-- 表格 -->
-      <div class="grid-left">
-        <el-card shadow="never">
-          <template #header>岸线数据明细</template>
-          <el-table :data="tableData" border size="small" :span-method="spanMethod">
-            <el-table-column label="河(湖)名称" prop="river" width="90" />
-            <el-table-column label="总长度(km)" prop="lineLeng" width="100" />
-            <el-table-column label="功能区" prop="zone" width="100" />
-            <el-table-column label="功能区长度(km)" prop="zoneLeng" />
-            <el-table-column label="功能区数量" prop="zoneCount" width="100" />
+    <!-- 内容区域:表格 + 图表 -->
+    <div class="content-area">
+      <!-- 左侧表格 -->
+      <div class="table-section">
+        <div class="section-title">岸线数据明细</div>
+        <div class="table-body">
+          <el-table
+            :data="tableData"
+            border
+            stripe
+            :span-method="spanMethod"
+            :header-cell-style="tableHeaderStyle"
+            height="100%"
+          >
+            <el-table-column
+              label="河(湖)名称"
+              prop="river"
+              width="100"
+              align="center"
+            />
+            <el-table-column
+              label="总长度(km)"
+              prop="lineLeng"
+              width="110"
+              align="center"
+            />
+            <el-table-column
+              label="功能区"
+              prop="zone"
+              width="110"
+              align="center"
+            />
+            <el-table-column
+              label="功能区长度(km)"
+              prop="zoneLeng"
+              align="center"
+            />
+            <el-table-column
+              label="功能区数量"
+              prop="zoneCount"
+              width="110"
+              align="center"
+            />
           </el-table>
-        </el-card>
+        </div>
       </div>
 
-      <!-- 图表 -->
-      <div class="grid-right">
-        <el-card shadow="never">
-          <template #header>太湖 — 功能区分布</template>
-          <div class="chart-row"><div ref="thBar" class="chart-box" /><div ref="thPie" class="chart-box" /></div>
-        </el-card>
-        <el-card shadow="never">
-          <template #header>望虞河 — 功能区分布</template>
-          <div class="chart-row"><div ref="wyBar" class="chart-box" /><div ref="wyPie" class="chart-box" /></div>
-        </el-card>
-        <el-card shadow="never">
-          <template #header>太浦河 — 功能区分布</template>
-          <div class="chart-row"><div ref="tpBar" class="chart-box" /><div ref="tpPie" class="chart-box" /></div>
-        </el-card>
+      <!-- 右侧图表 -->
+      <div class="chart-area">
+        <div class="chart-card" v-for="rv in rivers" :key="rv.key">
+          <div class="chart-title">{{ rv.name }} 功能区分布</div>
+          <div class="chart-row">
+            <div class="chart-panel">
+              <div class="chart-label">柱状图</div>
+              <div
+                :ref="(el) => (refMap[rv.key + 'Bar'] = el)"
+                class="chart-box"
+              />
+            </div>
+            <div class="chart-panel">
+              <div class="chart-label">饼图</div>
+              <div
+                :ref="(el) => (refMap[rv.key + 'Pie'] = el)"
+                class="chart-box"
+              />
+            </div>
+          </div>
+        </div>
       </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref, onMounted, nextTick } from 'vue'
-import { getShorelineStats } from '@/api/hzz/shoreline'
-import * as echarts from 'echarts'
+import { ref, onMounted, nextTick, reactive } from "vue";
+import { getShorelineStats } from "@/api/hzz/shoreline";
+import * as echarts from "echarts";
 
-const loading = ref(false)
-const rawData = ref([])
-const thBar = ref(null), thPie = ref(null), wyBar = ref(null), wyPie = ref(null), tpBar = ref(null), tpPie = ref(null)
-const refMap = { thBar, thPie, wyBar, wyPie, tpBar, tpPie }
+const loading = ref(false);
+const rawData = ref([]);
+const refMap = reactive({});
+const tableHeaderStyle = {
+  background: "#f5f7fa",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "13px",
+  textAlign: "center",
+};
 
-const zones = ['保护区', '保留区', '控制利用区']
-const tableData = ref([])
-const overview = ref([])
+const zones = ["保护区", "保留区", "控制利用区"];
+const tableData = ref([]);
+const overview = ref([]);
 
 const spanMethod = ({ columnIndex, rowIndex }) => {
   if (columnIndex <= 1) {
-    const rows = tableData.value
-    for (let i = rowIndex - 1; i >= 0; i--) { if (rows[i].river === rows[rowIndex].river) return { rowspan: 0, colspan: 1 } }
-    let cnt = 1; for (let i = rowIndex + 1; i < rows.length && rows[i].river === rows[rowIndex].river; i++) cnt++
-    return { rowspan: cnt, colspan: 1 }
+    const rows = tableData.value;
+    for (let i = rowIndex - 1; i >= 0; i--) {
+      if (rows[i].river === rows[rowIndex].river)
+        return { rowspan: 0, colspan: 1 };
+    }
+    let cnt = 1;
+    for (
+      let i = rowIndex + 1;
+      i < rows.length && rows[i].river === rows[rowIndex].river;
+      i++
+    )
+      cnt++;
+    return { rowspan: cnt, colspan: 1 };
   }
-}
+};
 
 const rivers = [
-  { name: '太湖', key: 'th', indices: [0,1,2], chartRefs: { bar: 'thBar', pie: 'thPie' } },
-  { name: '望虞河', key: 'wy', indices: [3,4,5], chartRefs: { bar: 'wyBar', pie: 'wyPie' } },
-  { name: '太浦河', key: 'tp', indices: [6,8], chartRefs: { bar: 'tpBar', pie: 'tpPie' } },
-]
+  { name: "太湖", key: "th", indices: [0, 1, 2] },
+  { name: "望虞河", key: "wy", indices: [3, 4, 5] },
+  { name: "太浦河", key: "tp", indices: [6, 8] },
+];
 
 const buildTable = () => {
-  const d = rawData.value
+  const d = rawData.value;
   tableData.value = [
-    { river: '太湖', lineLeng: d[0]?.lineLeng || '-', zone: '保护区', zoneLeng: d[0]?.lineGn || '-', zoneCount: d[0]?.count || '-' },
-    { river: '太湖', lineLeng: '', zone: '保留区', zoneLeng: d[1]?.lineGn || '-', zoneCount: d[1]?.count || '-' },
-    { river: '太湖', lineLeng: '', zone: '控制利用区', zoneLeng: d[2]?.lineGn || '-', zoneCount: d[2]?.count || '-' },
-    { river: '望虞河', lineLeng: d[3]?.lineLeng || '-', zone: '保护区', zoneLeng: d[3]?.lineGn || '-', zoneCount: d[3]?.count || '-' },
-    { river: '望虞河', lineLeng: '', zone: '保留区', zoneLeng: d[4]?.lineGn || '-', zoneCount: d[4]?.count || '-' },
-    { river: '望虞河', lineLeng: '', zone: '控制利用区', zoneLeng: d[5]?.lineGn || '-', zoneCount: d[5]?.count || '-' },
-    { river: '太浦河', lineLeng: d[6]?.lineLeng || '-', zone: '保护区', zoneLeng: d[6]?.lineGn || '-', zoneCount: d[6]?.count || '-' },
-    { river: '太浦河', lineLeng: '', zone: '控制利用区', zoneLeng: d[8]?.lineGn || '-', zoneCount: d[8]?.count || '-' },
-  ]
+    {
+      river: "太湖",
+      lineLeng: d[0]?.lineLeng || "-",
+      zone: "保护区",
+      zoneLeng: d[0]?.lineGn || "-",
+      zoneCount: d[0]?.count || "-",
+    },
+    {
+      river: "太湖",
+      lineLeng: "",
+      zone: "保留区",
+      zoneLeng: d[1]?.lineGn || "-",
+      zoneCount: d[1]?.count || "-",
+    },
+    {
+      river: "太湖",
+      lineLeng: "",
+      zone: "控制利用区",
+      zoneLeng: d[2]?.lineGn || "-",
+      zoneCount: d[2]?.count || "-",
+    },
+    {
+      river: "望虞河",
+      lineLeng: d[3]?.lineLeng || "-",
+      zone: "保护区",
+      zoneLeng: d[3]?.lineGn || "-",
+      zoneCount: d[3]?.count || "-",
+    },
+    {
+      river: "望虞河",
+      lineLeng: "",
+      zone: "保留区",
+      zoneLeng: d[4]?.lineGn || "-",
+      zoneCount: d[4]?.count || "-",
+    },
+    {
+      river: "望虞河",
+      lineLeng: "",
+      zone: "控制利用区",
+      zoneLeng: d[5]?.lineGn || "-",
+      zoneCount: d[5]?.count || "-",
+    },
+    {
+      river: "太浦河",
+      lineLeng: d[6]?.lineLeng || "-",
+      zone: "保护区",
+      zoneLeng: d[6]?.lineGn || "-",
+      zoneCount: d[6]?.count || "-",
+    },
+    {
+      river: "太浦河",
+      lineLeng: "",
+      zone: "控制利用区",
+      zoneLeng: d[8]?.lineGn || "-",
+      zoneCount: d[8]?.count || "-",
+    },
+  ];
   overview.value = [
-    { name: '太湖', total: d[0]?.lineLeng || '-', zones: 3 },
-    { name: '望虞河', total: d[3]?.lineLeng || '-', zones: 3 },
-    { name: '太浦河', total: d[6]?.lineLeng || '-', zones: 2 },
-  ]
-}
+    { name: "太湖", total: d[0]?.lineLeng || "-", zones: 3 },
+    { name: "望虞河", total: d[3]?.lineLeng || "-", zones: 3 },
+    { name: "太浦河", total: d[6]?.lineLeng || "-", zones: 2 },
+  ];
+};
 
 const renderCharts = () => {
-  const d = rawData.value
+  const d = rawData.value;
   nextTick(() => {
-    rivers.forEach(rv => {
-      const vals = rv.indices.map(i => Number(d[i]?.lineGn || 0))
-      const names = rv.indices.map(i => zones[i === 8 ? 2 : i === 6 && rv.key === 'tp' ? 0 : rv.indices.indexOf(i)])
-      const labels = rv.key === 'tp' ? ['保护区', '控制利用区'] : zones
-
-      const makeChart = (r, type) => {
-        const el = refMap[rv.key + (type === 'bar' ? 'Bar' : 'Pie')].value
-        if (!el) return
-        const c = echarts.init(el)
-        c.setOption(type === 'bar' ? {
-          grid: { top: 20, bottom: 30, left: 40, right: 20 },
-          xAxis: { type: 'category', data: labels }, yAxis: { type: 'value', name: 'km' },
-          series: [{ type: 'bar', data: vals, itemStyle: { color: '#409EFF' }, barMaxWidth: 50 }]
-        } : {
-          series: [{ type: 'pie', radius: ['45%','70%'], center: ['50%','55%'], data: labels.map((n,i)=>({name:n,value:vals[i]})), label: { formatter: '{b}\n{d}%', fontSize: 12 } }]
-        })
-        window.addEventListener('resize', () => c.resize())
+    rivers.forEach((rv) => {
+      const vals = rv.indices.map((i) => Number(d[i]?.lineGn || 0));
+      const labels = rv.key === "tp" ? ["保护区", "控制利用区"] : zones;
+      const colors = ["#409EFF", "#67C23A", "#E6A23C"];
+
+      const barEl = refMap[rv.key + "Bar"];
+      if (barEl) {
+        const barChart = echarts.init(barEl);
+        barChart.setOption({
+          tooltip: { trigger: "axis" },
+          grid: { top: 10, bottom: 20, left: 42, right: 12 },
+          xAxis: {
+            type: "category",
+            data: labels,
+            axisLabel: { fontSize: 10 },
+          },
+          yAxis: { type: "value", name: "km", nameTextStyle: { fontSize: 10 } },
+          series: [
+            {
+              type: "bar",
+              data: vals.map((v, i) => ({
+                value: v,
+                itemStyle: { color: colors[i], borderRadius: [3, 3, 0, 0] },
+              })),
+              barMaxWidth: 35,
+              label: {
+                show: true,
+                position: "top",
+                fontSize: 10,
+                color: "#666",
+              },
+            },
+          ],
+        });
+        window.addEventListener("resize", () => barChart.resize());
       }
-      makeChart(rv, 'bar')
-      makeChart(rv, 'pie')
-    })
-  })
-}
+
+      const pieEl = refMap[rv.key + "Pie"];
+      if (pieEl) {
+        const pieChart = echarts.init(pieEl);
+        pieChart.setOption({
+          tooltip: { trigger: "item" },
+          legend: {
+            bottom: 2,
+            textStyle: { fontSize: 10 },
+            itemWidth: 8,
+            itemHeight: 8,
+          },
+          series: [
+            {
+              type: "pie",
+              radius: ["52%", "72%"],
+              center: ["50%", "43%"],
+              data: labels.map((n, i) => ({
+                name: n,
+                value: vals[i],
+                itemStyle: { color: colors[i] },
+              })),
+              label: { formatter: "{b}\n{d}%", fontSize: 10 },
+            },
+          ],
+        });
+        window.addEventListener("resize", () => pieChart.resize());
+      }
+    });
+  });
+};
 
 onMounted(async () => {
-  loading.value = true
+  loading.value = true;
   try {
-    const res = await getShorelineStats()
-    rawData.value = res.data || []
-    buildTable()
-    renderCharts()
-  } finally { loading.value = false }
-})
+    const res = await getShorelineStats();
+    rawData.value = res.data || [];
+    buildTable();
+    renderCharts();
+  } finally {
+    loading.value = false;
+  }
+});
 </script>
 
 <style scoped>
-.syax-page { padding: 20px 24px; overflow-y: auto; height: calc(100vh - 84px); background: #f0f2f5; }
-.page-header { margin-bottom: 20px; }
-.page-header h1 { font-size: 20px; color: #1a1a2e; font-weight: 600; margin: 0; }
-.page-header h1::before { content: ''; display: inline-block; width: 4px; height: 20px; background: #409EFF; margin-right: 10px; vertical-align: middle; border-radius: 2px; }
+.syax-page {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 12px;
+  gap: 10px;
+  overflow: hidden;
+}
 
 /* 总览卡片 */
-.overview-cards { display: flex; gap: 16px; margin-bottom: 20px; }
-.card { flex: 1; background: #fff; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,.06); }
-.card-name { font-size: 14px; color: #666; margin-bottom: 8px; }
-.card-value { font-size: 28px; font-weight: 700; color: #0d4b80; }
-.card-unit { font-size: 13px; font-weight: 400; color: #999; }
-.card-sub { font-size: 12px; color: #999; margin-top: 4px; }
-
-/* 内容区 */
-.content-grid { display: flex; gap: 16px; align-items: flex-start; }
-.grid-left { width: 520px; flex-shrink: 0; }
-.grid-right { flex: 1; display: flex; flex-direction: column; gap: 16px; min-width: 0; }
-
-.chart-row { display: flex; gap: 12px; }
-.chart-box { flex: 1; height: 240px; }
-
-:deep(.el-card__header) { font-size: 14px; font-weight: 600; color: #333; padding: 12px 16px; }
-:deep(.el-card__body) { padding: 12px 16px; }
+.overview-cards {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 10px;
+  flex-shrink: 0;
+}
+.card {
+  background: #fff;
+  border-radius: 8px;
+  padding: 14px 18px;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+}
+.card-icon {
+  width: 38px;
+  height: 38px;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+.icon-0 {
+  background: #ecf5ff;
+  color: #409eff;
+}
+.icon-1 {
+  background: #f0f9eb;
+  color: #67c23a;
+}
+.icon-2 {
+  background: #fdf6ec;
+  color: #e6a23c;
+}
+
+.card-info {
+  flex: 1;
+  min-width: 0;
+}
+.card-name {
+  font-size: 12px;
+  color: #666;
+  margin-bottom: 4px;
+}
+.card-stats {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+.stat-item {
+  display: flex;
+  align-items: baseline;
+  gap: 2px;
+}
+.stat-value {
+  font-size: 22px;
+  font-weight: 700;
+  color: #0d4b80;
+  line-height: 1;
+}
+.stat-unit {
+  font-size: 11px;
+  color: #999;
+}
+.stat-divider {
+  width: 1px;
+  height: 18px;
+  background: #e8e8e8;
+}
+
+/* 内容区域 */
+.content-area {
+  flex: 1;
+  display: flex;
+  gap: 10px;
+  min-height: 0;
+}
+
+/* 左侧表格 */
+.table-section {
+  width: 540px;
+  flex-shrink: 0;
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+  padding: 12px 16px 0;
+  flex-shrink: 0;
+}
+.table-body {
+  flex: 1;
+  padding: 10px 12px 12px;
+  overflow: hidden;
+}
+
+/* 右侧图表 */
+.chart-area {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  min-width: 0;
+}
+.chart-card {
+  flex: 1;
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.chart-title {
+  font-size: 13px;
+  font-weight: 600;
+  color: #333;
+  padding: 10px 14px 0;
+  flex-shrink: 0;
+}
+.chart-row {
+  flex: 1;
+  display: flex;
+  gap: 8px;
+  padding: 8px 12px 10px;
+  min-height: 0;
+}
+.chart-panel {
+  flex: 1;
+  background: #fafbfc;
+  border-radius: 6px;
+  padding: 6px 6px 2px;
+  display: flex;
+  flex-direction: column;
+  min-width: 0;
+}
+.chart-label {
+  font-size: 10px;
+  color: #bbb;
+  text-align: center;
+  flex-shrink: 0;
+}
+.chart-box {
+  flex: 1;
+  min-height: 0;
+}
 </style>

+ 473 - 84
gw-ui/src/views/hlgl/wps/index.vue

@@ -1,102 +1,491 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" :inline="true" v-show="showSearch">
-      <el-form-item label="项目名称">
-        <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width:200px" @keyup.enter="getList" />
-      </el-form-item>
-      <el-form-item label="开始时间">
-        <el-date-picker v-model="queryParams.startDate" type="date" placeholder="选择开始时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:200px" />
-      </el-form-item>
-      <el-form-item label="结束时间">
-        <el-date-picker v-model="queryParams.endDate" type="date" placeholder="选择结束时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:200px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="getList">搜索</el-button>
-        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd">新增督查</el-button></el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
-    </el-row>
-
-    <el-table v-loading="loading" :data="wpsList" border>
-      <el-table-column label="序号" type="index" width="55" align="center" />
-      <el-table-column label="项目名称" prop="sProjectName" :show-overflow-tooltip="true" min-width="180" />
-      <el-table-column label="督察情况" prop="supervision" :show-overflow-tooltip="true" min-width="200" />
-      <el-table-column label="开始时间" prop="startDate" width="160" />
-      <el-table-column label="结束时间" prop="endDate" width="160" />
-      <el-table-column label="操作" align="center" width="150" fixed="right">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)">修改</el-button>
-          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" @pagination="getList" />
+  <div class="supervise-page">
+    <!-- 搜索区域 + 操作按钮 -->
+    <div class="search-wrapper" v-show="showSearch">
+      <div class="search-row">
+        <el-form :model="queryParams" :inline="true" class="search-form">
+          <el-form-item label="项目名称">
+            <el-input
+              v-model="queryParams.projectName"
+              placeholder="请输入项目名称"
+              clearable
+              style="width: 160px"
+              @keyup.enter="getList"
+            />
+          </el-form-item>
+          <el-form-item label="开始时间">
+            <el-date-picker
+              v-model="queryParams.startDate"
+              type="date"
+              placeholder="开始时间"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              style="width: 150px"
+            />
+          </el-form-item>
+          <el-form-item label="结束时间">
+            <el-date-picker
+              v-model="queryParams.endDate"
+              type="date"
+              placeholder="结束时间"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              style="width: 150px"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="getList"
+              >搜索</el-button
+            >
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="right-actions">
+          <el-button type="primary" plain icon="Plus" @click="handleAdd"
+            >新增督查</el-button
+          >
+          <right-toolbar
+            v-model:showSearch="showSearch"
+            @queryTable="getList"
+          />
+        </div>
+      </div>
+    </div>
+
+    <!-- 表格区域 -->
+    <div class="table-wrapper">
+      <el-table
+        v-loading="loading"
+        :data="wpsList"
+        border
+        stripe
+        :header-cell-style="headerCellStyle"
+        class="data-table"
+      >
+        <el-table-column label="序号" type="index" width="60" align="center" />
+        <el-table-column
+          label="项目名称"
+          prop="sProjectName"
+          :show-overflow-tooltip="true"
+          min-width="160"
+          align="center"
+        />
+        <el-table-column
+          label="督察情况"
+          prop="supervision"
+          :show-overflow-tooltip="true"
+          min-width="180"
+          align="center"
+        />
+        <el-table-column
+          label="开始时间"
+          prop="startDate"
+          width="160"
+          align="center"
+        />
+        <el-table-column
+          label="结束时间"
+          prop="endDate"
+          width="160"
+          align="center"
+        />
+        <el-table-column label="操作" align="center" width="160" fixed="right">
+          <template #default="scope">
+            <div class="action-buttons">
+              <el-button
+                link
+                type="primary"
+                icon="Edit"
+                @click="handleEdit(scope.row)"
+                >修改</el-button
+              >
+              <el-button
+                link
+                type="danger"
+                icon="Delete"
+                @click="handleDelete(scope.row)"
+                >删除</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <div class="pagination-wrap" v-show="total > 0">
+        <pagination
+          :total="total"
+          v-model:page="pageNum"
+          v-model:limit="pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
 
     <!-- 编辑对话框 -->
-    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body>
-      <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
-        <el-form-item label="关联项目" prop="smId"><el-input v-model="form.smId" /></el-form-item>
-        <el-form-item label="项目名称" prop="sProjectName"><el-input v-model="form.sProjectName" /></el-form-item>
-        <el-form-item label="督察情况" prop="supervision"><el-input v-model="form.supervision" type="textarea" :rows="4" /></el-form-item>
-        <el-form-item label="开始时间" prop="startDate"><el-date-picker v-model="form.startDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" /></el-form-item>
-        <el-form-item label="结束时间" prop="endDate"><el-date-picker v-model="form.endDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" /></el-form-item>
+    <el-dialog
+      :title="dialogTitle"
+      v-model="dialogVisible"
+      width="650px"
+      append-to-body
+      :close-on-click-modal="false"
+      class="form-dialog"
+    >
+      <el-form
+        ref="formRef"
+        :model="form"
+        :rules="formRules"
+        label-width="100px"
+        class="project-form"
+      >
+        <div class="form-section">
+          <div class="section-title">基本信息</div>
+          <el-row :gutter="20">
+            <el-col :span="24">
+              <el-form-item label="关联项目" prop="smId">
+                <el-input v-model="form.smId" placeholder="请输入关联项目ID" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="项目名称" prop="sProjectName">
+                <el-input
+                  v-model="form.sProjectName"
+                  placeholder="请输入项目名称"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+
+        <div class="form-section">
+          <div class="section-title">督察信息</div>
+          <el-row :gutter="20">
+            <el-col :span="24">
+              <el-form-item label="督察情况" prop="supervision">
+                <el-input
+                  v-model="form.supervision"
+                  type="textarea"
+                  :rows="4"
+                  placeholder="请输入督察情况"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="开始时间" prop="startDate">
+                <el-date-picker
+                  v-model="form.startDate"
+                  type="datetime"
+                  value-format="YYYY-MM-DD HH:mm:ss"
+                  placeholder="选择开始时间"
+                  style="width: 100%"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="结束时间" prop="endDate">
+                <el-date-picker
+                  v-model="form.endDate"
+                  type="datetime"
+                  value-format="YYYY-MM-DD HH:mm:ss"
+                  placeholder="选择结束时间"
+                  style="width: 100%"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
       </el-form>
+
       <template #footer>
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitForm">确定</el-button>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="submitForm" :loading="submitLoading"
+            >确定</el-button
+          >
+        </div>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue'
-import { listWps, addWps, updateWps, delWps } from '@/api/hzz/wps'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-const showSearch = ref(true)
-const loading = ref(false)
-const total = ref(0)
-const pageNum = ref(1)
-const pageSize = ref(10)
-const wpsList = ref([])
-const queryParams = ref({ projectName: '', startDate: '', endDate: '' })
-
-const dialogVisible = ref(false)
-const dialogTitle = ref('')
-const formRef = ref(null)
-const form = reactive({})
-const formRules = { supervision: [{ required: true, message: '督察情况不能为空', trigger: 'blur' }] }
+import { ref, reactive, onMounted } from "vue";
+import { listWps, addWps, updateWps, delWps } from "@/api/hzz/wps";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+const showSearch = ref(true);
+const loading = ref(false);
+const submitLoading = ref(false);
+const total = ref(0);
+const pageNum = ref(1);
+const pageSize = ref(10);
+const wpsList = ref([]);
+const queryParams = ref({ projectName: "", startDate: "", endDate: "" });
+
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const form = reactive({});
+const formRules = {
+  supervision: [
+    { required: true, message: "督察情况不能为空", trigger: "blur" },
+  ],
+};
+
+const headerCellStyle = {
+  background: "#f5f7fa",
+  color: "#333",
+  fontWeight: "600",
+  fontSize: "14px",
+  textAlign: "center",
+};
 
 const getList = async () => {
-  loading.value = true
+  loading.value = true;
   try {
-    const res = await listWps({ ...queryParams.value, pageNum: pageNum.value, pageSize: pageSize.value })
-    wpsList.value = res.rows || []
-    total.value = res.total || 0
-  } finally { loading.value = false }
-}
-const resetQuery = () => { queryParams.value = { projectName: '', startDate: '', endDate: '' }; getList() }
-const handleAdd = () => { dialogTitle.value = '新增督查'; Object.keys(form).forEach(k => delete form[k]); dialogVisible.value = true }
-const handleEdit = (row) => { dialogTitle.value = '修改督查'; Object.assign(form, row); dialogVisible.value = true }
+    const res = await listWps({
+      ...queryParams.value,
+      pageNum: pageNum.value,
+      pageSize: pageSize.value,
+    });
+    wpsList.value = res.rows || [];
+    total.value = res.total || 0;
+  } finally {
+    loading.value = false;
+  }
+};
+
+const resetQuery = () => {
+  queryParams.value = { projectName: "", startDate: "", endDate: "" };
+  getList();
+};
+
+const handleAdd = () => {
+  dialogTitle.value = "新增督查";
+  Object.keys(form).forEach((k) => delete form[k]);
+  dialogVisible.value = true;
+};
+
+const handleEdit = (row) => {
+  dialogTitle.value = "修改督查";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
 const handleDelete = async (row) => {
-  await ElMessageBox.confirm('确认删除该督查记录?', '提示', { type: 'warning' })
-  await delWps(row.GUID)
-  ElMessage.success('删除成功')
-  getList()
-}
+  try {
+    await ElMessageBox.confirm(
+      "确认删除该督查记录?删除后不可恢复。",
+      "删除确认",
+      {
+        type: "warning",
+        confirmButtonText: "确认删除",
+        cancelButtonText: "取消",
+      },
+    );
+    await delWps(row.GUID);
+    ElMessage.success("删除成功");
+    getList();
+  } catch {
+    // 取消删除
+  }
+};
+
 const submitForm = async () => {
-  if (!formRef.value) return
-  await formRef.value.validate()
-  if (form.GUID) { await updateWps(form) } else { await addWps(form) }
-  ElMessage.success('操作成功')
-  dialogVisible.value = false
-  getList()
-}
+  if (!formRef.value) return;
 
-onMounted(() => getList())
+  try {
+    await formRef.value.validate();
+  } catch {
+    ElMessage.warning("请完善必填信息");
+    return;
+  }
+
+  submitLoading.value = true;
+  try {
+    if (form.GUID) {
+      await updateWps(form);
+      ElMessage.success("修改成功");
+    } else {
+      await addWps(form);
+      ElMessage.success("新增成功");
+    }
+    dialogVisible.value = false;
+    getList();
+  } catch {
+    ElMessage.error("操作失败");
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+onMounted(() => getList());
 </script>
+
+<style scoped>
+.supervise-page {
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  padding: 16px;
+  gap: 12px;
+  overflow: hidden;
+}
+
+/* 搜索区域 */
+.search-wrapper {
+  background: #fff;
+  padding: 16px 20px;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  flex-shrink: 0;
+}
+.search-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.search-form {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+}
+.search-form :deep(.el-form-item) {
+  margin-bottom: 0;
+  margin-right: 12px;
+}
+.search-form :deep(.el-input__wrapper) {
+  border-radius: 6px;
+}
+.search-form :deep(.el-button) {
+  border-radius: 6px;
+}
+.right-actions {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.right-actions .el-button {
+  border-radius: 6px;
+  font-weight: 500;
+}
+
+/* 表格区域 */
+.table-wrapper {
+  flex: 1;
+  background: #fff;
+  border-radius: 10px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.data-table {
+  flex: 1;
+  border-radius: 6px;
+  overflow: hidden;
+}
+.data-table :deep(.el-table__body-wrapper) .el-table__row:hover {
+  background-color: #f0f7ff !important;
+}
+
+:deep(.el-popper) {
+  max-width: 800px !important;
+}
+
+.action-buttons {
+  display: flex;
+  justify-content: center;
+  gap: 8px;
+}
+.action-buttons .el-button {
+  font-size: 13px;
+  padding: 4px 10px;
+}
+.pagination-wrap {
+  padding-top: 12px;
+  flex-shrink: 0;
+}
+
+/* 对话框 */
+.form-dialog :deep(.el-dialog) {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.form-dialog :deep(.el-dialog__header) {
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  padding: 16px 24px;
+  margin: 0;
+  text-align: center;
+}
+.form-dialog :deep(.el-dialog__title) {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+}
+.form-dialog :deep(.el-dialog__headerbtn) {
+  top: 16px;
+}
+.form-dialog :deep(.el-dialog__close) {
+  color: #fff;
+  font-size: 18px;
+}
+.form-dialog :deep(.el-dialog__close:hover) {
+  color: #f0f0f0;
+}
+.form-dialog :deep(.el-dialog__body) {
+  padding: 24px;
+  max-height: 60vh;
+  overflow-y: auto;
+}
+.form-dialog :deep(.el-dialog__footer) {
+  padding: 16px 24px;
+  border-top: 1px solid #f0f0f0;
+  text-align: center;
+}
+
+/* 表单样式 */
+.project-form .form-section {
+  margin-bottom: 20px;
+}
+.project-form .form-section:last-child {
+  margin-bottom: 0;
+}
+.section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 16px;
+  padding-bottom: 8px;
+  border-bottom: 2px solid #667eea;
+  text-align: center;
+}
+.project-form :deep(.el-form-item__label) {
+  font-weight: 500;
+  color: #555;
+}
+.project-form :deep(.el-input__wrapper) {
+  border-radius: 6px;
+  transition: all 0.2s;
+}
+.project-form :deep(.el-textarea__inner) {
+  border-radius: 6px;
+}
+.project-form :deep(.el-date-editor) .el-input__wrapper {
+  border-radius: 6px;
+}
+
+/* 对话框底部按钮 */
+.dialog-footer {
+  display: flex;
+  justify-content: center;
+  gap: 12px;
+}
+.dialog-footer .el-button {
+  border-radius: 6px;
+  font-weight: 500;
+  padding: 10px 24px;
+}
+</style>