董国庆
2026-04-01 dd95eaa2cabb746eede2962f22af258ee0a18cdf
feat(header): 增强首页导航逻辑并存储clientId

- 在项目首页存储clientId至sessionStorage,用于后续导航
- 重构header组件模板,改善代码格式和可读性
- 扩展toHome方法,支持根据用户类型和存储状态进行智能路由
- 添加当用户类型未知时,根据存储的projectType和clientId进行路由跳转
3个文件已修改
1280 ■■■■■ 已修改文件
screen/src/view/header/index.vue 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/home/index.vue 1036 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/projectHome/index.vue 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/header/index.vue
@@ -1,11 +1,18 @@
<template>
  <div class="txt-center h--85 w100 relative imgBg fixed z-10" style="display: flex;justify-content: center;align-items: center;">
  <div
    class="txt-center h--85 w100 relative imgBg fixed z-10"
    style="display: flex; justify-content: center; align-items: center"
  >
    <div
      @click="toHome"
      class="flex a-center c-fff absolute adress1 cursor-pointer"
    >
      <img
        :src="clientLogo && clientLogo!='null' ? (VUE_APP_IMAGE_BASE_URL + '/project/download/' + clientLogo) : require('../../assets/img/logo.png')"
        :src="
          clientLogo && clientLogo != 'null'
            ? VUE_APP_IMAGE_BASE_URL + '/project/download/' + clientLogo
            : require('../../assets/img/logo.png')
        "
        class="w--30 h--30 shrink0 mr--10"
      />
      <div class="color2 lh--30 fs--30 SHSBold">
@@ -16,8 +23,18 @@
      ref="titleEl"
      class="fs--43 lh--43 color3 SHSBold letter--6"
      :style="[
        { width: '660px', textAlign: 'center', whiteSpace: titleWhiteSpace, display: 'inline-block' },
        { fontSize: titleFontSize + 'px', transform: 'scale(' + titleScale + ')', transformOrigin: 'center top', letterSpacing: titleLetterSpacing }
        {
          width: '660px',
          textAlign: 'center',
          whiteSpace: titleWhiteSpace,
          display: 'inline-block',
        },
        {
          fontSize: titleFontSize + 'px',
          transform: 'scale(' + titleScale + ')',
          transformOrigin: 'center top',
          letterSpacing: titleLetterSpacing,
        },
      ]"
    >
      {{ title || "智慧工程安全监管平台" }}
@@ -35,14 +52,30 @@
    <!-- 退出登录确认弹窗 -->
    <transition name="fade">
      <div v-if="showLogoutDialog" class="logout-mask" @click.self="showLogoutDialog = false">
      <div
        v-if="showLogoutDialog"
        class="logout-mask"
        @click.self="showLogoutDialog = false"
      >
        <div class="logout-dialog">
          <div class="dialog-title">退出登录</div>
          <div class="dialog-content">
            <div class="dialog-icon">
              <svg width="60" height="60" viewBox="0 0 60 60" fill="none">
                <circle cx="30" cy="30" r="28" stroke="#09B9FF" stroke-width="2" opacity="0.3"/>
                <path d="M30 20V32M30 38V40" stroke="#09B9FF" stroke-width="3" stroke-linecap="round"/>
                <circle
                  cx="30"
                  cy="30"
                  r="28"
                  stroke="#09B9FF"
                  stroke-width="2"
                  opacity="0.3"
                />
                <path
                  d="M30 20V32M30 38V40"
                  stroke="#09B9FF"
                  stroke-width="3"
                  stroke-linecap="round"
                />
              </svg>
            </div>
            <div class="dialog-text">确定要退出登录吗?</div>
@@ -62,139 +95,169 @@
  props: {
    title: {
      type: String,
      default: ''
      default: "",
    },
    name: {
      type: String,
      default: ''
      default: "",
    },
    logo: {
      type: String,
      default: ''
    }
      default: "",
    },
  },
  data () {
  data() {
    return {
      currentDate: '',
      clientName: sessionStorage.getItem('clientName'),
      clientLogo: sessionStorage.getItem('clientLogo'),
      currentDate: "",
      clientName: sessionStorage.getItem("clientName"),
      clientLogo: sessionStorage.getItem("clientLogo"),
      VUE_APP_IMAGE_BASE_URL: process.env.VUE_APP_IMAGE_BASE_URL,
      showLogoutDialog: false,
      titleFontSize: 43,
      titleScale: 1,
      titleWhiteSpace: 'normal',
      titleLetterSpacing: null
    }
      titleWhiteSpace: "normal",
      titleLetterSpacing: null,
    };
  },
  mounted () {
    this.updateDateTime()
    console.log('clientLogo', this.clientLogo)
    this.timer = setInterval(this.updateDateTime, 1000)
  mounted() {
    this.updateDateTime();
    console.log("clientLogo", this.clientLogo);
    this.timer = setInterval(this.updateDateTime, 1000);
    this.$nextTick(() => {
      this.adjustTitleFontSize()
      window.addEventListener('resize', this.adjustTitleFontSize)
    })
      this.adjustTitleFontSize();
      window.addEventListener("resize", this.adjustTitleFontSize);
    });
  },
  beforeDestroy () {
  beforeDestroy() {
    if (this.timer) {
      clearInterval(this.timer)
      clearInterval(this.timer);
    }
    window.removeEventListener('resize', this.adjustTitleFontSize)
    window.removeEventListener("resize", this.adjustTitleFontSize);
  },
  watch: {
    title () {
      this.$nextTick(this.adjustTitleFontSize)
    title() {
      this.$nextTick(this.adjustTitleFontSize);
    },
    name () {
      this.clientName = this.name
    name() {
      this.clientName = this.name;
    },
    logo () {
      this.clientLogo = this.logo
    }
    logo() {
      this.clientLogo = this.logo;
    },
  },
  methods: {
    adjustTitleFontSize () {
      const el = this.$refs.titleEl
      if (!el) return
    adjustTitleFontSize() {
      const el = this.$refs.titleEl;
      if (!el) return;
      // Reset styles to start measurement
      this.titleWhiteSpace = 'nowrap'
      this.titleScale = 1
      this.titleLetterSpacing = null
      this.titleWhiteSpace = "nowrap";
      this.titleScale = 1;
      this.titleLetterSpacing = null;
      const maxSize = 43
      const minSize = 20
      let size = maxSize
      el.style.fontSize = size + 'px'
      const limit = el.clientWidth
      let guard = 0
      const maxSize = 43;
      const minSize = 20;
      let size = maxSize;
      el.style.fontSize = size + "px";
      const limit = el.clientWidth;
      let guard = 0;
      // Reduce font size until it fits or hits the minimum
      while (el.scrollWidth > limit && size > minSize && guard < 50) {
        size -= 1
        el.style.fontSize = size + 'px'
        guard++
        size -= 1;
        el.style.fontSize = size + "px";
        guard++;
      }
      this.titleFontSize = size
      this.titleFontSize = size;
      // If it still overflows at min font size, allow wrapping
      if (el.scrollWidth > limit) {
        this.titleWhiteSpace = 'normal'
        this.titleWhiteSpace = "normal";
      }
    },
    toHome () {
      let userType = null
    toHome() {
      let userType = null;
      try {
        const userInfoStr = sessionStorage.getItem('userInfo')
        const userInfo = userInfoStr ? JSON.parse(userInfoStr) : null
        userType = Number(userInfo && userInfo.type)
        const userInfoStr = sessionStorage.getItem("userInfo");
        const userInfo = userInfoStr ? JSON.parse(userInfoStr) : null;
        userType = Number(userInfo && userInfo.type);
      } catch (e) {
        userType = null
        userType = null;
      }
      if (userType === 2) {
        if (this.$route.path === '/projectHome') {
          return
        if (this.$route.path === "/projectHome") {
          return;
        }
        this.$router.replace({
          path: '/projectHome',
          path: "/projectHome",
          query: {
            projectId: this.$route.query.projectId,
            projectName: this.$route.query.projectName
          }
        })
            projectName: this.$route.query.projectName,
          },
        });
        sessionStorage.setItem("projectType", userType);
      } else if (userType === 1) {
        if (this.$route.path === '/home') {
          return
        if (this.$route.path === "/home") {
          return;
        }
        this.$router.replace({
          path: '/home'
        })
          path: "/home",
        });
        sessionStorage.setItem("projectType", userType);
      } else {
        // userType = sessionStorage.getItem('projectType')
        if (sessionStorage.getItem("projectType") == 2) {
          if (this.$route.path === "/projectHome") {
            this.$router.replace({
              path: "/home",
              query:{
                clientId: sessionStorage.getItem("clientId"),
              }
            });
            sessionStorage.setItem("projectType", 1);
          } else {
            this.$router.replace({
              path: "/projectHome",
              query: {
                projectId: this.$route.query.projectId,
                projectName: this.$route.query.projectName,
              },
            });
          }
        } else {
          if (this.$route.path === "/home") {
            return;
          }
          this.$router.replace({
            path: "/home",
          });
        }
      }
      sessionStorage.setItem('projectType', userType)
    },
    updateDateTime () {
      const now = new Date()
    updateDateTime() {
      const now = new Date();
      // 更新日期 (YYYY年MM月DD日)
      this.currentDate = now.toLocaleDateString('zh-CN', {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      })
      this.currentDate = now.toLocaleDateString("zh-CN", {
        year: "numeric",
        month: "long",
        day: "numeric",
      });
    },
    handleLogout () {
    handleLogout() {
      // 清除所有登录信息
      sessionStorage.clear()
      sessionStorage.clear();
      // 关闭弹窗
      this.showLogoutDialog = false
      this.showLogoutDialog = false;
      // 跳转到登录页
      this.$router.replace({
        path: '/'
      })
    }
  }
}
        path: "/",
      });
    },
  },
};
</script>
<style scoped lang="less">
@@ -260,8 +323,7 @@
  background: linear-gradient(180deg, #0a1e3f 0%, #051429 100%);
  border: 2px solid rgba(9, 185, 255, 0.3);
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5),
              0 0 60px rgba(9, 185, 255, 0.15);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 60px rgba(9, 185, 255, 0.15);
  overflow: hidden;
  animation: dialogSlideIn 0.3s ease-out;
}
@@ -283,7 +345,11 @@
  font-size: 24px;
  font-weight: bold;
  color: #ffffff;
  background: linear-gradient(90deg, rgba(9, 185, 255, 0.15) 0%, rgba(77, 104, 255, 0.05) 100%);
  background: linear-gradient(
    90deg,
    rgba(9, 185, 255, 0.15) 0%,
    rgba(77, 104, 255, 0.05) 100%
  );
  border-bottom: 1px solid rgba(9, 185, 255, 0.2);
  letter-spacing: 1px;
}
@@ -301,7 +367,8 @@
}
@keyframes iconPulse {
  0%, 100% {
  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }
screen/src/view/home/index.vue
@@ -40,7 +40,10 @@
          src="../../assets/img/home/Group@2x (4).png"
          class="w--110 h--110 shrink0"
        />
        <div @click="toHiddenDanger" class="hoverBox bgimg4 pt--10 c-fff cursor-pointer">
        <div
          @click="toHiddenDanger"
          class="hoverBox bgimg4 pt--10 c-fff cursor-pointer"
        >
          <div class="flex a-center ml--14">
            <div class="ripple">
              <div class="ripple-wave wave-1"></div>
@@ -308,20 +311,14 @@
                    />
                    <div class="ml--7 SHSR shrink0">
                      <div class="color2 fs--14 lh--14">房建</div>
                      <div
                        class="mt--9 fs--8 lh--8 color3"
                      >
                        本月检查覆盖率
                      </div>
                      <div class="mt--9 fs--8 lh--8 color3">本月检查覆盖率</div>
                    </div>
                  </div>
                  <div class="ml--11 txt-center">
                    <div class="c-fff fs--22 lh--22 SHSBold">
                      {{ screenData.housingProjectCount }}
                    </div>
                    <div
                      class="mt--9 fs--10 lh--12 color4 SHSM"
                    >
                    <div class="mt--9 fs--10 lh--12 color4 SHSM">
                      {{ screenData.housingInspectionCoverage }}%
                    </div>
                  </div>
@@ -334,20 +331,14 @@
                    />
                    <div class="ml--7 SHSR shrink0">
                      <div class="color2 fs--14 lh--14">市政</div>
                      <div
                        class="mt--9 fs--8 lh--8 color3"
                      >
                        本月检查覆盖率
                      </div>
                      <div class="mt--9 fs--8 lh--8 color3">本月检查覆盖率</div>
                    </div>
                  </div>
                  <div class="ml--11 txt-center">
                    <div class="c-fff fs--22 lh--22 SHSBold">
                      {{ screenData.municipalProjectCount }}
                    </div>
                    <div
                      class="mt--9 fs--10 lh--12 color4 SHSM"
                    >
                    <div class="mt--9 fs--10 lh--12 color4 SHSM">
                      {{ screenData.municipalInspectionCoverage }}%
                    </div>
                  </div>
@@ -423,7 +414,7 @@
                      <div class="fs--45 lh--45 ff54- color8">
                        {{ screenData.inspectedMachineryTotal }}
                      </div>
                      <div class="mt--20  oa">
                      <div class="mt--20 oa">
                        <div
                          v-for="(
                            item, index
@@ -752,38 +743,39 @@
</template>
<script>
import HeaderBox from '../header/index.vue'
import moment from 'moment'
import s1 from '../../assets/img/home/s1.png'
import s2 from '../../assets/img/home/s2.png'
import s3 from '../../assets/img/home/s3.png'
import f1 from '../../assets/img/home/f1.png'
import f2 from '../../assets/img/home/f2.png'
import f3 from '../../assets/img/home/f3.png'
import z1 from '../../assets/img/home/assets1.png'
import z2 from '../../assets/img/home/assets2.png'
import z3 from '../../assets/img/home/assets3.png'
import s4 from '../../assets/img/home/s4.png'
import f4 from '../../assets/img/home/f4.png'
import z4 from '../../assets/img/home/z4.png'
import HeaderBox from "../header/index.vue";
import moment from "moment";
import s1 from "../../assets/img/home/s1.png";
import s2 from "../../assets/img/home/s2.png";
import s3 from "../../assets/img/home/s3.png";
import f1 from "../../assets/img/home/f1.png";
import f2 from "../../assets/img/home/f2.png";
import f3 from "../../assets/img/home/f3.png";
import z1 from "../../assets/img/home/assets1.png";
import z2 from "../../assets/img/home/assets2.png";
import z3 from "../../assets/img/home/assets3.png";
import s4 from "../../assets/img/home/s4.png";
import f4 from "../../assets/img/home/f4.png";
import z4 from "../../assets/img/home/z4.png";
import {
  getStatistics,
  getStoryList,
  getProjectSelect,
  getWorkPresentationList,
  getprojectTooltip
} from './service'
  getprojectTooltip,
} from "./service";
import { getLoginInfo } from "../projectHome/service";
export default {
  components: {
    HeaderBox
    HeaderBox,
  },
  data () {
  data() {
    return {
      mapView: null,
      mapPromise: null,
      dateRange: [moment().startOf('month'), moment().endOf('month')], // 默认本月
      dateTxt: '',
      dateRange: [moment().startOf("month"), moment().endOf("month")], // 默认本月
      dateTxt: "",
      activeTab: 1,
      scrollData: [], // 存储滚动数据
      scrollTimer: null, // 滚动定时器
@@ -793,604 +785,638 @@
      categoryFilter: {
        municipal: true,
        house: true,
        assets: true
        assets: true,
      },
      levelFilter: {
        A: true,
        B: true,
        C: true
        C: true,
      },
      markers: [],
      // 2项目总数(在建工程) 3大型机械设备数(在建工程) 4重大隐患数(在建工程) 5项目总数(固定资产) 6重大隐患数(固定资产) 7事故简讯 8工作展示
      permissionClient: JSON.parse(sessionStorage.getItem('permissionClient')),
      permissionClient: JSON.parse(sessionStorage.getItem("permissionClient")),
      // 9各分项隐患趋势 10隐患数量 11设备数量 12查看报告入口 13人员在岗率 14重大风险源 15 项目风险趋势
      permissionProject: JSON.parse(sessionStorage.getItem('permissionProject')),
      permissionProject: JSON.parse(
        sessionStorage.getItem("permissionProject")
      ),
      screenData: {},
      searchTabs: ['本周', '本月', '本季度', '本年'],
      activeTabs: '本年',
      searchTabs: ["本周", "本月", "本季度", "本年"],
      activeTabs: "本年",
      workListData: [],
      VUE_APP_IMAGE_BASE_URL:
        process.env.VUE_APP_IMAGE_BASE_URL + '/project/download/',
        process.env.VUE_APP_IMAGE_BASE_URL + "/project/download/",
      projectList: [],
      tooltipVisible: false,
      tooltipPos: { left: 0, top: 0 },
      tooltipData: {},
      tooltipLoading: false,
      tooltipError: false,
      boundaryPolygons: []
    }
      boundaryPolygons: [],
    };
  },
  watch: {
    dateRange: {
      handler (newVal, oldVal) {
      handler(newVal, oldVal) {
        if (newVal.length > 0) {
          this.dateTxt =
            moment(newVal[0]).format('YYYY年MM月DD日') +
            ' ~ ' +
            moment(newVal[1]).format('YYYY年MM月DD日')
            moment(newVal[0]).format("YYYY年MM月DD日") +
            " ~ " +
            moment(newVal[1]).format("YYYY年MM月DD日");
        } else {
          this.dateTxt = ''
          this.dateTxt = "";
        }
        this.$nextTick(() => {
          this.getScreenData()
        })
          this.getScreenData();
        });
      },
      immediate: true
    }
      immediate: true,
    },
  },
  created () {
    if (sessionStorage.getItem('activeTab')) {
      this.activeTab = Number(sessionStorage.getItem('activeTab'))
  created() {
    if (this.$route.query.clientId) {
      // alert(this.$route.query.clientId)
      getLoginInfo({ id: this.$route.query.clientId, type: 1 }).then((r) => {
        if (r) {
          this.permissionProject =  r.permissionProject ? r.permissionProject.split(",") : [];
          this.permissionClient =  r.permissionClient ? r.permissionClient.split(",") : [];
          sessionStorage.setItem(
            "permissionClient",
            JSON.stringify(
              r.permissionClient ? r.permissionClient.split(",") : []
            )
          );
          sessionStorage.setItem(
            "permissionProject",
            JSON.stringify(
              r.permissionProject ? r.permissionProject.split(",") : []
            )
          );
          sessionStorage.setItem("clientId", params.get("clientId"));
          sessionStorage.setItem("clientName", params.get("clientName"));
          sessionStorage.setItem("clientLogo", r.logo);
          sessionStorage.setItem("deviceTemplateName", r.deviceTemplateName); // 设备模板id
          sessionStorage.setItem("deviceTemplateId", r.deviceTemplateId); // 设备模板id
          sessionStorage.setItem("safetyId", r.safetyId); // 安全报告模板id
          sessionStorage.setItem("safetyName", r.safetyName); // 安全报告模板id
          sessionStorage.setItem("bigHiddenDanger", r.bigHiddenDanger); // 1开启 2关闭 重大隐患
          sessionStorage.setItem("generalHiddenDanger", r.generalHiddenDanger); // 1开启 2关闭 一般隐患
          sessionStorage.setItem("commentType", r.commentType); // 1模糊评价 2打分评价
        }
      });
    }
    if (sessionStorage.getItem("activeTab")) {
      this.activeTab = Number(sessionStorage.getItem("activeTab"));
    }
    this.mapPromise = window.AMapLoader.load({
      key: '232929db1cb31f26d42b3645a7456154',
      version: '2.0',
      plugins: ['AMap.DistrictSearch', 'AMap.Geocoder'],
      key: "232929db1cb31f26d42b3645a7456154",
      version: "2.0",
      plugins: ["AMap.DistrictSearch", "AMap.Geocoder"],
      AMapUI: {
        version: '1.1',
        plugins: ['geo/DistrictExplorer']
      }
    })
    this.getStoryData()
        version: "1.1",
        plugins: ["geo/DistrictExplorer"],
      },
    });
    this.getStoryData();
    getWorkPresentationList({
      clientId: sessionStorage.getItem('clientId'),
      clientId: sessionStorage.getItem("clientId"),
      state: 1,
      pageNum: 1,
      pageSize: 9999
      pageSize: 9999,
    }).then((res) => {
      this.workListData = res.records
    })
      this.workListData = res.records;
    });
  },
  mounted () {
  mounted() {
    this.$nextTick(() => {
      this.scrollContainer = this.$refs.scrollContainer
    })
      this.scrollContainer = this.$refs.scrollContainer;
    });
  },
  beforeDestroy () {
    sessionStorage.setItem('activeTab', this.activeTab)
    this.stopAutoScroll()
  beforeDestroy() {
    sessionStorage.setItem("activeTab", this.activeTab);
    this.stopAutoScroll();
    if (this._mapResizeHandler) {
      window.removeEventListener('resize', this._mapResizeHandler)
      this._mapResizeHandler = null
      window.removeEventListener("resize", this._mapResizeHandler);
      this._mapResizeHandler = null;
    }
    if (this.mapView) {
      this.mapView.destroy()
      this.mapView = null
      this.mapView.destroy();
      this.mapView = null;
    }
  },
  methods: {
    handleTabClick (tab) {
      this.activeTabs = tab
      this.getStoryData()
    handleTabClick(tab) {
      this.activeTabs = tab;
      this.getStoryData();
    },
    changeTab (type) {
      if (this.activeTab === type) return
      this.activeTab = type
      this.getScreenData()
    changeTab(type) {
      if (this.activeTab === type) return;
      this.activeTab = type;
      this.getScreenData();
    },
    getStoryData () {
      let startTime = moment()
      let endTime = moment()
      if (this.activeTabs === '本周') {
        startTime = moment().startOf('isoWeek')
        endTime = moment().endOf('isoWeek')
      } else if (this.activeTabs === '本月') {
        startTime = moment().startOf('month')
        endTime = moment().endOf('month')
      } else if (this.activeTabs === '本季度') {
        startTime = moment().startOf('quarter')
        endTime = moment().endOf('quarter')
      } else if (this.activeTabs === '本年') {
        startTime = moment().startOf('year')
        endTime = moment().endOf('year')
    getStoryData() {
      let startTime = moment();
      let endTime = moment();
      if (this.activeTabs === "本周") {
        startTime = moment().startOf("isoWeek");
        endTime = moment().endOf("isoWeek");
      } else if (this.activeTabs === "本月") {
        startTime = moment().startOf("month");
        endTime = moment().endOf("month");
      } else if (this.activeTabs === "本季度") {
        startTime = moment().startOf("quarter");
        endTime = moment().endOf("quarter");
      } else if (this.activeTabs === "本年") {
        startTime = moment().startOf("year");
        endTime = moment().endOf("year");
      }
      let time =
        startTime.format('YYYY-MM-DD') + ' - ' + endTime.format('YYYY-MM-DD')
        startTime.format("YYYY-MM-DD") + " - " + endTime.format("YYYY-MM-DD");
      getStoryList({
        time: time
        time: time,
      }).then((res) => {
        this.scrollData = res
        this.scrollData = res;
        if (this.scrollData.length > 4) {
          this.initScrollData()
          this.initScrollData();
        }
      })
      });
    },
    getScreenData () {
    getScreenData() {
      let params = {
        projectCategory: ['ongoing', 'fixed'][this.activeTab - 1],
        startDate: '',
        endDate: '',
        clientId: sessionStorage.getItem('clientId')
      }
        projectCategory: ["ongoing", "fixed"][this.activeTab - 1],
        startDate: "",
        endDate: "",
        clientId: sessionStorage.getItem("clientId"),
      };
      if (this.dateRange.length) {
        params.startDate = moment(this.dateRange[0]).format('YYYY-MM-DD')
        params.endDate = moment(this.dateRange[1]).format('YYYY-MM-DD')
        params.startDate = moment(this.dateRange[0]).format("YYYY-MM-DD");
        params.endDate = moment(this.dateRange[1]).format("YYYY-MM-DD");
      }
      getProjectSelect(params).then((res) => {
        this.projectList = res
      })
        this.projectList = res;
      });
      getStatistics(params).then((res) => {
        this.screenData = res.ongoingProject || res.fixedAsset
        this.setMapload()
      })
        this.screenData = res.ongoingProject || res.fixedAsset;
        this.setMapload();
      });
    },
    toAccidentCaseDetail (item) {
    toAccidentCaseDetail(item) {
      this.$router.push({
        path: '/accidentCase/detail',
        path: "/accidentCase/detail",
        query: {
          id: item.id
        }
      })
          id: item.id,
        },
      });
    },
    toAccidentCaseList () {
      this.$router.push('/accidentCase')
    toAccidentCaseList() {
      this.$router.push("/accidentCase");
    },
    toWorkDetail (item) {
    toWorkDetail(item) {
      this.$router.push({
        path: '/workList/detail',
        path: "/workList/detail",
        query: {
          id: item.id
        }
      })
          id: item.id,
        },
      });
    },
    toWorkList () {
      this.$router.push('/workList')
    toWorkList() {
      this.$router.push("/workList");
    },
    toPorjectList () {
    toPorjectList() {
      const query = {
        projectCategory: ['ongoing', 'fixed'][this.activeTab - 1]
      }
        projectCategory: ["ongoing", "fixed"][this.activeTab - 1],
      };
      // 如果有选择日期范围,则传递开始和结束时间
      if (this.dateRange && this.dateRange.length === 2) {
        query.startTime = moment(this.dateRange[0]).format(
          'YYYY-MM-DD HH:mm:ss'
        )
        query.endTime = moment(this.dateRange[1]).format('YYYY-MM-DD HH:mm:ss')
          "YYYY-MM-DD HH:mm:ss"
        );
        query.endTime = moment(this.dateRange[1]).format("YYYY-MM-DD HH:mm:ss");
      }
      this.$router.push({
        path: '/projectList',
        query: query
      })
        path: "/projectList",
        query: query,
      });
    },
    toEquipmentList () {
      const query = {}
    toEquipmentList() {
      const query = {};
      // 如果有选择日期范围,则传递开始和结束时间
      if (this.dateRange && this.dateRange.length === 2) {
        query.startTime = moment(this.dateRange[0]).format(
          'YYYY-MM-DD HH:mm:ss'
        )
        query.endTime = moment(this.dateRange[1]).format('YYYY-MM-DD HH:mm:ss')
          "YYYY-MM-DD HH:mm:ss"
        );
        query.endTime = moment(this.dateRange[1]).format("YYYY-MM-DD HH:mm:ss");
      }
      this.$router.push({
        path: '/equipmentList',
        query: query
      })
        path: "/equipmentList",
        query: query,
      });
    },
    toHiddenDanger () {
    toHiddenDanger() {
      const query = {
        projectCategory: ['ongoing', 'fixed'][this.activeTab - 1]
      }
        projectCategory: ["ongoing", "fixed"][this.activeTab - 1],
      };
      // 如果有选择日期范围,则传递开始和结束时间
      if (this.dateRange && this.dateRange.length === 2) {
        query.startTime = moment(this.dateRange[0]).format(
          'YYYY-MM-DD HH:mm:ss'
        )
        query.endTime = moment(this.dateRange[1]).format('YYYY-MM-DD HH:mm:ss')
          "YYYY-MM-DD HH:mm:ss"
        );
        query.endTime = moment(this.dateRange[1]).format("YYYY-MM-DD HH:mm:ss");
      }
      this.$router.push({
        path: '/hiddenDanger',
        query: query
      })
        path: "/hiddenDanger",
        query: query,
      });
    },
    // 切换活动索引
    changeActiveIndex (index) {
      this.activeIndex = index
      this.$refs.carousel.setActiveItem(index)
    changeActiveIndex(index) {
      this.activeIndex = index;
      this.$refs.carousel.setActiveItem(index);
    },
    // 切换活动索引
    handleChangeCarousel (index) {
      this.activeIndex = index
    handleChangeCarousel(index) {
      this.activeIndex = index;
    },
    handleClick () {
      this.$refs.datePicker.focus()
    handleClick() {
      this.$refs.datePicker.focus();
    },
    handleSearch () {
    handleSearch() {
      if (!this.projectId) {
        return
        return;
      }
      const target = this.screenData.projectMapPoints.find(
        (item) => item.projectId === this.projectId
      )
      );
      if (!target || Object.keys(target).length === 0) {
        this.$message.warning('无此项目地图点位')
        return
        this.$message.warning("无此项目地图点位");
        return;
      }
      if (target.longitude && target.latitude) {
        // 定位地图
        this.mapView.setZoomAndCenter(15, [
          Number(target.longitude),
          Number(target.latitude)
        ])
          Number(target.latitude),
        ]);
        // 显示悬浮窗
        setTimeout(() => {
          this.showTooltip(null, {
            projectId: target.projectId,
            position: [Number(target.longitude), Number(target.latitude)]
          })
        }, 1000)
            position: [Number(target.longitude), Number(target.latitude)],
          });
        }, 1000);
      } else {
        this.$message.warning('该项目暂无位置信息')
        this.$message.warning("该项目暂无位置信息");
      }
    },
    // 初始化滚动数据
    initScrollData () {
      this.stopAutoScroll()
      if (!this.scrollData || this.scrollData.length === 0) return
    initScrollData() {
      this.stopAutoScroll();
      if (!this.scrollData || this.scrollData.length === 0) return;
      // 为了实现无缝滚动,复制2份数据
      this.scrollData = [...this.scrollData, ...this.scrollData]
      this.scrollData = [...this.scrollData, ...this.scrollData];
      // 开始滚动
      this.$nextTick(() => {
        this.startAutoScroll()
      })
        this.startAutoScroll();
      });
    },
    // 开始自动滚动
    startAutoScroll () {
      this.stopAutoScroll()
    startAutoScroll() {
      this.stopAutoScroll();
      if (!this.scrollContainer || !this.scrollData.length) return
      if (!this.scrollContainer || !this.scrollData.length) return;
      const originalDataLength = this.scrollData.length / 2
      const originalDataLength = this.scrollData.length / 2;
      // 动态计算高度
      let firstHalfHeight = 0
      const wrapper = this.scrollContainer.children[0]
      let firstHalfHeight = 0;
      const wrapper = this.scrollContainer.children[0];
      if (wrapper && wrapper.children.length) {
        for (let i = 0; i < originalDataLength; i++) {
          if (wrapper.children[i]) {
            firstHalfHeight += wrapper.children[i].offsetHeight
            firstHalfHeight += wrapper.children[i].offsetHeight;
          }
        }
      }
      // 兜底
      if (firstHalfHeight === 0) firstHalfHeight = originalDataLength * 102
      if (firstHalfHeight === 0) firstHalfHeight = originalDataLength * 102;
      // 判断是否需要跳转的标志
      let shouldJump = false
      let shouldJump = false;
      const scroll = () => {
        let currentScrollTop = this.scrollContainer.scrollTop
        let currentScrollTop = this.scrollContainer.scrollTop;
        // 当滚动到第二份的末尾时
        if (currentScrollTop >= firstHalfHeight) {
          // 计算当前滚动位置相对于第二份数据开始的偏移量
          const offset = currentScrollTop - firstHalfHeight
          const offset = currentScrollTop - firstHalfHeight;
          // 直接跳到第一份数据相同偏移量的位置
          this.scrollContainer.scrollTop = offset
          this.scrollContainer.scrollTop = offset;
          // 设置标志表示刚刚进行过跳转
          shouldJump = true
          shouldJump = true;
        } else {
          // 如果在跳转后,并且位置重置到了0附近,重置标志
          if (shouldJump && currentScrollTop < 2) {
            shouldJump = false
            shouldJump = false;
          }
        }
        // 只有在不处于跳转状态时才继续滚动
        if (!shouldJump) {
          this.scrollContainer.scrollTop += 1
          this.scrollContainer.scrollTop += 1;
        }
        this.scrollTimer = requestAnimationFrame(scroll)
      }
        this.scrollTimer = requestAnimationFrame(scroll);
      };
      // 开始滚动
      this.scrollTimer = requestAnimationFrame(scroll)
      this.scrollTimer = requestAnimationFrame(scroll);
    },
    // 停止自动滚动
    stopAutoScroll () {
    stopAutoScroll() {
      if (this.scrollTimer) {
        cancelAnimationFrame(this.scrollTimer)
        this.scrollTimer = null
        cancelAnimationFrame(this.scrollTimer);
        this.scrollTimer = null;
      }
    },
    // 鼠标进入容器时暂停滚动
    handleMouseEnter () {
      this.stopAutoScroll()
    handleMouseEnter() {
      this.stopAutoScroll();
    },
    // 鼠标离开容器时恢复滚动
    handleMouseLeave () {
      this.startAutoScroll()
    handleMouseLeave() {
      this.startAutoScroll();
    },
    // 处理滚轮事件
    handleWheel (event) {
      event.preventDefault()
      this.scrollContainer.scrollTop += event.deltaY
    handleWheel(event) {
      event.preventDefault();
      this.scrollContainer.scrollTop += event.deltaY;
    },
    setMapload () {
    setMapload() {
      if (!this.mapPromise) {
        this.mapPromise = window.AMapLoader.load({
          key: '232929db1cb31f26d42b3645a7456154',
          version: '2.0',
          plugins: ['AMap.DistrictSearch', 'AMap.Geocoder'],
          key: "232929db1cb31f26d42b3645a7456154",
          version: "2.0",
          plugins: ["AMap.DistrictSearch", "AMap.Geocoder"],
          AMapUI: {
            version: '1.1',
            plugins: ['geo/DistrictExplorer']
          }
        })
            version: "1.1",
            plugins: ["geo/DistrictExplorer"],
          },
        });
      }
      this.mapPromise
        .then((AMap) => {
          this.mapView = new AMap.Map('container', {
          this.mapView = new AMap.Map("container", {
            zoom: 13,
            center: [104.065617, 30.656952], // 初始化地图中心点
            mapStyle: 'amap://styles/darkblue'
          })
          this.mapView.resize()
            mapStyle: "amap://styles/darkblue",
          });
          this.mapView.resize();
          this._mapResizeHandler = () => {
            if (this.mapView) {
              this.mapView.resize()
              this.mapView.resize();
            }
          }
          window.addEventListener('resize', this._mapResizeHandler)
          };
          window.addEventListener("resize", this._mapResizeHandler);
          // this.infoWindow = new AMap.InfoWindow({
          //   isCustom: true,
          //   offset: new AMap.Pixel(0, -30),
          // });
          this.initCategoryMarkers(AMap)
          this.renderAllBoundaries()
          this.initCategoryMarkers(AMap);
          this.renderAllBoundaries();
        })
        .catch((e) => {
          console.error(e) // 加载错误提示
        })
          console.error(e); // 加载错误提示
        });
    },
    initCategoryMarkers (AMap) {
    initCategoryMarkers(AMap) {
      if (!this.mapView) {
        return
        return;
      }
      const points = this.screenData.projectMapPoints.map((item) => {
        let category
        let category;
        if (this.activeTab === 2) {
          category = 'assets'
          category = "assets";
        } else {
          category = ['house', 'municipal', 'assets'][item.projectType]
          category = ["house", "municipal", "assets"][item.projectType];
        }
        return {
          ...item,
          position: [Number(item.longitude), Number(item.latitude)],
          category: category,
          level: item.category
        }
      })
      const markers = []
          level: item.category,
        };
      });
      const markers = [];
      points.forEach((item) => {
        const marker = this.createCategoryMarker(AMap, item)
        const marker = this.createCategoryMarker(AMap, item);
        if (marker) {
          this.mapView.add(marker)
          markers.push(marker)
          this.mapView.add(marker);
          markers.push(marker);
        }
      })
      this.markers = markers
      this.updateMarkerVisibility()
      });
      this.markers = markers;
      this.updateMarkerVisibility();
    },
    createCategoryMarker (AMap, item) {
    createCategoryMarker(AMap, item) {
      if (!item || !item.position) {
        return null
        return null;
      }
      let image = null
      let level = item.level
      if (item.category === 'municipal') {
        if (item.level === 'A') {
          image = s1
        } else if (item.level === 'B') {
          image = s3
        } else if (item.level === 'C') {
          image = s2
      let image = null;
      let level = item.level;
      if (item.category === "municipal") {
        if (item.level === "A") {
          image = s1;
        } else if (item.level === "B") {
          image = s3;
        } else if (item.level === "C") {
          image = s2;
        } else {
          image = s4
          image = s4;
        }
      } else if (item.category === 'house') {
        if (item.level === 'A') {
          image = f1
        } else if (item.level === 'B') {
          image = f3
        } else if (item.level === 'C') {
          image = f2
      } else if (item.category === "house") {
        if (item.level === "A") {
          image = f1;
        } else if (item.level === "B") {
          image = f3;
        } else if (item.level === "C") {
          image = f2;
        } else {
          image = f4
          image = f4;
        }
      } else if (item.category === 'assets') {
        if (item.level === 'A') {
          image = z1
        } else if (item.level === 'B') {
          image = z3
        } else if (item.level === 'C') {
          image = z2
      } else if (item.category === "assets") {
        if (item.level === "A") {
          image = z1;
        } else if (item.level === "B") {
          image = z3;
        } else if (item.level === "C") {
          image = z2;
        } else {
          image = z4
          image = z4;
        }
      }
      if (!image) {
        return null
        return null;
      }
      const icon = new AMap.Icon({
        image,
        size: new AMap.Size(30, 30),
        imageSize: new AMap.Size(30, 30)
      })
        imageSize: new AMap.Size(30, 30),
      });
      const marker = new AMap.Marker({
        position: item.position,
        icon,
        offset: new AMap.Pixel(-12, -12),
        extData: {
          category: item.category,
          level: level
        }
      })
      marker.on('mouseover', (e) => {
        this.showTooltip(e, item)
      })
      marker.on('mouseout', () => {
        this.hideTooltip()
      })
      marker.on('click', () => {
          level: level,
        },
      });
      marker.on("mouseover", (e) => {
        this.showTooltip(e, item);
      });
      marker.on("mouseout", () => {
        this.hideTooltip();
      });
      marker.on("click", () => {
        this.$router.push({
          path: '/projectHome',
          path: "/projectHome",
          query: {
            projectId: item.projectId,
            projectName: item.projectName
          }
        })
      })
      return marker
            projectName: item.projectName,
          },
        });
      });
      return marker;
    },
    updateMarkerVisibility () {
    updateMarkerVisibility() {
      if (!this.markers || !this.markers.length) {
        return
        return;
      }
      this.markers.forEach((marker) => {
        if (!marker) {
          return
          return;
        }
        const data = marker.getExtData ? marker.getExtData() : null
        const data = marker.getExtData ? marker.getExtData() : null;
        if (!data) {
          return
          return;
        }
        const categoryVisible = this.categoryFilter[data.category]
        const levelVisible = !data.level || this.levelFilter[data.level]
        const categoryVisible = this.categoryFilter[data.category];
        const levelVisible = !data.level || this.levelFilter[data.level];
        if (categoryVisible && levelVisible) {
          marker.show()
          marker.show();
        } else {
          marker.hide()
          marker.hide();
        }
      })
      });
    },
    toggleCategory (type) {
    toggleCategory(type) {
      if (!this.categoryFilter.hasOwnProperty(type)) {
        return
        return;
      }
      this.categoryFilter[type] = !this.categoryFilter[type]
      this.updateMarkerVisibility()
      this.categoryFilter[type] = !this.categoryFilter[type];
      this.updateMarkerVisibility();
    },
    toggleLevel (level) {
    toggleLevel(level) {
      if (!this.levelFilter.hasOwnProperty(level)) {
        return
        return;
      }
      this.levelFilter[level] = !this.levelFilter[level]
      this.updateMarkerVisibility()
      this.levelFilter[level] = !this.levelFilter[level];
      this.updateMarkerVisibility();
    },
    showTooltip (e, item) {
      if (!this.mapView) return
    showTooltip(e, item) {
      if (!this.mapView) return;
      const pixel = this.mapView.lngLatToContainer(item.position)
      const pixel = this.mapView.lngLatToContainer(item.position);
      // 获取当前缩放比例
      let scaleX = 1
      let scaleY = 1
      const app = document.querySelector('#app')
      let scaleX = 1;
      let scaleY = 1;
      const app = document.querySelector("#app");
      if (app) {
        const rect = app.getBoundingClientRect()
        const rect = app.getBoundingClientRect();
        // offsetWidth 是元素的布局宽度(不包含 transform)
        // rect.width 是元素的渲染宽度(包含 transform)
        if (app.offsetWidth > 0) {
          scaleX = rect.width / app.offsetWidth
          scaleX = rect.width / app.offsetWidth;
        }
        if (app.offsetHeight > 0) {
          scaleY = rect.height / app.offsetHeight
          scaleY = rect.height / app.offsetHeight;
        }
      }
      this.tooltipPos = { left: pixel.x / scaleX, top: pixel.y / scaleY - 60 }
      this.tooltipVisible = true
      this.tooltipLoading = true
      this.tooltipError = false
      this.tooltipData = {}
      this.tooltipPos = { left: pixel.x / scaleX, top: pixel.y / scaleY - 60 };
      this.tooltipVisible = true;
      this.tooltipLoading = true;
      this.tooltipError = false;
      this.tooltipData = {};
      getprojectTooltip(item.projectId)
        .then((res) => {
          let address = '-'
          let constructionCompanyInfo = '-'
          let supervisorCompanyInfo = '-'
          let developmentCompanyInfo = '-'
          let address = "-";
          let constructionCompanyInfo = "-";
          let supervisorCompanyInfo = "-";
          let developmentCompanyInfo = "-";
          try {
            if (res.addressInfo) {
              const addressInfo = JSON.parse(res.addressInfo)
              const addressInfo = JSON.parse(res.addressInfo);
              address =
                (addressInfo.province || '') +
                (addressInfo.city || '') +
                (addressInfo.district || '') +
                (addressInfo.detail || '')
                (addressInfo.province || "") +
                (addressInfo.city || "") +
                (addressInfo.district || "") +
                (addressInfo.detail || "");
            }
            if (res.constructionCompanyInfo) {
              constructionCompanyInfo = JSON.parse(res.constructionCompanyInfo)
              constructionCompanyInfo = JSON.parse(res.constructionCompanyInfo);
            }
            if (res.supervisorCompanyInfo) {
              supervisorCompanyInfo = JSON.parse(res.supervisorCompanyInfo)
              supervisorCompanyInfo = JSON.parse(res.supervisorCompanyInfo);
            }
            if (res.developmentCompanyInfo) {
              developmentCompanyInfo = JSON.parse(res.developmentCompanyInfo)
              developmentCompanyInfo = JSON.parse(res.developmentCompanyInfo);
            }
          } catch (error) {
            console.error('Parse error', error)
            console.error("Parse error", error);
          }
          // 对设备租赁单位进行去重处理
          let uniqueProperties = []
          let uniqueProperties = [];
          if (res.properties && res.properties.length > 0) {
            const seenNames = new Set()
            const seenNames = new Set();
            uniqueProperties = res.properties.filter((item) => {
              try {
                const parsed = JSON.parse(item)
                const companyName = parsed['工程起重机械租赁单位']
                const parsed = JSON.parse(item);
                const companyName = parsed["工程起重机械租赁单位"];
                if (companyName && !seenNames.has(companyName)) {
                  seenNames.add(companyName)
                  return true
                  seenNames.add(companyName);
                  return true;
                }
                return false
                return false;
              } catch (e) {
                return false
                return false;
              }
            })
            });
          }
          this.tooltipData = {
@@ -1399,107 +1425,107 @@
            constructionCompanyInfo,
            supervisorCompanyInfo,
            developmentCompanyInfo,
            properties: uniqueProperties
          }
          this.tooltipLoading = false
            properties: uniqueProperties,
          };
          this.tooltipLoading = false;
        })
        .catch(() => {
          this.tooltipLoading = false
          this.tooltipError = true
        })
          this.tooltipLoading = false;
          this.tooltipError = true;
        });
    },
    hideTooltip () {
      this.tooltipVisible = false
    hideTooltip() {
      this.tooltipVisible = false;
    },
    clearBoundary () {
    clearBoundary() {
      if (this.boundaryPolygons.length) {
        this.mapView.remove(this.boundaryPolygons)
        this.boundaryPolygons = []
        this.mapView.remove(this.boundaryPolygons);
        this.boundaryPolygons = [];
      }
    },
    async renderAllBoundaries () {
      this.clearBoundary()
      if (!this.mapView || !this.screenData.projectMapPoints) return
    async renderAllBoundaries() {
      this.clearBoundary();
      if (!this.mapView || !this.screenData.projectMapPoints) return;
      // 确保 AMapUI 已加载
      if (!window.AMapUI) {
        console.warn('AMapUI not loaded')
        return
        console.warn("AMapUI not loaded");
        return;
      }
      const provinceSet = new Set()
      const districtSet = new Set()
      const citySet = new Set() // 显式收集有数据的城市
      const pointsToGeocode = []
      const provinceSet = new Set();
      const districtSet = new Set();
      const citySet = new Set(); // 显式收集有数据的城市
      const pointsToGeocode = [];
      // 1. 收集数据
      this.screenData.projectMapPoints.forEach((point) => {
        let hasData = false
        let hasData = false;
        if (point.addressInfo) {
          try {
            const info = JSON.parse(point.addressInfo)
            const info = JSON.parse(point.addressInfo);
            if (info.province) {
              provinceSet.add(info.province)
              hasData = true
              provinceSet.add(info.province);
              hasData = true;
            }
            if (info.district) {
              districtSet.add(info.district)
              hasData = true
              districtSet.add(info.district);
              hasData = true;
            }
            // 如果只有市没有区,或者需要单独渲染市
            if (info.city) {
              citySet.add(info.city)
              hasData = true
              citySet.add(info.city);
              hasData = true;
            }
          } catch (e) {
            // ignore
          }
        }
        if (!hasData) {
          pointsToGeocode.push([point.longitude, point.latitude])
          pointsToGeocode.push([point.longitude, point.latitude]);
        }
      })
      });
      // 2. 逆地理编码补充缺失数据
      if (pointsToGeocode.length > 0) {
        const geocoder = new window.AMap.Geocoder()
        const batchSize = 20
        const batches = []
        const geocoder = new window.AMap.Geocoder();
        const batchSize = 20;
        const batches = [];
        for (let i = 0; i < pointsToGeocode.length; i += batchSize) {
          batches.push(pointsToGeocode.slice(i, i + batchSize))
          batches.push(pointsToGeocode.slice(i, i + batchSize));
        }
        await Promise.all(
          batches.map((batch) => {
            return new Promise((resolve) => {
              geocoder.getAddress(batch, (status, result) => {
                if (status === 'complete' && result.regeocodes) {
                if (status === "complete" && result.regeocodes) {
                  result.regeocodes.forEach((geo) => {
                    if (geo.addressComponent) {
                      const { district, city, province } = geo.addressComponent
                      if (province) provinceSet.add(province)
                      if (city) citySet.add(city)
                      if (district) districtSet.add(district)
                      const { district, city, province } = geo.addressComponent;
                      if (province) provinceSet.add(province);
                      if (city) citySet.add(city);
                      if (district) districtSet.add(district);
                    }
                  })
                  });
                }
                resolve()
              })
            })
                resolve();
              });
            });
          })
        )
        );
      }
      // 初始化 DistrictExplorer
      let districtExplorer = null
      let districtExplorer = null;
      try {
        districtExplorer = new window.AMapUI.DistrictExplorer({
          map: this.mapView,
          eventSupport: false // 我们自己绘制,不需要它的事件
        })
          eventSupport: false, // 我们自己绘制,不需要它的事件
        });
      } catch (e) {
        console.error('Init DistrictExplorer failed', e)
        return
        console.error("Init DistrictExplorer failed", e);
        return;
      }
      // 辅助函数:根据名称查找 adcode
@@ -1507,18 +1533,18 @@
        return new Promise((resolve) => {
          const search = new window.AMap.DistrictSearch({
            subdistrict: 1, // 获取下一级以便后续逻辑使用(如省查市)
            extensions: 'base', // 这里只需要 adcode,不需要详细边界
            level: level
          })
            extensions: "base", // 这里只需要 adcode,不需要详细边界
            level: level,
          });
          search.search(name, (status, result) => {
            if (status === 'complete' && result.districtList.length > 0) {
              resolve(result.districtList[0])
            if (status === "complete" && result.districtList.length > 0) {
              resolve(result.districtList[0]);
            } else {
              resolve(null)
              resolve(null);
            }
          })
        })
      }
          });
        });
      };
      // 辅助函数:使用 DistrictExplorer 加载并绘制区域
      const renderAreaNode = (
@@ -1531,23 +1557,23 @@
        return new Promise((resolve) => {
          districtExplorer.loadAreaNode(adcode, (error, areaNode) => {
            if (error || !areaNode) {
              resolve()
              return
              resolve();
              return;
            }
            // 获取父级区域的 GeoJSON (ParentFeature)
            // 这包含了正确的孔洞信息
            const feature = areaNode.getParentFeature()
            const feature = areaNode.getParentFeature();
            if (!feature || !feature.geometry) {
              resolve()
              return
              resolve();
              return;
            }
            const geometry = feature.geometry
            const geometry = feature.geometry;
            const types =
              geometry.type === 'MultiPolygon'
              geometry.type === "MultiPolygon"
                ? geometry.coordinates
                : [geometry.coordinates]
                : [geometry.coordinates];
            // 遍历所有多边形(处理 MultiPolygon)
            types.forEach((coords) => {
@@ -1560,120 +1586,120 @@
                path: coords, // 直接使用 GeoJSON 的坐标数组
                strokeColor: style.strokeColor,
                strokeWeight: 2,
                fillColor: style.fillColor || 'transparent',
                fillColor: style.fillColor || "transparent",
                fillOpacity:
                  style.fillOpacity !== undefined ? style.fillOpacity : 0.2,
                zIndex: style.zIndex,
                bubble: true,
                cursor:
                  level === 'district' && (isActive || isHoverable)
                    ? 'pointer'
                    : 'default',
                extData: { level, isActive }
              })
                  level === "district" && (isActive || isHoverable)
                    ? "pointer"
                    : "default",
                extData: { level, isActive },
              });
              // 添加 hover 事件
              if (level === 'district' && (isActive || isHoverable)) {
                const baseFillColor = style.fillColor || 'transparent'
              if (level === "district" && (isActive || isHoverable)) {
                const baseFillColor = style.fillColor || "transparent";
                const baseFillOpacity =
                  style.fillOpacity !== undefined ? style.fillOpacity : 0.2
                const baseStrokeColor = style.strokeColor
                  style.fillOpacity !== undefined ? style.fillOpacity : 0.2;
                const baseStrokeColor = style.strokeColor;
                const hoverStyle = isActive
                  ? {
                    fillColor: '#00b3ff',
                    fillOpacity: 0.35,
                    strokeColor: '#00b3ff',
                    strokeWeight: 3
                  }
                      fillColor: "#00b3ff",
                      fillOpacity: 0.35,
                      strokeColor: "#00b3ff",
                      strokeWeight: 3,
                    }
                  : {
                    fillColor: '#00b3ff',
                    fillOpacity: 0.2
                  }
                      fillColor: "#00b3ff",
                      fillOpacity: 0.2,
                    };
                polygon.on('mouseover', () => {
                  polygon.setOptions(hoverStyle)
                })
                polygon.on('mouseout', () => {
                polygon.on("mouseover", () => {
                  polygon.setOptions(hoverStyle);
                });
                polygon.on("mouseout", () => {
                  polygon.setOptions({
                    fillColor: baseFillColor,
                    fillOpacity: baseFillOpacity,
                    strokeColor: baseStrokeColor,
                    strokeWeight: 2
                  })
                })
                    strokeWeight: 2,
                  });
                });
              }
              this.mapView.add(polygon)
              this.boundaryPolygons.push(polygon)
            })
              this.mapView.add(polygon);
              this.boundaryPolygons.push(polygon);
            });
            resolve()
          })
        })
      }
            resolve();
          });
        });
      };
      // 3. 处理省份:渲染省界,并收集其下属的所有城市 adcode
      const cityCodesFromProvinces = new Set()
      const cityAdcodeToNameMap = new Map() // 辅助Map,用于判断是否在citySet中
      const cityCodesFromProvinces = new Set();
      const cityAdcodeToNameMap = new Map(); // 辅助Map,用于判断是否在citySet中
      const provincePromises = Array.from(provinceSet).map(
        async (provinceName) => {
          const result = await resolveAdcode(provinceName, 'province')
          const result = await resolveAdcode(provinceName, "province");
          if (result) {
            // 渲染省 - 无填充
            renderAreaNode(
              result.adcode,
              {
                strokeColor: '#5470C6',
                fillColor: '',
                strokeColor: "#5470C6",
                fillColor: "",
                fillOpacity: 0,
                zIndex: 8
                zIndex: 8,
              },
              'province',
              "province",
              false
            )
            );
            // 收集该省下的所有市(用于渲染市界)
            if (result.districtList && result.districtList.length) {
              result.districtList.forEach((city) => {
                cityCodesFromProvinces.add(city.adcode)
                cityAdcodeToNameMap.set(city.adcode, city.name)
              })
                cityCodesFromProvinces.add(city.adcode);
                cityAdcodeToNameMap.set(city.adcode, city.name);
              });
            }
          }
        }
      )
      );
      await Promise.all(provincePromises)
      await Promise.all(provincePromises);
      // 4. 渲染市
      // 合并 "省份下属的市" 和 "直接出现在数据中的市"
      const allCityAdcodes = new Set([...cityCodesFromProvinces])
      const allCityAdcodes = new Set([...cityCodesFromProvinces]);
      // 处理直接出现的市名称
      const cityResolutionPromises = Array.from(citySet).map(
        async (cityName) => {
          const result = await resolveAdcode(cityName, 'city')
          const result = await resolveAdcode(cityName, "city");
          if (result) {
            allCityAdcodes.add(result.adcode)
            cityAdcodeToNameMap.set(result.adcode, cityName)
            allCityAdcodes.add(result.adcode);
            cityAdcodeToNameMap.set(result.adcode, cityName);
          }
        }
      )
      await Promise.all(cityResolutionPromises)
      );
      await Promise.all(cityResolutionPromises);
      const cityRenderPromises = Array.from(allCityAdcodes).map((adcode) => {
        // 渲染所有市 - 无填充 (仅轮廓)
        // 注意:isActive设为false,因为我们只希望聚焦在具体的区或标点上
        return renderAreaNode(
          adcode,
          { strokeColor: '#FAC858', fillColor: '', fillOpacity: 0, zIndex: 9 },
          'city',
          { strokeColor: "#FAC858", fillColor: "", fillOpacity: 0, zIndex: 9 },
          "city",
          false
        )
      })
        );
      });
      await Promise.all(cityRenderPromises)
      await Promise.all(cityRenderPromises);
      // 5. 渲染区
      // 策略:
@@ -1682,23 +1708,23 @@
      //    - 如果区不在 districtSet 中 (无数据):透明 + Hover效果 (isActive=false)
      // 2. 遍历 districtSet,处理可能漏掉的区 (比如没有对应的city信息的孤立区数据)
      const renderedDistrictAdcodes = new Set()
      const renderedDistrictAdcodes = new Set();
      const cityDistrictPromises = Array.from(citySet).map(async (cityName) => {
        const result = await resolveAdcode(cityName, 'city')
        const result = await resolveAdcode(cityName, "city");
        if (result && result.districtList) {
          const subDistricts = result.districtList
          const subDistricts = result.districtList;
          const subPromises = subDistricts.map((sub) => {
            renderedDistrictAdcodes.add(sub.adcode)
            renderedDistrictAdcodes.add(sub.adcode);
            // 检查该区是否有数据
            // 模糊匹配:districtSet中的名字可能不带"区"字
            let hasData = false
            let hasData = false;
            if (sub.name) {
              for (let d of districtSet) {
                if (sub.name.includes(d) || d.includes(sub.name)) {
                  hasData = true
                  break
                  hasData = true;
                  break;
                }
              }
            }
@@ -1707,75 +1733,75 @@
              // 有数据:默认高亮,无Hover特效
              return renderAreaNode(
                sub.adcode,
                { strokeColor: '#00ffd2', fillColor: '#00ffd2', zIndex: 10 },
                'district',
                { strokeColor: "#00ffd2", fillColor: "#00ffd2", zIndex: 10 },
                "district",
                true,
                false
              )
              );
            } else {
              // 无数据:默认透明,有Hover特效
              return renderAreaNode(
                sub.adcode,
                {
                  strokeColor: '#00ffd2',
                  fillColor: '',
                  strokeColor: "#00ffd2",
                  fillColor: "",
                  fillOpacity: 0,
                  zIndex: 10
                  zIndex: 10,
                },
                'district',
                "district",
                false,
                true
              )
              );
            }
          })
          return Promise.all(subPromises)
          });
          return Promise.all(subPromises);
        }
      })
      });
      await Promise.all(cityDistrictPromises)
      await Promise.all(cityDistrictPromises);
      // 处理可能遗漏的区 (districtSet 中有,但没在上面的循环中渲染过)
      const orphanDistrictPromises = Array.from(districtSet).map(
        async (districtName) => {
          // 先检查是否已经渲染过 (需要先resolve adcode才能精确匹配,但这里先用名字简单过滤一下,减少请求)
          // 由于上面是根据adcode记录的,这里还是得resolve一下比较稳妥
          const result = await resolveAdcode(districtName, 'district')
          const result = await resolveAdcode(districtName, "district");
          if (result && !renderedDistrictAdcodes.has(result.adcode)) {
            // 既然在districtSet里,肯定是有数据的
            return renderAreaNode(
              result.adcode,
              { strokeColor: '#00ffd2', fillColor: '#00ffd2', zIndex: 10 },
              'district',
              { strokeColor: "#00ffd2", fillColor: "#00ffd2", zIndex: 10 },
              "district",
              true,
              false
            )
            );
          }
        }
      )
      );
      await Promise.all(orphanDistrictPromises)
      await Promise.all(orphanDistrictPromises);
      // 6. 自动调整视野
      const targetOverlays = []
      const targetOverlays = [];
      if (this.markers && this.markers.length > 0) {
        targetOverlays.push(...this.markers)
        targetOverlays.push(...this.markers);
      }
      if (this.boundaryPolygons.length > 0) {
        // 只保留 isActive 为 true 的区域用于缩放 (即有数据的区)
        const activePolygons = this.boundaryPolygons.filter((p) => {
          const data = p.getExtData()
          return data && data.isActive
        })
        targetOverlays.push(...activePolygons)
          const data = p.getExtData();
          return data && data.isActive;
        });
        targetOverlays.push(...activePolygons);
      }
      if (targetOverlays.length > 0) {
        this.mapView.setFitView(targetOverlays, false, [60, 60, 60, 60])
        this.mapView.setFitView(targetOverlays, false, [60, 60, 60, 60]);
      }
    }
  }
}
    },
  },
};
</script>
<style lang="less" scoped>
screen/src/view/projectHome/index.vue
@@ -872,6 +872,7 @@
          sessionStorage.setItem('commentType', res.commentType) // 1模糊评价 2打分评价
          sessionStorage.setItem('clientName', res.name)
          sessionStorage.setItem('clientLogo', res.logo)
          sessionStorage.setItem('clientId', res.clientId)
          this.clientName = res.name
          this.clientLogo = res.logo
        })