董国庆
2026-03-04 088caf67329169fea02cc6641a65043fe511b4f9
文档问题修改
1个文件已添加
7个文件已修改
659 ■■■■■ 已修改文件
estate-frontend-v2024/src/views/business/forms/__tests__/edit-projectPermission-form.test.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
estate-frontend-v2024/src/views/business/forms/edit-projectPermission-form.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/.env.development 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/home/index.vue 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/planOfAudit/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/planOfAudit/service.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/projectHome/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/projectList/index.vue 568 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
estate-frontend-v2024/src/views/business/forms/__tests__/edit-projectPermission-form.test.jsx
New file
@@ -0,0 +1,16 @@
import React from "react";
import { render } from "@testing-library/react";
import EditProjectPermissionForm from "../edit-projectPermission-form";
test("renders permission modal without crashing when currentRowData is null", () => {
  const { getByText } = render(
    <EditProjectPermissionForm
      currentRowData={null}
      visible={true}
      confirmLoading={false}
      onCancel={() => {}}
      onOk={() => {}}
    />
  );
  expect(getByText("参数设置")).toBeTruthy();
});
estate-frontend-v2024/src/views/business/forms/edit-projectPermission-form.jsx
@@ -25,16 +25,21 @@
  constructor(props) {
    super(props);
    const hasPermission = props.currentRowData && props.currentRowData.permission && props.currentRowData.permission.length > 0;
    this.setState({
    this.state = {
      ...this.state,
      dataSource: this.initDataSource(props.currentRowData),
      permissionEnabled: !!hasPermission,
    });
    };
  }
  initDataSource(data) {
    const perms = data && data.permission ? data.permission.split(',').map(Number) : [];
    // 如果没有 permission 字段或者为空字符串,默认可能需要处理(全选?全不选?)。目前全不选。
    // 注意:如果是第一次设置,可能 perms 为空。
    if (!perms.length) {
      return initialDataSource.map(item => ({
        ...item,
        visible: true
      }));
    }
    return initialDataSource.map(item => ({
      ...item,
      visible: perms.includes(item.id)
@@ -115,8 +120,9 @@
          <Column
            title={() => {
              const allChecked = dataSource.length > 0 && dataSource.every(item => item.visible);
              const hasChecked = dataSource.some(item => item.visible);
              const list = Array.isArray(dataSource) ? dataSource : [];
              const allChecked = list.length > 0 && list.every(item => item.visible);
              const hasChecked = list.some(item => item.visible);
              const indeterminate = hasChecked && !allChecked;
              return (
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
screen/.env.development
@@ -1,8 +1,8 @@
# 开发环境配置
# 主API地址
# VUE_APP_BASE_URL=http://221.182.45.100:8180
VUE_APP_BASE_URL=http://192.168.110.64:9908
VUE_APP_BASE_URL=http://221.182.45.100:8180
# VUE_APP_BASE_URL=http://192.168.110.64:9908
# 图片回显地址
VUE_APP_IMAGE_BASE_URL=http://221.182.45.100:8180
screen/src/view/home/index.vue
@@ -425,7 +425,7 @@
                      <div class="fs--45 lh--45 ff54- color8">
                        {{ screenData.inspectedMachineryTotal }}
                      </div>
                      <div class="mt--20 h--50 oa">
                      <div class="mt--20  oa">
                        <div
                          v-for="(
                            item, index
@@ -442,7 +442,7 @@
                      <div class="fs--45 lh--45 ff54- color6">
                        {{ screenData.uninspectedMachineryTotal }}
                      </div>
                      <div class="mt--20 h--50 oa">
                      <div class="mt--20 oa">
                        <div
                          v-for="(
                            item, index
screen/src/view/planOfAudit/index.vue
@@ -706,41 +706,52 @@
          return
        }
        // 使用当前时间戳作为 uuid
        const uuid = Date.now().toString()
        // 第一步:调用接口生成Word文档(返回Word文档流)
        let wordBlob
        try {
          if (type === 'safe') {
            // 安全报告(模糊评价)
            if (!safetyId) {
              this.$message.error('缺少安全报告模板ID')
              this.loading = false
              this.reportVisible = false
              return
            }
            const safeCommentType = parseInt(localStorage.getItem('commentType') || '1')
            if (safeCommentType === 1) {
            wordBlob = await getFuzzyReportWord({
              checkTaskId: checkTaskId,
              templateId: safetyId,
              uuid: safetyName
            })
          } else {
            // 设备报告(打分评价)
              wordBlob = await getScoreReportWord({
                checkTaskId: checkTaskId,
                templateId: safetyId,
                uuid: safetyName
              })
            }
          } else {
            if (!deviceTemplateId) {
              this.$message.error('缺少设备报告模板ID')
              this.loading = false
              this.reportVisible = false
              return
            }
            wordBlob = await getScoreReportWord({
             const safeCommentType = parseInt(localStorage.getItem('commentType') || '1')
            if (safeCommentType === 1) {
              wordBlob = await getFuzzyReportWord({
              checkTaskId: checkTaskId,
              templateId: deviceTemplateId,
                templateId: deviceTemplateName,
              uuid: deviceTemplateName
            })
            } else {
            wordBlob = await getScoreReportWord({
              checkTaskId: checkTaskId,
              templateId: deviceTemplateName,
              uuid: deviceTemplateName
            })}
          }
        } catch (error) {
          // 捕获生成Word文档时的错误
          console.error('生成Word文档失败:', error)
          this.$message.error(error.message || '生成报告失败')
          this.loading = false
@@ -749,24 +760,21 @@
        }
        // 检查 Word 文档是否生成成功
        if (!wordBlob || wordBlob.size === 0) {
          this.$message.error('报告生成失败')
          this.loading = false
          this.reportVisible = false
          return
        }
        // 第二步:使用uuid获取PDF链接
        let pdfUrlResponse
        try {
          if (type === 'safe') {
            pdfUrlResponse = await getFuzzyReportPdfUrl({ uuid: safetyName })
          } else {
            pdfUrlResponse = await getScoreReportPdfUrl({ uuid: deviceTemplateName })
            pdfUrlResponse = await getFuzzyReportPdfUrl({ uuid: deviceTemplateName })
          }
        } catch (error) {
          // 捕获获取PDF链接时的错误
          console.error('获取PDF链接失败:', error)
          this.$message.error(error.message || '获取PDF链接失败')
          this.loading = false
@@ -774,9 +782,13 @@
          return
        }
        // 根据后端返回的数据结构获取PDF链接
        // 假设后端返回格式为 { data: "http://xxx.pdf" } 或直接返回字符串
        const pdfUrl = pdfUrlResponse.data || pdfUrlResponse
        let pdfUrl = ''
        console.log('pdfUrlResponse',pdfUrlResponse)
        if (typeof pdfUrlResponse === 'string') {
          pdfUrl = pdfUrlResponse
        } else if (pdfUrlResponse && pdfUrlResponse.data) {
          pdfUrl = pdfUrlResponse.data
        }
        if (!pdfUrl) {
          this.$message.error('获取PDF链接失败')
screen/src/view/planOfAudit/service.js
@@ -114,7 +114,7 @@
// 获取设备报告PDF链接(打分评价)- 通过uuid获取PDF链接
export const getScoreReportPdfUrl = async (params) => {
  return reportRequest({
    url: '/admin/stat/report/fuzzyReportPdfNew',
    url: '/admin/stat/report/scoreReportPdfNew',
    method: 'get',
    params
  })
screen/src/view/projectHome/index.vue
@@ -235,7 +235,7 @@
          </template>
          <template #content>
            <div class="people-info">
              <div class="people-grid">
              <div class="people-card">
                <div
                  class="people-item"
                  v-for="(item, index) in peopleList"
@@ -969,6 +969,9 @@
          // 初始化时不立即添加 marker,等待项目信息加载完成后再添加
          // this.initCategoryMarkers(AMap);
          if (this.projectInfo && this.projectInfo.type !== undefined) {
            this.updateMapMarker()
          }
        })
        .catch((e) => {
          console.error(e) // 加载错误提示
screen/src/view/projectList/index.vue
@@ -119,7 +119,7 @@
          <div
            @click.stop="handleClick(1)"
            class="absolute h100 flex a-center c-fff fs--14 SHS pointer-events-none"
            style="left: 20px; right: 60px; z-index: 1;"
            style="left: 20px; right: 60px; z-index: 1"
            v-if="startTimeTxt"
          >
            {{ startTimeTxt }}
@@ -128,11 +128,14 @@
            @click.stop="handleClick(1)"
            v-else
            class="absolute h100 flex a-center c-fff fs--14 SHSR pointer-events-none"
            style="left: 20px; right: 60px; z-index: 1;"
            style="left: 20px; right: 60px; z-index: 1"
          >
            请选择开始日期
          </div>
          <div class="flex a-center gap--5" style="position: relative; z-index: 2;">
          <div
            class="flex a-center gap--5"
            style="position: relative; z-index: 2"
          >
            <i
              @click.stop="clearStartTime"
              class="el-icon-circle-close c-fff cursor-pointer"
@@ -160,7 +163,7 @@
          <div
            @click="handleClick(2)"
            class="absolute h100 flex a-center c-fff fs--14 SHS pointer-events-none"
            style="left: 20px; right: 60px; z-index: 1;"
            style="left: 20px; right: 60px; z-index: 1"
            v-if="endTimeTxt"
          >
            {{ endTimeTxt }}
@@ -169,11 +172,14 @@
            @click="handleClick(2)"
            v-else
            class="absolute h100 flex a-center c-fff fs--14 SHSR pointer-events-none"
            style="left: 20px; right: 60px; z-index: 1;"
            style="left: 20px; right: 60px; z-index: 1"
          >
            请选择结束日期
          </div>
          <div class="flex a-center gap--5" style="position: relative; z-index: 2;">
          <div
            class="flex a-center gap--5"
            style="position: relative; z-index: 2"
          >
            <i
              @click.stop="clearEndTime"
              class="el-icon-circle-close c-fff cursor-pointer"
@@ -228,7 +234,11 @@
            {{ formatAddress(row.addressInfo) }}
          </template>
        </el-table-column>
        <el-table-column label="本月检查情况" prop="checkResult" min-width="120">
        <el-table-column
          label="本月检查情况"
          prop="checkResult"
          min-width="120"
        >
          <template #default="{ row }">
            <div class="flex a-center j-center">
              <div class="ripple" v-if="row.checkResult">
@@ -252,7 +262,7 @@
                ></div>
              </div>
              <div :class="row.checkResult ? 'ml--6' : ''" class="SHSR">
                {{ row.checkResult || '-' }}
                {{ row.checkResult || "-" }}
              </div>
            </div>
          </template>
@@ -264,7 +274,7 @@
        </el-table-column>
        <el-table-column label="本月检查风险" prop="risk" min-width="120">
          <template #default="{ row }">
            <div class="SHSR">{{ row.risk || '-' }}</div>
            <div class="SHSR">{{ row.risk || "-" }}</div>
          </template>
        </el-table-column>
@@ -302,11 +312,7 @@
    </div>
    <!-- 项目列表弹窗 -->
    <CustomDialog title="项目" :visible.sync="projectVisible" width="1420px">
      <CustomTable
        :data="checkList"
        height="630"
        v-loading="checkLoading"
      >
      <CustomTable :data="checkList" height="630" v-loading="checkLoading">
        <el-table-column label="检查计划" prop="planName" min-width="150" />
        <el-table-column label="计划类型" min-width="120">
          <template #default="{ row }">
@@ -320,10 +326,14 @@
        </el-table-column>
        <el-table-column label="分数" prop="score" min-width="100">
          <template #default="{ row }">
            {{ row.score || '-' }}
            {{ commentType === "1" ? row.fuzzyScore : row.score }}
          </template>
        </el-table-column>
        <el-table-column v-if="shouldShowRiskLevel" label="风险类型" min-width="120">
        <el-table-column
          v-if="shouldShowRiskLevel"
          label="风险类型"
          min-width="120"
        >
          <template #default="{ row }">
            {{ formatRiskLevel(row.riskLevel) }}
          </template>
@@ -353,22 +363,26 @@
                  class="py--12 w100 txt-center br--10 bgcolor6 report-btn"
                  :class="{
                    'report-disabled': row.containSystemRes !== 1,
                    'report-enabled': row.containSystemRes === 1
                    'report-enabled': row.containSystemRes === 1,
                  }"
                >
                  <span>安全报告</span>
                  <span v-if="row.containSystemRes !== 1" class="no-report-tag">(暂无)</span>
                  <span v-if="row.containSystemRes !== 1" class="no-report-tag"
                    >(暂无)</span
                  >
                </div>
                <div
                  @click="openReport('device', row)"
                  class="py--12 w100 txt-center br--10 bgcolor7 report-btn"
                  :class="{
                    'report-disabled': row.containDeviceRes !== 1,
                    'report-enabled': row.containDeviceRes === 1
                    'report-enabled': row.containDeviceRes === 1,
                  }"
                >
                  <span>设备报告</span>
                  <span v-if="row.containDeviceRes !== 1" class="no-report-tag">(暂无)</span>
                  <span v-if="row.containDeviceRes !== 1" class="no-report-tag"
                    >(暂无)</span
                  >
                </div>
              </div>
            </el-popover>
@@ -384,31 +398,31 @@
    >
      <el-descriptions :column="3">
        <el-descriptions-item label="施工单位">
          {{ companyInfo.construction?.name || '-' }}
          {{ companyInfo.construction?.name || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="项目经理">
          {{ companyInfo.construction?.leader || '-' }}
          {{ companyInfo.construction?.leader || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="联系方式">
          {{ companyInfo.construction?.phone || '-' }}
          {{ companyInfo.construction?.phone || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="监理单位">
          {{ companyInfo.supervisor?.name || '-' }}
          {{ companyInfo.supervisor?.name || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="总监">
          {{ companyInfo.supervisor?.leader || '-' }}
          {{ companyInfo.supervisor?.leader || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="联系方式">
          {{ companyInfo.supervisor?.phone || '-' }}
          {{ companyInfo.supervisor?.phone || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="建设单位">
          {{ companyInfo.development?.name || '-' }}
          {{ companyInfo.development?.name || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="项目负责人">
          {{ companyInfo.development?.leader || '-' }}
          {{ companyInfo.development?.leader || "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="联系方式">
          {{ companyInfo.development?.phone || '-' }}
          {{ companyInfo.development?.phone || "-" }}
        </el-descriptions-item>
      </el-descriptions>
    </CustomDialog>
@@ -419,15 +433,19 @@
      width="1420px"
      @close="closeReportDialog"
    >
      <div style="height: 691px;" class="flex flex-column" v-loading="reportLoading">
      <div
        style="height: 691px"
        class="flex flex-column"
        v-loading="reportLoading"
      >
        <!-- PDF预览容器 -->
        <div
          v-if="showPreview && pdfUrl"
          style="width: 100%; height: 100%; overflow: auto; background: #525659;"
          style="width: 100%; height: 100%; overflow: auto; background: #525659"
        >
          <iframe
            :src="pdfUrl"
            style="width: 100%; height: 100%; border: none;"
            style="width: 100%; height: 100%; border: none"
            frameborder="0"
          ></iframe>
        </div>
@@ -437,7 +455,12 @@
        </div>
        <!-- 底部操作按钮 -->
        <div class="flex j-center gap--20 mt--20">
          <el-button type="primary" @click="downloadReport" :disabled="!reportBlobUrl">下载报告</el-button>
          <el-button
            type="primary"
            @click="downloadReport"
            :disabled="!reportBlobUrl"
            >下载报告</el-button
          >
          <el-button @click="closeReportDialog">关闭</el-button>
        </div>
      </div>
@@ -446,8 +469,8 @@
</template>
<script>
import HeaderBox from '../header/index.vue'
import moment from 'moment'
import HeaderBox from "../header/index.vue";
import moment from "moment";
import {
  getProjectList,
  projectList,
@@ -455,8 +478,8 @@
  getFuzzyReportWord,
  getScoreReportWord,
  getFuzzyReportPdfUrl,
  getScoreReportPdfUrl
} from './service'
  getScoreReportPdfUrl,
} from "./service";
export default {
  components: { HeaderBox },
  props: {},
@@ -468,23 +491,23 @@
      riskLevel: null, // 项目风险
      startTime: null, // 开始时间
      endTime: null, // 结束时间
      startTimeTxt: '', // 开始时间显示文本
      endTimeTxt: '', // 结束时间显示文本
      projectName: '', // 项目名称搜索
      startTimeTxt: "", // 开始时间显示文本
      endTimeTxt: "", // 结束时间显示文本
      projectName: "", // 项目名称搜索
      // 下拉选项
      projectOptions: [], // 项目下拉列表
      projectTypeOptions: [
        { label: '房建', value: 0 },
        { label: '市政', value: 1 },
        { label: '固定资产', value: 2 }
        { label: "房建", value: 0 },
        { label: "市政", value: 1 },
        { label: "固定资产", value: 2 },
      ], // 项目类型下拉列表
      riskLevelOptions: [
        { label: '拒绝接受风险', value: 0 },
        { label: '不可接受风险', value: 1 },
        { label: '可接受风险', value: 2 },
        { label: '可容忍风险', value: 3 },
        { label: '可忽略风险', value: 4 }
        { label: "拒绝接受风险", value: 0 },
        { label: "不可接受风险", value: 1 },
        { label: "可接受风险", value: 2 },
        { label: "可容忍风险", value: 3 },
        { label: "可忽略风险", value: 4 },
      ],
      // 表格数据
@@ -509,244 +532,252 @@
      // 用户信息
      userInfo: {},
      clientId: localStorage.getItem('clientId'),
      clientId: localStorage.getItem("clientId"),
      isClientMode: false, // 是否是委托方模式
      // 报告相关
      reportUrl: '', // 已废弃,保留兼容性
      reportBlobUrl: '', // Blob URL用于下载
      pdfUrl: '', // PDF预览URL
      reportUrl: "", // 已废弃,保留兼容性
      reportBlobUrl: "", // Blob URL用于下载
      pdfUrl: "", // PDF预览URL
      reportLoading: false,
      showPreview: false, // 是否显示预览
      currentRow: null, // 当前选中的行数据
      // 评价类型
      commentType: localStorage.getItem('commentType') || null,
      projectCategory: null
    }
      commentType: localStorage.getItem("commentType") || null,
      projectCategory: null,
    };
  },
  computed: {
    // 是否显示风险类型列(commentType 不为 2 时显示)
    shouldShowRiskLevel () {
      return this.commentType != '2'
    }
      return this.commentType != "2";
    },
  },
  watch: {
    startTime: {
      handler (newVal) {
        if (newVal) {
          this.startTimeTxt = moment(newVal).format('YYYY年MM月DD日 HH:mm:ss')
          this.startTimeTxt = moment(newVal).format("YYYY年MM月DD日 HH:mm:ss");
        } else {
          this.startTimeTxt = ''
          this.startTimeTxt = "";
        }
      },
      immediate: true
      immediate: true,
    },
    endTime: {
      handler (newVal) {
        if (newVal) {
          this.endTimeTxt = moment(newVal).format('YYYY年MM月DD日 HH:mm:ss')
          this.endTimeTxt = moment(newVal).format("YYYY年MM月DD日 HH:mm:ss");
        } else {
          this.endTimeTxt = ''
          this.endTimeTxt = "";
        }
      },
      immediate: true
    }
      immediate: true,
    },
  },
  created () {
    if (this.$route.query.projectCategory) {
      this.projectCategory = this.$route.query.projectCategory
      this.projectCategory = this.$route.query.projectCategory;
    }
    // 接收从 home 页面传递过来的时间参数并回填
    if (this.$route.query.startTime) {
      this.startTime = new Date(this.$route.query.startTime)
      this.startTime = new Date(this.$route.query.startTime);
    }
    if (this.$route.query.endTime) {
      this.endTime = new Date(this.$route.query.endTime)
      this.endTime = new Date(this.$route.query.endTime);
    }
    this.initUserInfo()
    this.initData()
    this.initUserInfo();
    this.initData();
  },
  mounted () {},
  methods: {
    // 初始化用户信息
    initUserInfo () {
      const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
      this.userInfo = userInfo
      const userInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
      this.userInfo = userInfo;
      // 判断是否是委托方(type为1表示委托方)
      this.isClientMode = userInfo.type === 1
      this.isClientMode = userInfo.type === 1;
    },
    // 初始化数据
    async initData () {
      await this.loadProjectOptions()
      await this.loadTableData()
      await this.loadProjectOptions();
      await this.loadTableData();
    },
    // 加载项目下拉列表(仅委托方需要)
    async loadProjectOptions () {
      if (!this.isClientMode) return
      if (!this.isClientMode) return;
      try {
        const res = await getProjectList({
          clientId: this.clientId,
          projectCategory: ['ongoing', 'fixed'][localStorage.getItem('activeTab') - 1]
        })
          projectCategory: ["ongoing", "fixed"][
            localStorage.getItem("activeTab") - 1
          ],
        });
        if (res) {
          this.projectOptions = res.map((item) => ({
            label: item.name,
            value: item.id
          }))
            value: item.id,
          }));
        }
      } catch (error) {
        console.error('加载项目列表失败:', error)
        console.error("加载项目列表失败:", error);
      }
    },
    // 加载表格数据
    async loadTableData () {
      this.loading = true
      this.loading = true;
      try {
        const params = {
          pageNum: this.currentPage,
          pageSize: this.pageSize,
          clientId: this.clientId
        }
          clientId: this.clientId,
        };
        // 添加搜索条件
        if (this.searchProjectId) params.projectId = this.searchProjectId
        if (this.projectType) params.type = this.projectType
        if (this.riskLevel !== null && this.riskLevel !== '') { params.riskLevel = this.riskLevel }
        if (this.searchProjectId) params.projectId = this.searchProjectId;
        if (this.projectType) params.type = this.projectType;
        if (this.riskLevel !== null && this.riskLevel !== "") {
          params.riskLevel = this.riskLevel;
        }
        if (this.startTime) {
          params.startTime = moment(this.startTime).format(
            'YYYY-MM-DD HH:mm:ss'
          )
            "YYYY-MM-DD HH:mm:ss"
          );
        }
        if (this.endTime) { params.endTime = moment(this.endTime).format('YYYY-MM-DD HH:mm:ss') }
        if (this.projectName) params.name = this.projectName
        if (this.endTime) {
          params.endTime = moment(this.endTime).format("YYYY-MM-DD HH:mm:ss");
        }
        if (this.projectName) params.name = this.projectName;
        if (this.projectCategory) {
          params.category = localStorage.getItem('activeTab')
          params.category = localStorage.getItem("activeTab");
        }
        const res = await projectList(params)
        const res = await projectList(params);
        if (res) {
          this.tableData = res.records || []
          this.total = res.total || 0
          this.tableData = res.records || [];
          this.total = res.total || 0;
        }
      } catch (error) {
        console.error('加载项目列表失败:', error)
        this.$message.error('加载项目列表失败')
        console.error("加载项目列表失败:", error);
        this.$message.error("加载项目列表失败");
      } finally {
        this.loading = false
        this.loading = false;
      }
    },
    // 搜索
    handleSearch () {
      this.currentPage = 1
      this.loadTableData()
      this.currentPage = 1;
      this.loadTableData();
    },
    // 清除项目选择
    clearSearchProjectId () {
      this.searchProjectId = null
      this.handleSearch()
      this.searchProjectId = null;
      this.handleSearch();
    },
    // 清除项目类型
    clearProjectType () {
      this.projectType = null
      this.handleSearch()
      this.projectType = null;
      this.handleSearch();
    },
    // 清除风险等级
    clearRiskLevel () {
      this.riskLevel = null
      this.handleSearch()
      this.riskLevel = null;
      this.handleSearch();
    },
    // 清除开始时间
    clearStartTime () {
      this.startTime = null
      this.handleSearch()
      this.startTime = null;
      this.handleSearch();
    },
    // 清除结束时间
    clearEndTime () {
      this.endTime = null
      this.handleSearch()
      this.endTime = null;
      this.handleSearch();
    },
    // 分页大小改变
    handleSizeChange (val) {
      this.pageSize = val
      this.currentPage = 1
      this.loadTableData()
      this.pageSize = val;
      this.currentPage = 1;
      this.loadTableData();
    },
    // 当前页改变
    handleCurrentChange (val) {
      this.currentPage = val
      this.loadTableData()
      this.currentPage = val;
      this.loadTableData();
    },
    // 点击日期选择器
    handleClick (type) {
      this.$refs[`datetime${type}`].focus()
      this.$refs[`datetime${type}`].focus();
    },
    // 返回
    backFun () {
      this.$router.go(-1)
      this.$router.go(-1);
    },
    // 跳转到项目详情
    toProjectDetail (row) {
      this.$router.push({
        path: '/projectHome',
        path: "/projectHome",
        query: {
          projectId: row.id,
          projectName: row.name
        }
      })
          projectName: row.name,
        },
      });
    },
    // 跳转到隐患页面
    toHiddenDanger (row, type) {
      this.$router.push({
        path: '/hiddenDanger',
        path: "/hiddenDanger",
        query: {
          projectId: row.id,
          activeTab: type,
          projectCategory: ['ongoing', 'fixed'][localStorage.getItem('activeTab') - 1]
        }
      })
          projectCategory: ["ongoing", "fixed"][
            localStorage.getItem("activeTab") - 1
          ],
        },
      });
    },
    // 打开报告弹窗
    async openReportDialog (row) {
      this.currentProjectId = row.id
      this.projectVisible = true
      await this.loadCheckList(row.id)
      this.currentProjectId = row.id;
      this.projectVisible = true;
      await this.loadCheckList(row.id);
    },
    // 加载检查列表
    async loadCheckList (projectId) {
      this.checkLoading = true
      this.checkLoading = true;
      try {
        const res = await getDeviceDetailCheckList({
          projectId: projectId
        })
          projectId: projectId,
        });
        if (res) {
          this.checkList = res.records || []
          this.checkList = res.records || [];
        }
      } catch (error) {
        console.error('加载检查列表失败:', error)
        this.$message.error('加载检查列表失败')
        console.error("加载检查列表失败:", error);
        this.$message.error("加载检查列表失败");
      } finally {
        this.checkLoading = false
        this.checkLoading = false;
      }
    },
@@ -756,269 +787,296 @@
      this.companyInfo = {
        construction: this.parseCompanyInfo(row.constructionCompanyInfo),
        supervisor: this.parseCompanyInfo(row.supervisorCompanyInfo),
        development: this.parseCompanyInfo(row.developmentCompanyInfo)
      }
      this.companyVisible = true
        development: this.parseCompanyInfo(row.developmentCompanyInfo),
      };
      this.companyVisible = true;
    },
    // 解析单位信息字符串
    parseCompanyInfo (infoStr) {
      if (!infoStr) return {}
      if (!infoStr) return {};
      try {
        return JSON.parse(infoStr)
        return JSON.parse(infoStr);
      } catch (error) {
        return {}
        return {};
      }
    },
    // 检查是否有任何报告
    hasAnyReport (row) {
      return row.containSystemRes === 1 || row.containDeviceRes === 1
      return row.containSystemRes === 1 || row.containDeviceRes === 1;
    },
    // 检查报告可用性(当按钮被禁用时点击)
    checkReportAvailability (row) {
      if (!this.hasAnyReport(row)) {
        this.$message.warning('该计划暂无可查看的报告')
        this.$message.warning("该计划暂无可查看的报告");
      }
    },
    // 打开报告
    async openReport (type, row) {
      // 根据类型检查是否有对应报告
      const hasReport = type === 'safe'
      const hasReport =
        type === "safe"
        ? row.containSystemRes === 1
        : row.containDeviceRes === 1
          : row.containDeviceRes === 1;
      if (!hasReport) {
        this.$message.warning(`暂无${type === 'safe' ? '安全' : '设备'}报告`)
        return
        this.$message.warning(`暂无${type === "safe" ? "安全" : "设备"}报告`);
        return;
      }
      this.currentRow = row
      this.reportVisible = true
      this.showPreview = false
      this.reportLoading = true
      this.currentRow = row;
      this.reportVisible = true;
      this.showPreview = false;
      this.reportLoading = true;
      try {
        // 从 localStorage 获取模板 ID
        const deviceTemplateId = localStorage.getItem('deviceTemplateId')
        const safetyId = localStorage.getItem('safetyId')
        const safetyName = localStorage.getItem('safetyName')
        const deviceTemplateName = localStorage.getItem('deviceTemplateName')
        const deviceTemplateId = localStorage.getItem("deviceTemplateId");
        const safetyId = localStorage.getItem("safetyId");
        const safetyName = localStorage.getItem("safetyName");
        const deviceTemplateName = localStorage.getItem("deviceTemplateName");
        // 检查任务 ID
        const checkTaskId = row.id
        const checkTaskId = row.id;
        if (!checkTaskId) {
          this.$message.error('缺少检查任务ID')
          this.reportLoading = false
          this.reportVisible = false
          return
          this.$message.error("缺少检查任务ID");
          this.reportLoading = false;
          this.reportVisible = false;
          return;
        }
        // 使用当前时间戳作为 uuid
        const uuid = Date.now().toString()
        const uuid = Date.now().toString();
        // 第一步:调用接口生成Word文档(返回Word文档流)
        let wordBlob
        let wordBlob;
        try {
          if (type === 'safe') {
          if (type === "safe") {
            // 安全报告(模糊评价)
            if (!safetyId) {
              this.$message.error('缺少安全报告模板ID')
              this.reportLoading = false
              this.reportVisible = false
              return
              this.$message.error("缺少安全报告模板ID");
              this.reportLoading = false;
              this.reportVisible = false;
              return;
            }
            const safeCommentType = parseInt(
              localStorage.getItem("commentType") || "1"
            );
            if (safeCommentType === 1) {
            wordBlob = await getFuzzyReportWord({
              checkTaskId: checkTaskId,
              templateId: safetyId,
              uuid: safetyName
            })
                uuid: safetyName,
              });
            } else {
              wordBlob = await getScoreReportWord({
                checkTaskId: checkTaskId,
                templateId: safetyId,
                uuid: safetyName,
              });
            }
          } else {
            // 设备报告(打分评价)
            if (!deviceTemplateId) {
              this.$message.error('缺少设备报告模板ID')
              this.reportLoading = false
              this.reportVisible = false
              return
              this.$message.error("缺少设备报告模板ID");
              this.reportLoading = false;
              this.reportVisible = false;
              return;
            }
            const safeCommentType = parseInt(
              localStorage.getItem("commentType") || "1"
            );
            if (safeCommentType === 1) {
              wordBlob = await getFuzzyReportWord({
                checkTaskId: checkTaskId,
                templateId: deviceTemplateId,
                uuid: deviceTemplateName,
              });
            } else {
            wordBlob = await getScoreReportWord({
              checkTaskId: checkTaskId,
              templateId: deviceTemplateId,
              uuid: deviceTemplateName
            })
                uuid: deviceTemplateName,
              });
            }
          }
        } catch (error) {
          // 捕获生成Word文档时的错误
          console.error('生成Word文档失败:', error)
          this.$message.error(error.message || '生成报告失败')
          this.reportLoading = false
          this.reportVisible = false
          return
          console.error("生成Word文档失败:", error);
          this.$message.error(error.message || "生成报告失败");
          this.reportLoading = false;
          this.reportVisible = false;
          return;
        }
        // 检查 Word 文档是否生成成功
        if (!wordBlob || wordBlob.size === 0) {
          this.$message.error('报告生成失败')
          this.reportLoading = false
          this.reportVisible = false
          return
          this.$message.error("报告生成失败");
          this.reportLoading = false;
          this.reportVisible = false;
          return;
        }
        // 第二步:使用uuid获取PDF链接
        let pdfUrlResponse
        let pdfUrlResponse;
        try {
          if (type === 'safe') {
            pdfUrlResponse = await getFuzzyReportPdfUrl({ uuid: safetyName })
          if (type === "safe") {
            pdfUrlResponse = await getFuzzyReportPdfUrl({ uuid: safetyName });
          } else {
            pdfUrlResponse = await getScoreReportPdfUrl({ uuid: deviceTemplateName })
            pdfUrlResponse = await getFuzzyReportPdfUrl({
              uuid: deviceTemplateName,
            });
          }
        } catch (error) {
          // 捕获获取PDF链接时的错误
          console.error('获取PDF链接失败:', error)
          this.$message.error(error.message || '获取PDF链接失败')
          this.reportLoading = false
          this.reportVisible = false
          return
          console.error("获取PDF链接失败:", error);
          this.$message.error(error.message || "获取PDF链接失败");
          this.reportLoading = false;
          this.reportVisible = false;
          return;
        }
        // 根据后端返回的数据结构获取PDF链接
        // 假设后端返回格式为 { data: "http://xxx.pdf" } 或直接返回字符串
        const pdfUrl = pdfUrlResponse.data || pdfUrlResponse
        const pdfUrl = pdfUrlResponse.data || pdfUrlResponse;
        if (!pdfUrl) {
          this.$message.error('获取PDF链接失败')
          this.reportLoading = false
          this.reportVisible = false
          return
          this.$message.error("获取PDF链接失败");
          this.reportLoading = false;
          this.reportVisible = false;
          return;
        }
        // 保存PDF链接用于预览和下载
        this.pdfUrl = pdfUrl
        this.reportBlobUrl = pdfUrl // 用于下载
        this.showPreview = true
        this.pdfUrl = pdfUrl;
        this.reportBlobUrl = pdfUrl; // 用于下载
        this.showPreview = true;
        this.$message.success('报告加载成功')
        this.$message.success("报告加载成功");
      } catch (error) {
        console.error('获取报告失败:', error)
        console.error("获取报告失败:", error);
        // 优先显示错误对象中的 message
        if (error.message) {
          this.$message.error(error.message)
          this.$message.error(error.message);
        } else if (error.response) {
          const status = error.response.status
          const status = error.response.status;
          if (status === 404) {
            this.$message.error('报告不存在')
            this.$message.error("报告不存在");
          } else if (status === 500) {
            this.$message.error('服务器错误,请稍后重试')
            this.$message.error("服务器错误,请稍后重试");
          } else {
            this.$message.error(`获取报告失败: ${error.response.statusText || '未知错误'}`)
            this.$message.error(
              `获取报告失败: ${error.response.statusText || "未知错误"}`
            );
          }
        } else {
          this.$message.error('获取报告失败,请检查网络连接')
          this.$message.error("获取报告失败,请检查网络连接");
        }
        this.showPreview = false
        this.reportVisible = false
        this.showPreview = false;
        this.reportVisible = false;
      } finally {
        this.reportLoading = false
        this.reportLoading = false;
      }
    },
    // 下载报告
    downloadReport () {
      if (!this.reportBlobUrl) {
        this.$message.warning('报告未加载')
        return
        this.$message.warning("报告未加载");
        return;
      }
      try {
        // 直接打开PDF链接进行下载
        window.open(this.reportBlobUrl, '_blank')
        this.$message.success('报告下载成功')
        window.open(this.reportBlobUrl, "_blank");
        this.$message.success("报告下载成功");
      } catch (error) {
        console.error('下载报告失败:', error)
        this.$message.error('下载报告失败')
        console.error("下载报告失败:", error);
        this.$message.error("下载报告失败");
      }
    },
    // 关闭报告弹窗
    closeReportDialog () {
      this.reportVisible = false
      this.showPreview = false
      this.reportVisible = false;
      this.showPreview = false;
      // 清空PDF链接(不需要释放URL,因为是服务器链接)
      this.reportBlobUrl = ''
      this.pdfUrl = ''
      this.reportUrl = '' // 保留兼容性
      this.reportBlobUrl = "";
      this.pdfUrl = "";
      this.reportUrl = ""; // 保留兼容性
    },
    // 格式化项目类型
    formatProjectType (type) {
      const typeMap = {
        0: '房建',
        1: '市政',
        2: '建筑工程'
      }
      return typeMap[type] || '-'
        0: "房建",
        1: "市政",
        2: "建筑工程",
      };
      return typeMap[type] || "-";
    },
    // 格式化风险等级
    formatRiskLevel (level) {
      const levelMap = {
        0: '拒绝接受风险',
        1: '不可接受风险',
        2: '可接受风险',
        3: '可容忍风险',
        4: '可忽略风险'
      }
      return levelMap[level] || '-'
        0: "拒绝接受风险",
        1: "不可接受风险",
        2: "可接受风险",
        3: "可容忍风险",
        4: "可忽略风险",
      };
      return levelMap[level] || "-";
    },
    // 格式化时间
    formatTime (time) {
      return time ? moment(time).format('YYYY-MM-DD HH:mm:ss') : '-'
      return time ? moment(time).format("YYYY-MM-DD HH:mm:ss") : "-";
    },
    // 格式化计划类型
    formatPlanType (type) {
      const typeMap = {
        1: '日常检查',
        2: '专项检查',
        3: '综合检查'
      }
      return typeMap[type] || '-'
        1: "日常检查",
        2: "专项检查",
        3: "综合检查",
      };
      return typeMap[type] || "-";
    },
    // 判断检查情况是否良好(用于控制图标颜色)
    isCheckResultGood (checkResult) {
      // 检查情况只有"已检查"和"未检查"两种状态
      // 已检查显示绿色,未检查显示红色
      if (!checkResult) return false
      if (!checkResult) return false;
      return checkResult.includes('已检查')
      return checkResult.includes("已检查");
    },
    // 格式化地址信息
    formatAddress (addressInfo) {
      if (!addressInfo) return '-'
      if (!addressInfo) return "-";
      try {
        const addressObj = JSON.parse(addressInfo)
        const addressObj = JSON.parse(addressInfo);
        // 拼接完整地址:省 + 市 + 区 + 详细地址
        const { province, city, district, detail } = addressObj
        const parts = []
        if (province) parts.push(province)
        if (city) parts.push(city)
        if (district && district !== '其它区') parts.push(district)
        if (detail) parts.push(detail)
        return parts.join('') || '-'
        const { province, city, district, detail } = addressObj;
        const parts = [];
        if (province) parts.push(province);
        if (city) parts.push(city);
        if (district && district !== "其它区") parts.push(district);
        if (detail) parts.push(detail);
        return parts.join("") || "-";
      } catch (error) {
        // 如果解析失败,直接返回原始字符串
        return addressInfo || '-'
        return addressInfo || "-";
      }
    }
  }
}
    },
  },
};
</script>
<style lang="less">
.custom-popper {