瀏覽代碼

新安江GDP

dumingliang 7 小時之前
父節點
當前提交
79bd689420

+ 1 - 1
gw-admin/src/main/java/com/goldenwater/web/controller/xajgl/StXajBController.java

@@ -96,7 +96,7 @@ public class StXajBController extends BaseController {
     @PreAuthorize("@ss.hasPermi('xajgl:station:query')")
     public AjaxResult dataList(
             @Parameter(description = "测站代码") @RequestParam("stcd") String stcd,
-            @Parameter(description = "业务域编码") @RequestParam("bizCode") String bizCode,
+            @Parameter(description = "业务域编码") @RequestParam(value = "bizCode", required = false) String bizCode,
             @Parameter(description = "开始时间") @RequestParam(value = "startTm", required = false) String startTm,
             @Parameter(description = "结束时间") @RequestParam(value = "endTm", required = false) String endTm) {
         List<StXajData> list = stXajDataService.selectStXajDataByStcd(bizCode, stcd, startTm, endTm);

+ 118 - 2
gw-admin/src/main/java/com/goldenwater/web/controller/xajgl/WwCityGdpXajController.java

@@ -3,24 +3,46 @@ package com.goldenwater.web.controller.xajgl;
 import com.goldenwater.common.annotation.Log;
 import com.goldenwater.common.core.controller.BaseController;
 import com.goldenwater.common.core.domain.AjaxResult;
+import com.goldenwater.common.core.page.TableDataInfo;
 import com.goldenwater.common.enums.BusinessType;
+import com.goldenwater.common.utils.poi.ExcelUtil;
 import com.goldenwater.xajgl.domain.WwCityGdpXaj;
 import com.goldenwater.xajgl.service.IWwCityGdpXajService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import jakarta.annotation.Resource;
+import java.util.ArrayList;
 import java.util.List;
 
+/**
+ * 新安江流域GDP数据管理Controller
+ */
 @RestController
 @RequestMapping("/xajgl/gdp")
 @Tag(name = "新安江GDP数据管理", description = "地方人民政府GDP数据管理接口")
 public class WwCityGdpXajController extends BaseController {
 
-    @Autowired
+    @Resource
     private IWwCityGdpXajService wwCityGdpXajService;
 
+    /**
+     * 查询GDP数据列表
+     */
+    @GetMapping("/list")
+    @Operation(summary = "查询GDP数据列表", description = "分页查询GDP数据列表")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:list')")
+    public TableDataInfo list(WwCityGdpXaj wwCityGdpXaj) {
+        startPage();
+        List<WwCityGdpXaj> list = wwCityGdpXajService.selectWwCityGdpXajList(wwCityGdpXaj);
+        return getDataTable(list);
+    }
+
     /**
      * 查询各县区最新年份数据
      */
@@ -41,4 +63,98 @@ public class WwCityGdpXajController extends BaseController {
         List<WwCityGdpXaj> list = wwCityGdpXajService.selectHistoryByCounty(county);
         return AjaxResult.success(list);
     }
+
+    /**
+     * 根据区县和年份获取详细信息
+     */
+    @GetMapping("/{county}/{yr}")
+    @Operation(summary = "获取GDP详细信息", description = "根据区县和年份获取GDP详细信息")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:query')")
+    public AjaxResult getInfo(@PathVariable("county") String county, @PathVariable("yr") String yr) {
+        return AjaxResult.success(wwCityGdpXajService.selectByCountyAndYear(county, yr));
+    }
+
+    /**
+     * 新增GDP数据
+     */
+    @PostMapping
+    @Operation(summary = "新增GDP数据", description = "新增GDP数据")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:add')")
+    @Log(title = "GDP数据管理", businessType = BusinessType.INSERT)
+    public AjaxResult add(@RequestBody WwCityGdpXaj wwCityGdpXaj) {
+        return toAjax(wwCityGdpXajService.insertWwCityGdpXaj(wwCityGdpXaj));
+    }
+
+    /**
+     * 修改GDP数据
+     */
+    @PutMapping
+    @Operation(summary = "修改GDP数据", description = "修改GDP数据")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:edit')")
+    @Log(title = "GDP数据管理", businessType = BusinessType.UPDATE)
+    public AjaxResult edit(@RequestBody WwCityGdpXaj wwCityGdpXaj) {
+        return toAjax(wwCityGdpXajService.updateWwCityGdpXaj(wwCityGdpXaj));
+    }
+
+    /**
+     * 删除GDP数据
+     */
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除GDP数据", description = "删除GDP数据")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:remove')")
+    @Log(title = "GDP数据管理", businessType = BusinessType.DELETE)
+    public AjaxResult remove(@RequestBody WwCityGdpXaj wwCityGdpXaj) {
+        return toAjax(wwCityGdpXajService.deleteByCountyAndYear(wwCityGdpXaj.getCounty(), wwCityGdpXaj.getYr()));
+    }
+
+    /**
+     * 批量删除GDP数据
+     */
+    @DeleteMapping("/deleteBatch")
+    @Operation(summary = "批量删除GDP数据", description = "批量删除GDP数据")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:remove')")
+    @Log(title = "GDP数据管理", businessType = BusinessType.DELETE)
+    public AjaxResult removeBatch(@RequestBody List<WwCityGdpXaj> list) {
+        return toAjax(wwCityGdpXajService.deleteByList(list));
+    }
+
+    /**
+     * 导出GDP数据
+     */
+    @PostMapping("/export")
+    @Operation(summary = "导出GDP数据", description = "导出GDP数据")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:export')")
+    public void export(HttpServletResponse response, WwCityGdpXaj wwCityGdpXaj) {
+        List<WwCityGdpXaj> list = wwCityGdpXajService.selectWwCityGdpXajList(wwCityGdpXaj);
+        ExcelUtil<WwCityGdpXaj> util = new ExcelUtil<WwCityGdpXaj>(WwCityGdpXaj.class);
+        util.exportExcel(response, list, "新安江流域GDP数据");
+    }
+
+    /**
+     * 导入GDP数据
+     */
+    @PostMapping("/import")
+    @Operation(summary = "导入GDP数据", description = "导入GDP数据")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:import')")
+    @Log(title = "GDP数据管理", businessType = BusinessType.IMPORT)
+    public AjaxResult importData(@RequestParam("file") MultipartFile file) throws Exception {
+        ExcelUtil<WwCityGdpXaj> util = new ExcelUtil<WwCityGdpXaj>(WwCityGdpXaj.class);
+        List<WwCityGdpXaj> list = util.importExcel(file.getInputStream());
+        if (list == null || list.isEmpty()) {
+            return AjaxResult.error("导入数据为空");
+        }
+        String msg = wwCityGdpXajService.importGdp(list);
+        return AjaxResult.success(msg);
+    }
+
+    /**
+     * 下载导入模板
+     */
+    @GetMapping("/importTemplate")
+    @Operation(summary = "下载导入模板", description = "下载GDP数据导入模板")
+    @PreAuthorize("@ss.hasPermi('xajgl:gdp:import')")
+    public void importTemplate(HttpServletResponse response) {
+        ExcelUtil<WwCityGdpXaj> util = new ExcelUtil<WwCityGdpXaj>(WwCityGdpXaj.class);
+        util.exportExcel(response, new ArrayList<WwCityGdpXaj>(), "新安江流域GDP导入模板");
+    }
 }

+ 32 - 2
gw-gx/src/main/java/com/goldenwater/xajgl/domain/WwCityGdpXaj.java

@@ -1,88 +1,118 @@
 package com.goldenwater.xajgl.domain;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
-import java.io.Serializable;
+import com.goldenwater.common.annotation.Excel;
+import com.goldenwater.common.core.domain.BaseEntity;
 import java.math.BigDecimal;
 
-public class WwCityGdpXaj implements Serializable {
+/**
+ * 新安江流域GDP数据对象 WW_CITY_GDP_XAJ
+ */
+public class WwCityGdpXaj extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
     /** 省份 */
+    @Excel(name = "省份")
     private String prov;
 
     /** 年份 */
+    @Excel(name = "年份")
     private String yr;
 
     /** 地市 */
+    @Excel(name = "地市")
     private String city;
 
     /** 区县 */
+    @Excel(name = "区县")
     private String county;
 
     /** 城市人口总数(万人) */
+    @Excel(name = "城市人口(万人)")
     private BigDecimal peoCitySum;
 
     /** 农村人口总数(万人) */
+    @Excel(name = "农村人口(万人)")
     private BigDecimal peoCountySum;
 
     /** 人口总数(万人) */
+    @Excel(name = "人口总数(万人)")
     private BigDecimal pepSum;
 
     /** 一产(亿元) */
+    @Excel(name = "一产(亿元)")
     private BigDecimal gdp1Sum;
 
     /** 二产(亿元) */
+    @Excel(name = "二产(亿元)")
     private BigDecimal gdp2Sum;
 
     /** 三产(亿元) */
+    @Excel(name = "三产(亿元)")
     private BigDecimal gdp3Sum;
 
     /** 合计(亿元) */
+    @Excel(name = "GDP合计(亿元)")
     private BigDecimal gdpSum;
 
     /** 农田有效灌溉面积(万亩) */
+    @Excel(name = "农田有效灌溉面积(万亩)")
     private BigDecimal effectIrrArea;
 
     /** 城镇化率(%) */
+    @Excel(name = "城镇化率(%)")
     private BigDecimal peoRadio;
 
     /** 耕地面积(万亩) */
+    @Excel(name = "耕地面积(万亩)")
     private BigDecimal gdArea;
 
     /** 耕地有效灌溉面积(万亩) */
+    @Excel(name = "耕地有效灌溉面积(万亩)")
     private BigDecimal effectGdArea;
 
     /** 实际灌溉面积-耕地(万亩) */
+    @Excel(name = "实际灌溉面积-耕地(万亩)")
     private BigDecimal effectGgGd;
 
     /** 实际灌溉面积-林地(万亩) */
+    @Excel(name = "实际灌溉面积-林地(万亩)")
     private BigDecimal effectGgLd;
 
     /** 实际灌溉面积-园地(万亩) */
+    @Excel(name = "实际灌溉面积-园地(万亩)")
     private BigDecimal effectGgYd;
 
     /** 实际灌溉面积-牧草地(万亩) */
+    @Excel(name = "实际灌溉面积-牧草地(万亩)")
     private BigDecimal effectGgMcd;
 
     /** 鱼塘补水面积(万亩) */
+    @Excel(name = "鱼塘补水面积(万亩)")
     private BigDecimal ytbsArea;
 
     /** 大牲畜(万头) */
+    @Excel(name = "大牲畜(万头)")
     private BigDecimal scBig;
 
     /** 小牲畜(万头) */
+    @Excel(name = "小牲畜(万头)")
     private BigDecimal scSmall;
 
     /** 牲畜合计(万头) */
+    @Excel(name = "牲畜合计(万头)")
     private BigDecimal scSum;
 
     /** 工业增加值(亿元) */
+    @Excel(name = "工业增加值(亿元)")
     private BigDecimal insduAdd;
 
     /** 粮食产量(万t) */
+    @Excel(name = "粮食产量(万t)")
     private BigDecimal argicuTt;
 
     /** 顺序 */
+    @Excel(name = "顺序")
     private BigDecimal orderNm;
 
     public String getProv() { return prov; }

+ 30 - 0
gw-gx/src/main/java/com/goldenwater/xajgl/mapper/WwCityGdpXajMapper.java

@@ -6,6 +6,11 @@ import java.util.List;
 
 public interface WwCityGdpXajMapper {
 
+    /**
+     * 查询GDP数据列表(分页)
+     */
+    List<WwCityGdpXaj> selectWwCityGdpXajList(WwCityGdpXaj wwCityGdpXaj);
+
     /**
      * 查询各县区最新年份数据
      */
@@ -15,4 +20,29 @@ public interface WwCityGdpXajMapper {
      * 查询指定县区历史数据
      */
     List<WwCityGdpXaj> selectHistoryByCounty(@Param("county") String county);
+
+    /**
+     * 根据区县和年份查询单条数据
+     */
+    WwCityGdpXaj selectByCountyAndYear(@Param("county") String county, @Param("yr") String yr);
+
+    /**
+     * 新增GDP数据
+     */
+    int insertWwCityGdpXaj(WwCityGdpXaj wwCityGdpXaj);
+
+    /**
+     * 修改GDP数据
+     */
+    int updateWwCityGdpXaj(WwCityGdpXaj wwCityGdpXaj);
+
+    /**
+     * 删除GDP数据
+     */
+    int deleteByCountyAndYear(@Param("county") String county, @Param("yr") String yr);
+
+    /**
+     * 批量删除GDP数据
+     */
+    int deleteByList(List<WwCityGdpXaj> list);
 }

+ 35 - 0
gw-gx/src/main/java/com/goldenwater/xajgl/service/IWwCityGdpXajService.java

@@ -5,6 +5,11 @@ import java.util.List;
 
 public interface IWwCityGdpXajService {
 
+    /**
+     * 查询GDP数据列表(分页)
+     */
+    List<WwCityGdpXaj> selectWwCityGdpXajList(WwCityGdpXaj wwCityGdpXaj);
+
     /**
      * 查询各县区最新年份数据
      */
@@ -14,4 +19,34 @@ public interface IWwCityGdpXajService {
      * 查询指定县区历史数据
      */
     List<WwCityGdpXaj> selectHistoryByCounty(String county);
+
+    /**
+     * 根据区县和年份查询单条数据
+     */
+    WwCityGdpXaj selectByCountyAndYear(String county, String yr);
+
+    /**
+     * 新增GDP数据
+     */
+    int insertWwCityGdpXaj(WwCityGdpXaj wwCityGdpXaj);
+
+    /**
+     * 修改GDP数据
+     */
+    int updateWwCityGdpXaj(WwCityGdpXaj wwCityGdpXaj);
+
+    /**
+     * 删除GDP数据
+     */
+    int deleteByCountyAndYear(String county, String yr);
+
+    /**
+     * 批量删除GDP数据
+     */
+    int deleteByList(List<WwCityGdpXaj> list);
+
+    /**
+     * 导入GDP数据
+     */
+    String importGdp(List<WwCityGdpXaj> list);
 }

+ 61 - 0
gw-gx/src/main/java/com/goldenwater/xajgl/service/impl/WwCityGdpXajServiceImpl.java

@@ -5,6 +5,8 @@ import com.goldenwater.xajgl.mapper.WwCityGdpXajMapper;
 import com.goldenwater.xajgl.service.IWwCityGdpXajService;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import java.util.List;
 
 @Service("wwCityGdpXajService")
@@ -13,6 +15,11 @@ public class WwCityGdpXajServiceImpl implements IWwCityGdpXajService {
     @Resource
     private WwCityGdpXajMapper wwCityGdpXajMapper;
 
+    @Override
+    public List<WwCityGdpXaj> selectWwCityGdpXajList(WwCityGdpXaj wwCityGdpXaj) {
+        return wwCityGdpXajMapper.selectWwCityGdpXajList(wwCityGdpXaj);
+    }
+
     @Override
     public List<WwCityGdpXaj> selectLatestByCounty() {
         return wwCityGdpXajMapper.selectLatestByCounty();
@@ -22,4 +29,58 @@ public class WwCityGdpXajServiceImpl implements IWwCityGdpXajService {
     public List<WwCityGdpXaj> selectHistoryByCounty(String county) {
         return wwCityGdpXajMapper.selectHistoryByCounty(county);
     }
+
+    @Override
+    public WwCityGdpXaj selectByCountyAndYear(String county, String yr) {
+        return wwCityGdpXajMapper.selectByCountyAndYear(county, yr);
+    }
+
+    @Override
+    public int insertWwCityGdpXaj(WwCityGdpXaj wwCityGdpXaj) {
+        return wwCityGdpXajMapper.insertWwCityGdpXaj(wwCityGdpXaj);
+    }
+
+    @Override
+    public int updateWwCityGdpXaj(WwCityGdpXaj wwCityGdpXaj) {
+        return wwCityGdpXajMapper.updateWwCityGdpXaj(wwCityGdpXaj);
+    }
+
+    @Override
+    public int deleteByCountyAndYear(String county, String yr) {
+        return wwCityGdpXajMapper.deleteByCountyAndYear(county, yr);
+    }
+
+    @Override
+    public int deleteByList(List<WwCityGdpXaj> list) {
+        return wwCityGdpXajMapper.deleteByList(list);
+    }
+
+    @Override
+    @Transactional
+    public String importGdp(List<WwCityGdpXaj> list) {
+        int success = 0;
+        int fail = 0;
+        StringBuilder failMsg = new StringBuilder();
+        for (WwCityGdpXaj gdp : list) {
+            try {
+                // 根据区县+年份判断是否存在,存在则更新,不存在则新增
+                WwCityGdpXaj existing = wwCityGdpXajMapper.selectByCountyAndYear(gdp.getCounty(), gdp.getYr());
+                if (existing != null) {
+                    wwCityGdpXajMapper.updateWwCityGdpXaj(gdp);
+                } else {
+                    wwCityGdpXajMapper.insertWwCityGdpXaj(gdp);
+                }
+                success++;
+            } catch (Exception e) {
+                fail++;
+                failMsg.append("<br/>第").append(success + fail).append("条导入失败:").append(e.getMessage());
+            }
+        }
+        StringBuilder msg = new StringBuilder();
+        msg.append("<br/>导入成功").append(success).append("条数据。");
+        if (fail > 0) {
+            msg.append("<br/>导入失败").append(fail).append("条数据。").append(failMsg);
+        }
+        return msg.toString();
+    }
 }

+ 94 - 9
gw-gx/src/main/resources/mapper/xajgl/WwCityGdpXajMapper.xml

@@ -32,6 +32,29 @@
         <result property="orderNm" column="ORDER_NM"/>
     </resultMap>
 
+    <sql id="selectWwCityGdpXajVo">
+        select PROV, YR, CITY, COUNTY,
+               PEO_CITY_SUM, PEO_COUNTY_SUM, PEP_SUM,
+               GDP_1_SUM, GDP_2_SUM, GDP_3_SUM, GDP_SUM,
+               EFFECT_IRR_AREA, PEO_RADIO, GD_AREA,
+               EFFECT_GD_AREA, EFFECT_GG_GD, EFFECT_GG_LD,
+               EFFECT_GG_YD, EFFECT_GG_MCD, YTBS_AREA,
+               SC_BIG, SC_SMALL, SC_SUM,
+               INSDU_ADD, ARGICU_TT, ORDER_NM
+        from WW_CITY_GDP_XAJ
+    </sql>
+
+    <select id="selectWwCityGdpXajList" parameterType="com.goldenwater.xajgl.domain.WwCityGdpXaj" resultMap="WwCityGdpXajMap">
+        <include refid="selectWwCityGdpXajVo"/>
+        <where>
+            <if test="prov != null and prov != ''">AND PROV = #{prov}</if>
+            <if test="yr != null and yr != ''">AND YR = #{yr}</if>
+            <if test="city != null and city != ''">AND CITY = #{city}</if>
+            <if test="county != null and county != ''">AND COUNTY like concat('%', #{county}, '%')</if>
+        </where>
+        order by ORDER_NM, YR desc
+    </select>
+
     <select id="selectLatestByCounty" resultMap="WwCityGdpXajMap">
         select t.PROV, t.YR, t.CITY, t.COUNTY,
                t.PEO_CITY_SUM, t.PEO_COUNTY_SUM, t.PEP_SUM,
@@ -51,17 +74,79 @@
     </select>
 
     <select id="selectHistoryByCounty" resultMap="WwCityGdpXajMap">
-        select PROV, YR, CITY, COUNTY,
-               PEO_CITY_SUM, PEO_COUNTY_SUM, PEP_SUM,
-               GDP_1_SUM, GDP_2_SUM, GDP_3_SUM, GDP_SUM,
-               EFFECT_IRR_AREA, PEO_RADIO, GD_AREA,
-               EFFECT_GD_AREA, EFFECT_GG_GD, EFFECT_GG_LD,
-               EFFECT_GG_YD, EFFECT_GG_MCD, YTBS_AREA,
-               SC_BIG, SC_SMALL, SC_SUM,
-               INSDU_ADD, ARGICU_TT, ORDER_NM
-        from WW_CITY_GDP_XAJ
+        <include refid="selectWwCityGdpXajVo"/>
         where COUNTY = #{county}
         order by YR
     </select>
 
+    <select id="selectByCountyAndYear" resultMap="WwCityGdpXajMap">
+        <include refid="selectWwCityGdpXajVo"/>
+        where COUNTY = #{county} and YR = #{yr}
+    </select>
+
+    <insert id="insertWwCityGdpXaj" parameterType="com.goldenwater.xajgl.domain.WwCityGdpXaj">
+        insert into WW_CITY_GDP_XAJ (
+            PROV, YR, CITY, COUNTY,
+            PEO_CITY_SUM, PEO_COUNTY_SUM, PEP_SUM,
+            GDP_1_SUM, GDP_2_SUM, GDP_3_SUM, GDP_SUM,
+            EFFECT_IRR_AREA, PEO_RADIO, GD_AREA,
+            EFFECT_GD_AREA, EFFECT_GG_GD, EFFECT_GG_LD,
+            EFFECT_GG_YD, EFFECT_GG_MCD, YTBS_AREA,
+            SC_BIG, SC_SMALL, SC_SUM,
+            INSDU_ADD, ARGICU_TT, ORDER_NM
+        ) values (
+            #{prov}, #{yr}, #{city}, #{county},
+            #{peoCitySum}, #{peoCountySum}, #{pepSum},
+            #{gdp1Sum}, #{gdp2Sum}, #{gdp3Sum}, #{gdpSum},
+            #{effectIrrArea}, #{peoRadio}, #{gdArea},
+            #{effectGdArea}, #{effectGgGd}, #{effectGgLd},
+            #{effectGgYd}, #{effectGgMcd}, #{ytbsArea},
+            #{scBig}, #{scSmall}, #{scSum},
+            #{insduAdd}, #{argicuTt}, #{orderNm}
+        )
+    </insert>
+
+    <update id="updateWwCityGdpXaj" parameterType="com.goldenwater.xajgl.domain.WwCityGdpXaj">
+        update WW_CITY_GDP_XAJ
+        <set>
+            <if test="prov != null and prov != ''">PROV = #{prov},</if>
+            <if test="city != null and city != ''">CITY = #{city},</if>
+            <if test="peoCitySum != null">PEO_CITY_SUM = #{peoCitySum},</if>
+            <if test="peoCountySum != null">PEO_COUNTY_SUM = #{peoCountySum},</if>
+            <if test="pepSum != null">PEP_SUM = #{pepSum},</if>
+            <if test="gdp1Sum != null">GDP_1_SUM = #{gdp1Sum},</if>
+            <if test="gdp2Sum != null">GDP_2_SUM = #{gdp2Sum},</if>
+            <if test="gdp3Sum != null">GDP_3_SUM = #{gdp3Sum},</if>
+            <if test="gdpSum != null">GDP_SUM = #{gdpSum},</if>
+            <if test="effectIrrArea != null">EFFECT_IRR_AREA = #{effectIrrArea},</if>
+            <if test="peoRadio != null">PEO_RADIO = #{peoRadio},</if>
+            <if test="gdArea != null">GD_AREA = #{gdArea},</if>
+            <if test="effectGdArea != null">EFFECT_GD_AREA = #{effectGdArea},</if>
+            <if test="effectGgGd != null">EFFECT_GG_GD = #{effectGgGd},</if>
+            <if test="effectGgLd != null">EFFECT_GG_LD = #{effectGgLd},</if>
+            <if test="effectGgYd != null">EFFECT_GG_YD = #{effectGgYd},</if>
+            <if test="effectGgMcd != null">EFFECT_GG_MCD = #{effectGgMcd},</if>
+            <if test="ytbsArea != null">YTBS_AREA = #{ytbsArea},</if>
+            <if test="scBig != null">SC_BIG = #{scBig},</if>
+            <if test="scSmall != null">SC_SMALL = #{scSmall},</if>
+            <if test="scSum != null">SC_SUM = #{scSum},</if>
+            <if test="insduAdd != null">INSDU_ADD = #{insduAdd},</if>
+            <if test="argicuTt != null">ARGICU_TT = #{argicuTt},</if>
+            <if test="orderNm != null">ORDER_NM = #{orderNm},</if>
+        </set>
+        where COUNTY = #{county} and YR = #{yr}
+    </update>
+
+    <delete id="deleteByCountyAndYear">
+        delete from WW_CITY_GDP_XAJ where COUNTY = #{county} and YR = #{yr}
+    </delete>
+
+    <delete id="deleteByList">
+        delete from WW_CITY_GDP_XAJ
+        where
+        <foreach collection="list" item="item" open="(" separator=" or " close=")">
+            (COUNTY = #{item.county} and YR = #{item.yr})
+        </foreach>
+    </delete>
+
 </mapper>

+ 83 - 0
gw-ui/src/api/xajgl/gdp.js

@@ -1,5 +1,14 @@
 import request from '@/utils/request'
 
+// 查询GDP数据列表
+export function listGdp(query) {
+  return request({
+    url: '/xajgl/gdp/list',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询各县区最新年份GDP数据
 export function getLatestGdpByCounty() {
   return request({
@@ -16,3 +25,77 @@ export function getGdpHistoryByCounty(county) {
     params: { county }
   })
 }
+
+// 根据区县和年份获取详细信息
+export function getGdp(county, yr) {
+  return request({
+    url: '/xajgl/gdp/' + county + '/' + yr,
+    method: 'get'
+  })
+}
+
+// 新增GDP数据
+export function addGdp(data) {
+  return request({
+    url: '/xajgl/gdp',
+    method: 'post',
+    data
+  })
+}
+
+// 修改GDP数据
+export function updateGdp(data) {
+  return request({
+    url: '/xajgl/gdp',
+    method: 'put',
+    data
+  })
+}
+
+// 删除GDP数据
+export function delGdp(data) {
+  return request({
+    url: '/xajgl/gdp/delete',
+    method: 'delete',
+    data
+  })
+}
+
+// 批量删除GDP数据
+export function delGdpBatch(data) {
+  return request({
+    url: '/xajgl/gdp/deleteBatch',
+    method: 'delete',
+    data
+  })
+}
+
+// 导出GDP数据
+export function exportGdp(query) {
+  return request({
+    url: '/xajgl/gdp/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
+
+// 导入GDP数据
+export function importGdp(file) {
+  const formData = new FormData()
+  formData.append('file', file)
+  return request({
+    url: '/xajgl/gdp/import',
+    method: 'post',
+    data: formData
+  })
+}
+
+// 下载导入模板
+export function importGdpTemplate() {
+  return request({
+    url: '/xajgl/gdp/importTemplate',
+    method: 'get',
+    responseType: 'blob'
+  })
+}

+ 27 - 14
gw-ui/src/views/front/Xaj.vue

@@ -70,7 +70,7 @@
             <div class="dialog-content">
               <div class="dialog-table">
                 <el-table :data="historyData" v-loading="historyLoading" stripe border height="420">
-                  <el-table-column prop="tm" label="时间" min-width="160" />
+                  <el-table-column prop="tm" label="时间" min-width="160" :formatter="fmtTimeCol" />
                   <el-table-column prop="z" label="水位(m)" />
                   <el-table-column prop="inq" label="入库流量(m³/s)" />
                   <el-table-column prop="outq" label="出库流量(m³/s)" />
@@ -85,7 +85,7 @@
             <div class="dialog-content">
               <div class="dialog-table">
                 <el-table :data="historyData" v-loading="historyLoading" stripe border height="420">
-                  <el-table-column prop="tm" label="时间" min-width="160" />
+                  <el-table-column prop="tm" label="时间" min-width="160" :formatter="fmtTimeCol" />
                   <el-table-column prop="ww" label="下泄水量(万m³)" />
                 </el-table>
               </div>
@@ -128,7 +128,7 @@
         <div class="dialog-content">
           <div class="dialog-table">
             <el-table :data="historyData" v-loading="historyLoading" stripe border height="420">
-              <el-table-column prop="tm" label="时间" min-width="160" />
+              <el-table-column prop="tm" label="时间" min-width="160" :formatter="fmtTimeCol" />
               <el-table-column v-if="currentStation.tp === 'DRP'" prop="drp" label="降雨量(mm)" />
               <template v-else-if="currentStation.tp === 'ZZ'">
                 <el-table-column prop="z" label="水位(m)" />
@@ -193,6 +193,15 @@ function formatDate(date) {
   return `${y}-${m}-${d}`
 }
 
+// 表格时间列格式化为 YYYY-MM-DD
+function fmtTimeCol(row, column, cellValue) {
+  if (!cellValue) return '-'
+  if (/^\d{4}-\d{2}-\d{2}/.test(cellValue)) return cellValue.substring(0, 10)
+  const d = new Date(cellValue)
+  if (isNaN(d)) return String(cellValue)
+  return formatDate(d)
+}
+
 // 打开站点弹框
 function openStationDialog(station) {
   currentStation.value = station
@@ -225,6 +234,20 @@ async function loadHistoryData() {
     ElMessage.error('加载历史数据失败')
   } finally {
     historyLoading.value = false
+    nextTick(initCurrentChart)
+  }
+}
+
+// 根据当前站点类型和Tab初始化对应图表
+function initCurrentChart() {
+  if (!currentStation.value) return
+  if (currentStation.value.tp === 'SK') {
+    const tab = skActiveTab.value
+    if (tab === 'hydro') initSkHydroChart()
+    else if (tab === 'water') initSkWaterChart()
+    else if (tab === 'qx') initSkQxChart()
+  } else {
+    initDefaultChart()
   }
 }
 
@@ -269,17 +292,7 @@ function handleDialogClose() {
 
 // 日期范围变更
 function handleDateRangeChange() {
-  loadHistoryData().then(() => {
-    nextTick(() => {
-      if (currentStation.value && currentStation.value.tp === 'SK') {
-        const tab = skActiveTab.value
-        if (tab === 'hydro') initSkHydroChart()
-        else if (tab === 'water') initSkWaterChart()
-      } else {
-        initDefaultChart()
-      }
-    })
-  })
+  loadHistoryData()
 }
 
 // SK Tab切换

+ 347 - 29
gw-ui/src/views/front/xaj/GisModule.vue

@@ -50,16 +50,25 @@
             </div>
           </div>
 
+          <!-- 地图图层切换 -->
+          <div class="layer-switch">
+            <el-radio-group v-model="currentLayer" size="small" @change="switchLayer">
+              <el-radio-button label="vec">矢量图</el-radio-button>
+              <el-radio-button label="img">影像图</el-radio-button>
+              <el-radio-button label="ter">地形图</el-radio-button>
+            </el-radio-group>
+          </div>
+
           <div class="coord-display">
             经度: {{ coordDisplay.lng }} 纬度: {{ coordDisplay.lat }}
           </div>
 
           <div v-if="tooltipData.visible" class="map-tooltip" :style="tooltipStyle">
             <div class="tooltip-title">{{ tooltipData.stnm }}</div>
-            <div class="tooltip-row">站码:<b>{{ tooltipData.stcd }}</b></div>
             <div class="tooltip-row">共享单位:{{ tooltipData.unit }}</div>
             <div class="tooltip-row">监测时间:{{ tooltipData.loading ? '加载中...' : tooltipData.tm }}</div>
             <template v-if="!tooltipData.loading && tooltipData.values.length">
+              <div class="tooltip-divider"></div>
               <div class="tooltip-row" v-for="v in tooltipData.values" :key="v.label">
                 {{ v.label }}:<b>{{ v.value }}</b> {{ v.unit }}
               </div>
@@ -77,11 +86,56 @@
         </div>
       </el-tab-pane>
     </el-tabs>
+
+    <!-- 历史数据弹框 -->
+    <el-dialog v-model="historyDialogVisible" :title="historyDialogTitle" width="85%" append-to-body>
+      <div class="history-content">
+        <!-- 左侧表格 -->
+        <div class="history-left">
+          <el-table :data="historyData" v-loading="historyLoading" stripe border style="width: 100%" height="100%">
+            <el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
+            <template v-for="col in currentHistoryColumns" :key="col.prop">
+              <el-table-column
+                :prop="col.prop"
+                :label="col.label"
+                :width="col.width"
+                :min-width="col.minWidth"
+                :formatter="col.formatter"
+                :align="col.align || 'center'"
+                show-overflow-tooltip
+              />
+            </template>
+          </el-table>
+          <div v-if="historyData.length === 0 && !historyLoading" class="empty-state">
+            <el-icon class="empty-icon"><DataAnalysis /></el-icon>
+            <p>暂无历史数据</p>
+          </div>
+        </div>
+        <!-- 右侧图表 -->
+        <div class="history-right">
+          <div class="chart-header">
+            <span class="toolbar-label">时间范围:</span>
+            <el-date-picker
+              v-model="historyDateRange"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="开始"
+              end-placeholder="结束"
+              value-format="YYYY-MM-DD"
+              @change="loadHistoryData"
+              size="default"
+              style="width: 260px"
+            />
+          </div>
+          <div ref="historyChartRef" class="history-chart"></div>
+        </div>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup name="GisModule">
-import { ref, reactive, onMounted, onUnmounted, nextTick, watch } from 'vue'
+import { ref, reactive, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
 import { Map as OlMap, View } from 'ol'
 import { XYZ, Vector as VectorSource } from 'ol/source'
 import TileLayer from 'ol/layer/Tile'
@@ -89,13 +143,14 @@ import VectorLayer from 'ol/layer/Vector'
 import { fromLonLat, toLonLat } from 'ol/proj'
 import { Feature } from 'ol'
 import { Point } from 'ol/geom'
-import { Style, Fill, Stroke, Icon, Circle as CircleStyle } from 'ol/style'
+import { Style, Fill, Stroke, Icon, Circle as CircleStyle, Text as TextStyle } from 'ol/style'
 import GeoJSON from 'ol/format/GeoJSON'
 import 'ol/ol.css'
-import { DataLine } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
+import * as echarts from 'echarts'
+import { DataLine, Search, DataAnalysis } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { xajfqGeoJson } from '@/assets/mapJSON/xajfq_geojson'
-import { listMapStations, getLatestData } from '@/api/xajgl/station'
+import { listMapStations, getLatestData, listData } from '@/api/xajgl/station'
 
 const props = defineProps({
   active: { type: Boolean, default: false }
@@ -106,6 +161,37 @@ const emit = defineEmits(['open-station-dialog'])
 const activeTab = ref('situation')
 const mapContainer = ref(null)
 
+// 历史数据弹框相关
+const historyDialogVisible = ref(false)
+const historyDialogTitle = ref('')
+const historyData = ref([])
+const historyLoading = ref(false)
+const historyDateRange = ref([])
+const currentStation = ref(null)
+const historyChartRef = ref(null)
+let historyChart = null
+
+// 站点类型对应的业务域配置
+const STATION_BIZ_CONFIG = {
+  DRP: { bizCode: 'AH_SL_YL', columns: ['stnm', 'tm', 'drp', 'prov', 'city', 'county'], historyChart: ['drp'] },
+  ZZ: { bizCode: 'AH_SL_JL', columns: ['stnm', 'tm', 'z', 'q', 'prov', 'city', 'county'], historyChart: ['z', 'q'] },
+  DYP: { bizCode: 'AH_SL_ZF', columns: ['stnm', 'tm', 'dyp', 'prov', 'city', 'county'], historyChart: ['dyp'] },
+  SK: { bizCode: 'FD_XAJ', columns: ['stnm', 'tm', 'z', 'dwz', 'inq', 'outq'], historyChart: ['z', 'inq', 'outq'] }
+}
+
+// 历史数据列配置
+const historyColumns = [
+  { prop: 'stnm', label: '站点名称', width: 150, fixed: 'left' },
+  { prop: 'tm', label: '时间', width: 150, formatter: fmtDateTime },
+  { prop: 'z', label: '水位(m)', width: 100 },
+  { prop: 'dwz', label: '坝下水位(m)', width: 110 },
+  { prop: 'q', label: '流量(m³/s)', width: 110 },
+  { prop: 'inq', label: '入库流量(m³/s)', width: 120 },
+  { prop: 'outq', label: '出库流量(m³/s)', width: 120 },
+  { prop: 'drp', label: '降雨量(mm)', width: 110 },
+  { prop: 'dyp', label: '蒸发量(mm)', width: 110 }
+]
+
 const stationColorMap = {
   DRP: '#17c2c7',
   ZZ: '#1c97e7',
@@ -131,8 +217,27 @@ const typeLegend = [
 let olMap = null
 let areaLayer = null
 let stationLayer = null
+let baseLayer = null
+let labelLayer = null
 let resizeObserver = null
 let currentHoverStcd = null
+const currentLayer = ref('vec')
+
+// 天地图图层配置
+const layerConfig = {
+  vec: {
+    base: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65',
+    label: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
+  },
+  img: {
+    base: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65',
+    label: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
+  },
+  ter: {
+    base: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65',
+    label: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cta_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
+  }
+}
 
 const coordDisplay = reactive({ lng: '119.0500', lat: '29.5000' })
 
@@ -170,6 +275,14 @@ function createStationStyle(tp) {
 function initMap() {
   if (!mapContainer.value) return
 
+  // 创建底图和注记图层
+  baseLayer = new TileLayer({
+    source: new XYZ({ url: layerConfig.vec.base })
+  })
+  labelLayer = new TileLayer({
+    source: new XYZ({ url: layerConfig.vec.label })
+  })
+
   olMap = new OlMap({
     target: mapContainer.value,
     view: new View({
@@ -178,18 +291,7 @@ function initMap() {
       minZoom: 8,
       maxZoom: 18
     }),
-    layers: [
-      new TileLayer({
-        source: new XYZ({
-          url: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
-        })
-      }),
-      new TileLayer({
-        source: new XYZ({
-          url: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
-        })
-      })
-    ]
+    layers: [baseLayer, labelLayer]
   })
 
   areaLayer = new VectorLayer({ source: new VectorSource() })
@@ -204,6 +306,14 @@ function initMap() {
   olMap.on('pointermove', handlePointerMove)
 }
 
+// 切换地图图层
+function switchLayer(type) {
+  if (!baseLayer || !labelLayer) return
+  const config = layerConfig[type]
+  baseLayer.setSource(new XYZ({ url: config.base }))
+  labelLayer.setSource(new XYZ({ url: config.label }))
+}
+
 function loadAreaGeoJson() {
   const source = areaLayer.getSource()
   try {
@@ -213,9 +323,17 @@ function loadAreaGeoJson() {
       featureProjection: 'EPSG:3857'
     })
     features.forEach(f => {
+      const name = f.get('name') || ''
       f.setStyle(new Style({
-        fill: new Fill({ color: 'rgba(28, 151, 231, 0.12)' }),
-        stroke: new Stroke({ color: 'rgba(28, 151, 231, 0.7)', width: 2 })
+        fill: new Fill({ color: 'rgba(128, 128, 128, 0.1)' }),
+        stroke: new Stroke({ color: 'rgba(0, 51, 102, 0.9)', width: 2.5 }),
+        text: name ? new TextStyle({
+          text: name,
+          font: '12px Microsoft YaHei',
+          fill: new Fill({ color: '#303133' }),
+          stroke: new Stroke({ color: '#fff', width: 3 }),
+          overflow: true
+        }) : undefined
       }))
     })
     source.addFeatures(features)
@@ -266,29 +384,147 @@ async function getLatestDataWithCache(stcd) {
 function buildLatestValues(tp, data) {
   const values = []
   if (tp === 'DRP') {
-    values.push({ label: '降雨量', value: data.drp, unit: 'mm' })
+    if (data.drp != null && data.drp !== '') values.push({ label: '降雨量', value: data.drp, unit: 'mm' })
   } else if (tp === 'ZZ') {
-    values.push({ label: '水位', value: data.z, unit: 'm' })
-    values.push({ label: '流量', value: data.q, unit: 'm³/s' })
+    if (data.z != null && data.z !== '') values.push({ label: '水位', value: data.z, unit: 'm' })
+    if (data.q != null && data.q !== '') values.push({ label: '流量', value: data.q, unit: 'm³/s' })
   } else if (tp === 'DYP') {
-    values.push({ label: '蒸发量', value: data.dyp, unit: 'mm' })
+    if (data.dyp != null && data.dyp !== '') values.push({ label: '蒸发量', value: data.dyp, unit: 'mm' })
   } else if (tp === 'SK') {
-    values.push({ label: '水位', value: data.z, unit: 'm' })
-    values.push({ label: '下泄水量', value: data.ww, unit: '万m³' })
+    if (data.z != null && data.z !== '') values.push({ label: '坝上水位', value: data.z, unit: 'm' })
+    if (data.inq != null && data.inq !== '') values.push({ label: '入库流量', value: data.inq, unit: 'm³/s' })
+    if (data.outq != null && data.outq !== '') values.push({ label: '出库流量', value: data.outq, unit: 'm³/s' })
+    if (data.ww != null && data.ww !== '') values.push({ label: '下泄水量', value: data.ww, unit: '万m³' })
   }
-  return values.filter(v => v.value != null && v.value !== '')
+  return values
+}
+
+// 格式化时间为 YYYY-MM-DD
+function fmtDate(val) {
+  if (!val) return '-'
+  const d = new Date(val)
+  if (isNaN(d)) return String(val)
+  const pad = n => n < 10 ? '0' + n : String(n)
+  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
+}
+
+// 格式化日期时间
+function fmtDateTime(row, col, val) {
+  if (!val) return '-'
+  const d = new Date(val)
+  if (isNaN(d)) return String(val)
+  const pad = n => n < 10 ? '0' + n : String(n)
+  return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`
+}
+
+// 格式化日期
+function formatDate(date) {
+  const y = date.getFullYear()
+  const m = String(date.getMonth() + 1).padStart(2, '0')
+  const d = String(date.getDate()).padStart(2, '0')
+  return `${y}-${m}-${d}`
 }
 
+// 计算当前历史数据列配置
+const currentHistoryColumns = computed(() => {
+  if (!currentStation.value) return historyColumns
+  const config = STATION_BIZ_CONFIG[currentStation.value.tp]
+  if (!config) return historyColumns
+  return historyColumns.filter(col => config.columns.includes(col.prop))
+})
+
 function handleMapClick(event) {
   const feature = olMap.forEachFeatureAtPixel(event.pixel, f => f, {
     layerFilter: l => l === stationLayer
   })
   if (feature) {
     const station = feature.get('station')
-    if (station) emit('open-station-dialog', station)
+    if (station) {
+      openHistoryDialog(station)
+    }
   }
 }
 
+// 打开历史数据弹框
+function openHistoryDialog(station) {
+  currentStation.value = station
+  historyDialogTitle.value = `${station.stnm} - 历史数据`
+  // 默认7天
+  const end = new Date()
+  const start = new Date()
+  start.setDate(start.getDate() - 7)
+  historyDateRange.value = [formatDate(start), formatDate(end)]
+  historyData.value = []
+  historyDialogVisible.value = true
+  loadHistoryData()
+}
+
+// 加载历史数据
+function loadHistoryData() {
+  if (!currentStation.value || !historyDateRange.value || historyDateRange.value.length !== 2) return
+  historyLoading.value = true
+  const [startTm, endTm] = historyDateRange.value
+  const config = STATION_BIZ_CONFIG[currentStation.value.tp]
+  const bizCode = config ? config.bizCode : null
+  listData(currentStation.value.stcd, startTm, endTm, bizCode).then(res => {
+    if (res.code === 200) {
+      historyData.value = res.data || []
+    } else {
+      historyData.value = []
+    }
+    historyLoading.value = false
+    nextTick(() => initHistoryChart())
+  }).catch(() => {
+    historyData.value = []
+    historyLoading.value = false
+  })
+}
+
+// 初始化历史数据图表
+function initHistoryChart() {
+  if (!historyChartRef.value) return
+  if (!historyChart) {
+    historyChart = echarts.init(historyChartRef.value)
+  }
+  if (!historyData.value || historyData.value.length === 0) {
+    historyChart.clear()
+    return
+  }
+  const config = STATION_BIZ_CONFIG[currentStation.value.tp]
+  if (!config || !config.historyChart || config.historyChart.length === 0) {
+    historyChart.clear()
+    return
+  }
+  const chartFields = config.historyChart
+  const sortedData = [...historyData.value].sort((a, b) => new Date(a.tm) - new Date(b.tm))
+  const xData = sortedData.map(d => fmtDateTime(null, null, d.tm))
+  const series = chartFields.map(field => {
+    const col = historyColumns.find(c => c.prop === field)
+    return {
+      name: col ? col.label : field,
+      type: 'line',
+      data: sortedData.map(d => d[field] ?? null),
+      smooth: true,
+      symbol: 'circle',
+      symbolSize: 4,
+      lineStyle: { width: 2 }
+    }
+  })
+  const option = {
+    tooltip: { trigger: 'axis' },
+    legend: { data: series.map(s => s.name), bottom: 0 },
+    grid: { left: '10%', right: '5%', top: '10%', bottom: '15%' },
+    xAxis: {
+      type: 'category',
+      data: xData,
+      axisLabel: { rotate: 30, fontSize: 11 }
+    },
+    yAxis: { type: 'value' },
+    series
+  }
+  historyChart.setOption(option, true)
+}
+
 function handlePointerMove(event) {
   const coord = toLonLat(event.coordinate)
   coordDisplay.lng = coord[0].toFixed(4)
@@ -330,7 +566,7 @@ async function loadTooltipData(station) {
   const data = await getLatestDataWithCache(station.stcd)
   if (currentHoverStcd !== station.stcd) return
   if (data) {
-    tooltipData.tm = data.tm || '-'
+    tooltipData.tm = fmtDate(data.tm)
     tooltipData.values = buildLatestValues(station.tp, data)
   } else {
     tooltipData.tm = '暂无数据'
@@ -554,6 +790,27 @@ watch(activeTab, (val) => {
   border-radius: 50%;
 }
 
+.layer-switch {
+  position: absolute;
+  top: 12px;
+  left: 12px;
+  z-index: 100;
+}
+
+.layer-switch :deep(.el-radio-button__inner) {
+  background: rgba(255, 255, 255, 0.9);
+  border-color: #dcdfe6;
+  color: #606266;
+  font-size: 12px;
+  padding: 6px 12px;
+}
+
+.layer-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
+  background: #1c97e7;
+  border-color: #1c97e7;
+  color: #fff;
+}
+
 .coord-display {
   position: absolute;
   bottom: 8px;
@@ -596,6 +853,12 @@ watch(activeTab, (val) => {
   color: #e8e8e8;
 }
 
+.map-tooltip .tooltip-divider {
+  height: 1px;
+  background: rgba(255, 255, 255, 0.15);
+  margin: 4px 0;
+}
+
 .map-tooltip .tooltip-row b {
   color: #fff;
   margin: 0 2px;
@@ -631,4 +894,59 @@ watch(activeTab, (val) => {
     gap: 10px;
   }
 }
+
+.history-content {
+  display: flex;
+  gap: 16px;
+  height: 520px;
+}
+
+.history-left {
+  flex: 1;
+  min-width: 0;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+
+.history-right {
+  width: 45%;
+  flex-shrink: 0;
+  display: flex;
+  flex-direction: column;
+}
+
+.chart-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 12px;
+  flex-shrink: 0;
+}
+
+.chart-header .toolbar-label {
+  font-size: 13px;
+  color: #606266;
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+.history-chart {
+  flex: 1;
+  min-height: 0;
+}
+
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 40px 0;
+  color: #909399;
+}
+
+.empty-icon {
+  font-size: 48px;
+  margin-bottom: 16px;
+}
 </style>

+ 496 - 0
gw-ui/src/views/xajgl/data/xajGdp.vue

@@ -0,0 +1,496 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
+      <el-form-item label="省份" prop="prov">
+        <el-input v-model="queryParams.prov" placeholder="请输入省份" clearable style="width: 150px" @keyup.enter="handleQuery" />
+      </el-form-item>
+      <el-form-item label="年份" prop="yr">
+        <el-input v-model="queryParams.yr" placeholder="请输入年份" clearable style="width: 120px" @keyup.enter="handleQuery" />
+      </el-form-item>
+      <el-form-item label="地市" prop="city">
+        <el-input v-model="queryParams.city" placeholder="请输入地市" clearable style="width: 150px" @keyup.enter="handleQuery" />
+      </el-form-item>
+      <el-form-item label="区县" prop="county">
+        <el-input v-model="queryParams.county" placeholder="请输入区县" clearable style="width: 150px" @keyup.enter="handleQuery" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</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" v-hasPermi="['xajgl:gdp:add']">新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleBatchDelete" v-hasPermi="['xajgl:gdp:remove']">删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['xajgl:gdp:import']">导入</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xajgl:gdp:export']">导出</el-button>
+      </el-col>
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
+    </el-row>
+
+    <el-table v-loading="loading" :data="gdpList" @selection-change="handleSelectionChange" border stripe>
+      <el-table-column type="selection" width="50" align="center" />
+      <el-table-column type="index" label="序号" width="60" align="center" />
+      <el-table-column label="省份" prop="prov" width="80" align="center" show-overflow-tooltip />
+      <el-table-column label="年份" prop="yr" width="80" align="center" />
+      <el-table-column label="地市" prop="city" width="100" align="center" show-overflow-tooltip />
+      <el-table-column label="区县" prop="county" width="100" align="center" show-overflow-tooltip />
+      <el-table-column label="人口总数(万人)" prop="pepSum" width="120" align="right" />
+      <el-table-column label="GDP合计(亿元)" prop="gdpSum" width="130" align="right" />
+      <el-table-column label="一产(亿元)" prop="gdp1Sum" width="100" align="right" />
+      <el-table-column label="二产(亿元)" prop="gdp2Sum" width="100" align="right" />
+      <el-table-column label="三产(亿元)" prop="gdp3Sum" width="100" align="right" />
+      <el-table-column label="城镇化率(%)" prop="peoRadio" width="110" align="right" />
+      <el-table-column label="农田有效灌溉面积(万亩)" prop="effectIrrArea" width="170" align="right" />
+      <el-table-column label="顺序" prop="orderNm" width="70" align="center" />
+      <el-table-column label="操作" width="150" align="center" fixed="right" class-name="small-padding fixed-width">
+        <template #default="scope">
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xajgl:gdp:edit']">修改</el-button>
+          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xajgl:gdp:remove']">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+
+    <!-- 添加或修改GDP数据对话框 -->
+    <el-dialog :title="title" v-model="open" width="900px" append-to-body>
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="180px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="省份" prop="prov">
+              <el-input v-model="form.prov" placeholder="请输入省份" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="年份" prop="yr">
+              <el-input v-model="form.yr" placeholder="请输入年份" :disabled="isEdit" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="地市" prop="city">
+              <el-input v-model="form.city" placeholder="请输入地市" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="区县" prop="county">
+              <el-input v-model="form.county" placeholder="请输入区县" :disabled="isEdit" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-divider content-position="left">人口数据</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="城市人口(万人)" prop="peoCitySum">
+              <el-input-number v-model="form.peoCitySum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="农村人口(万人)" prop="peoCountySum">
+              <el-input-number v-model="form.peoCountySum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="人口总数(万人)" prop="pepSum">
+              <el-input-number v-model="form.pepSum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="城镇化率(%)" prop="peoRadio">
+              <el-input-number v-model="form.peoRadio" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-divider content-position="left">GDP数据</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="一产(亿元)" prop="gdp1Sum">
+              <el-input-number v-model="form.gdp1Sum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="二产(亿元)" prop="gdp2Sum">
+              <el-input-number v-model="form.gdp2Sum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="三产(亿元)" prop="gdp3Sum">
+              <el-input-number v-model="form.gdp3Sum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="GDP合计(亿元)" prop="gdpSum">
+              <el-input-number v-model="form.gdpSum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="工业增加值(亿元)" prop="insduAdd">
+              <el-input-number v-model="form.insduAdd" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-divider content-position="left">农业数据</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="农田有效灌溉面积(万亩)" prop="effectIrrArea">
+              <el-input-number v-model="form.effectIrrArea" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="耕地面积(万亩)" prop="gdArea">
+              <el-input-number v-model="form.gdArea" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="耕地有效灌溉面积(万亩)" prop="effectGdArea">
+              <el-input-number v-model="form.effectGdArea" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="实际灌溉-耕地(万亩)" prop="effectGgGd">
+              <el-input-number v-model="form.effectGgGd" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="实际灌溉-林地(万亩)" prop="effectGgLd">
+              <el-input-number v-model="form.effectGgLd" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="实际灌溉-园地(万亩)" prop="effectGgYd">
+              <el-input-number v-model="form.effectGgYd" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="实际灌溉-牧草地(万亩)" prop="effectGgMcd">
+              <el-input-number v-model="form.effectGgMcd" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="鱼塘补水面积(万亩)" prop="ytbsArea">
+              <el-input-number v-model="form.ytbsArea" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="粮食产量(万t)" prop="argicuTt">
+              <el-input-number v-model="form.argicuTt" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-divider content-position="left">畜牧数据</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="大牲畜(万头)" prop="scBig">
+              <el-input-number v-model="form.scBig" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="小牲畜(万头)" prop="scSmall">
+              <el-input-number v-model="form.scSmall" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="牲畜合计(万头)" prop="scSum">
+              <el-input-number v-model="form.scSum" :precision="2" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="顺序" prop="orderNm">
+              <el-input-number v-model="form.orderNm" :controls="false" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 导入对话框 -->
+    <el-dialog title="导入GDP数据" v-model="importOpen" width="400px" append-to-body>
+      <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="uploadHeaders" :action="uploadUrl" :disabled="isUploading" :on-progress="handleUploadProgress" :on-success="handleUploadSuccess" :auto-upload="false" drag>
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <template #tip>
+          <div class="el-upload__tip text-center">
+            <el-checkbox v-model="updateSupport">是否更新已经存在的数据</el-checkbox>
+            <div>
+              <span>仅允许导入xls、xlsx格式文件。</span>
+              <el-link type="primary" underline="never" style="font-size: 12px; vertical-align: baseline" @click="handleDownloadTemplate">下载模板</el-link>
+            </div>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitUpload">确 定</el-button>
+          <el-button @click="importOpen = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="XajGdp">
+import { listGdp, getGdp, addGdp, updateGdp, delGdp, delGdpBatch, importGdpTemplate } from "@/api/xajgl/gdp";
+import { getToken } from "@/utils/auth";
+import { ElMessage } from "element-plus";
+
+const { proxy } = getCurrentInstance();
+
+const gdpList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const selections = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const open = ref(false);
+const importOpen = ref(false);
+const title = ref("");
+const isEdit = ref(false);
+const isUploading = ref(false);
+const updateSupport = ref(false);
+const uploadRef = ref(null);
+
+const uploadHeaders = { Authorization: 'Bearer ' + getToken() };
+const uploadUrl = computed(() => {
+  return import.meta.env.VITE_SERVICE_BASE_TITLE + '/xajgl/gdp/import?updateSupport=' + (updateSupport.value ? 1 : 0);
+});
+
+const data = reactive({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 15,
+    prov: undefined,
+    yr: undefined,
+    city: undefined,
+    county: undefined,
+  },
+  rules: {
+    prov: [{ required: true, message: "省份不能为空", trigger: "blur" }],
+    yr: [{ required: true, message: "年份不能为空", trigger: "blur" }],
+    city: [{ required: true, message: "地市不能为空", trigger: "blur" }],
+    county: [{ required: true, message: "区县不能为空", trigger: "blur" }],
+  },
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询GDP数据列表 */
+function getList() {
+  loading.value = true;
+  listGdp(queryParams.value).then((response) => {
+    gdpList.value = response.rows;
+    total.value = response.total;
+    loading.value = false;
+  });
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  proxy.resetForm("queryRef");
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+function handleSelectionChange(selection) {
+  selections.value = selection;
+  ids.value = selection.map((item) => item.county + '|' + item.yr);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+}
+
+/** 重置表单 */
+function reset() {
+  form.value = {
+    prov: undefined,
+    yr: undefined,
+    city: undefined,
+    county: undefined,
+    peoCitySum: undefined,
+    peoCountySum: undefined,
+    pepSum: undefined,
+    gdp1Sum: undefined,
+    gdp2Sum: undefined,
+    gdp3Sum: undefined,
+    gdpSum: undefined,
+    effectIrrArea: undefined,
+    peoRadio: undefined,
+    gdArea: undefined,
+    effectGdArea: undefined,
+    effectGgGd: undefined,
+    effectGgLd: undefined,
+    effectGgYd: undefined,
+    effectGgMcd: undefined,
+    ytbsArea: undefined,
+    scBig: undefined,
+    scSmall: undefined,
+    scSum: undefined,
+    insduAdd: undefined,
+    argicuTt: undefined,
+    orderNm: undefined,
+  };
+  proxy.resetForm("formRef");
+}
+
+/** 新增按钮操作 */
+function handleAdd() {
+  reset();
+  open.value = true;
+  title.value = "新增GDP数据";
+  isEdit.value = false;
+}
+
+/** 修改按钮操作 */
+function handleUpdate(row) {
+  reset();
+  isEdit.value = true;
+  getGdp(row.county, row.yr).then((response) => {
+    form.value = response.data;
+    open.value = true;
+    title.value = "修改GDP数据";
+  });
+}
+
+/** 取消按钮操作 */
+function cancel() {
+  open.value = false;
+}
+
+/** 提交按钮 */
+function submitForm() {
+  proxy.$refs["formRef"].validate((valid) => {
+    if (valid) {
+      if (isEdit.value) {
+        updateGdp(form.value).then(() => {
+          proxy.$modal.msgSuccess("修改成功");
+          open.value = false;
+          getList();
+        });
+      } else {
+        addGdp(form.value).then(() => {
+          proxy.$modal.msgSuccess("新增成功");
+          open.value = false;
+          getList();
+        });
+      }
+    }
+  });
+}
+
+/** 删除按钮操作 */
+function handleDelete(row) {
+  proxy.$modal.confirm('是否确认删除区县【' + row.county + '】年份【' + row.yr + '】的数据?').then(() => {
+    return delGdp({ county: row.county, yr: row.yr });
+  }).then(() => {
+    getList();
+    proxy.$modal.msgSuccess("删除成功");
+  }).catch(() => {});
+}
+
+/** 批量删除按钮操作 */
+function handleBatchDelete() {
+  const countyList = selections.value.map(item => item.county);
+  proxy.$modal.confirm('是否确认删除选中的' + selections.value.length + '条数据?').then(() => {
+    return delGdpBatch(selections.value);
+  }).then(() => {
+    getList();
+    proxy.$modal.msgSuccess("删除成功");
+  }).catch(() => {});
+}
+
+/** 导出按钮操作 */
+function handleExport() {
+  proxy.download("/xajgl/gdp/export", { ...queryParams.value }, `xaj_gdp_${new Date().getTime()}.xlsx`);
+}
+
+/** 导入按钮操作 */
+function handleImport() {
+  isUploading.value = false;
+  updateSupport.value = false;
+  importOpen.value = true;
+  nextTick(() => {
+    uploadRef.value?.clearFiles();
+  });
+}
+
+/** 下载模板 */
+async function handleDownloadTemplate() {
+  try {
+    const response = await importGdpTemplate()
+    const blob = new Blob([response])
+    const url = window.URL.createObjectURL(blob)
+    const link = document.createElement('a')
+    link.href = url
+    link.download = '新安江流域GDP导入模板.xlsx'
+    document.body.appendChild(link)
+    link.click()
+    document.body.removeChild(link)
+    window.URL.revokeObjectURL(url)
+    ElMessage.success('模板下载成功')
+  } catch (error) {
+    ElMessage.error('模板下载失败')
+  }
+}
+
+/** 上传进度 */
+function handleUploadProgress() {
+  isUploading.value = true;
+}
+
+/** 上传成功 */
+function handleUploadSuccess(response) {
+  importOpen.value = false;
+  isUploading.value = false;
+  uploadRef.value?.clearFiles();
+  proxy.$alert("<div style='overflow:auto;overflow-x:hidden;max-height:70vh;padding:10px 20px 0;'>" + response.msg + '</div>', '导入结果', { dangerouslyUseHTMLString: true });
+  getList();
+}
+
+/** 提交上传 */
+function submitUpload() {
+  uploadRef.value.submit();
+}
+
+getList();
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+.mb8 {
+  margin-bottom: 8px;
+}
+</style>