董国庆
2026-01-19 cbba6f8649c98d39004cb45ee1d94f931c7d9c72
静态页面
1个文件已添加
3个文件已修改
597 ■■■■■ 已修改文件
screen/src/router/index.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/projectHome/index.vue 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/subentry/index.vue 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/view/subentryChart/index.vue 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
screen/src/router/index.js
@@ -102,6 +102,12 @@
    meta: {
      title: '分项隐患数'
    }
  },{
    path: '/subentryChart',
    component: () => import('../view/subentryChart/index.vue'),
    meta: {
      title: '分项隐患数'
    }
  }
]
screen/src/view/projectHome/index.vue
@@ -85,18 +85,14 @@
          <template #title> 设备信息 </template>
          <template #content>
            <div class="equipment-info">
              <img
                src="../../assets//img/project/chartBg.png"
                class="chart-bg"
                alt=""
              />
              <img src="../../assets//img/project/chartBg.png" class="chart-bg" alt="" />
              <div class="pie-canvas" ref="pieChart"></div>
              <div class="legend">
                <div class="legend-item" v-for="(it, idx) in legendData" :key="idx">
                  <div class="dot" :style="{ backgroundColor: it.itemStyle.color }"></div>
                  <div class="legend-info">
                    <div class="legend-name" :title="it.name">{{it.name}}</div>
                    <div class="legend-number">{{it.value}}</div>
                    <div class="legend-name" :title="it.name">{{ it.name }}</div>
                    <div class="legend-number">{{ it.value }}</div>
                  </div>
                </div>
              </div>
@@ -200,15 +196,8 @@
              <div class="people-card">
                <div class="people-item">
                  <div class="people-chart">
                    <el-progress
                      width="74"
                      type="circle"
                      :show-text="false"
                      :percentage="25"
                      define-back-color="#000"
                      color="#32C5FF"
                      :stroke-linecap="square"
                    ></el-progress>
                    <el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
                      color="#32C5FF" :stroke-linecap="square"></el-progress>
                    <div class="progress-card">
                      <div class="progress-num">25.4</div>
                      <div class="progress-unit">%</div>
@@ -218,15 +207,8 @@
                </div>
                <div class="people-item">
                  <div class="people-chart">
                    <el-progress
                      width="74"
                      type="circle"
                      :show-text="false"
                      :percentage="25"
                      define-back-color="#000"
                      color="#32C5FF"
                      :stroke-linecap="square"
                    ></el-progress>
                    <el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
                      color="#32C5FF" :stroke-linecap="square"></el-progress>
                    <div class="progress-card">
                      <div class="progress-num">25.4</div>
                      <div class="progress-unit">%</div>
@@ -236,15 +218,8 @@
                </div>
                <div class="people-item">
                  <div class="people-chart">
                    <el-progress
                      width="74"
                      type="circle"
                      :show-text="false"
                      :percentage="25"
                      define-back-color="#000"
                      color="#32C5FF"
                      :stroke-linecap="square"
                    ></el-progress>
                    <el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
                      color="#32C5FF" :stroke-linecap="square"></el-progress>
                    <div class="progress-card">
                      <div class="progress-num">25.4</div>
                      <div class="progress-unit">%</div>
@@ -256,15 +231,8 @@
              <div class="people-card2">
                <div class="people-item mr--67">
                  <div class="people-chart">
                    <el-progress
                      width="74"
                      type="circle"
                      :show-text="false"
                      :percentage="25"
                      define-back-color="#000"
                      color="#32C5FF"
                      :stroke-linecap="square"
                    ></el-progress>
                    <el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
                      color="#32C5FF" :stroke-linecap="square"></el-progress>
                    <div class="progress-card">
                      <div class="progress-num">25.4</div>
                      <div class="progress-unit">%</div>
@@ -274,15 +242,8 @@
                </div>
                <div class="people-item">
                  <div class="people-chart">
                    <el-progress
                      width="74"
                      type="circle"
                      :show-text="false"
                      :percentage="25"
                      define-back-color="#000"
                      color="#32C5FF"
                      :stroke-linecap="square"
                    ></el-progress>
                    <el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
                      color="#32C5FF" :stroke-linecap="square"></el-progress>
                    <div class="progress-card">
                      <div class="progress-num">25.4</div>
                      <div class="progress-unit">%</div>
@@ -315,21 +276,14 @@
              <div class="risk-body">
                <div class="risk-item" v-for="(item, index) in riskSourceList" :key="index">
                  <div class="label card1">{{ index + 1 }}</div>
                  <div
                    class="label card2"
                    :class="{
                      'merged-cell': item.showRiskScale && item.rowspan > 1,
                      'hidden-cell': !item.showRiskScale
                    }"
                  >
                    <div
                      v-if="item.showRiskScale"
                      class="merged-content"
                      :style="{
                        height: `${item.rowspan * 36}px`,
                        lineHeight: `${item.rowspan * 36}px`
                      }"
                    >
                  <div class="label card2" :class="{
                    'merged-cell': item.showRiskScale && item.rowspan > 1,
                    'hidden-cell': !item.showRiskScale
                  }">
                    <div v-if="item.showRiskScale" class="merged-content" :style="{
                      height: `${item.rowspan * 36}px`,
                      lineHeight: `${item.rowspan * 36}px`
                    }">
                      {{ item.riskScale }}
                    </div>
                  </div>
@@ -344,7 +298,7 @@
          </template>
        </TitleBox>
      </div>
      <div class="mt--30">
      <div class="mt--30" @click="toRiskTrend()">
        <TitleBox>
          <template #title>项目风险趋势 </template>
          <template #right>
@@ -465,13 +419,13 @@
      immediate: true,
    },
  },
  created() {},
  created() { },
  mounted() {
    this.setMapload()
    this.initPie3D()
    this.initRiskChart()
  },
  beforeDestroy() {},
  beforeDestroy() { },
  methods: {
    toPersonnelOnDuty() {
      this.$router.push({
@@ -1075,6 +1029,9 @@
        ]
      }
      riskChart.setOption(option)
    },
    toRiskTrend() {
      this.$router.push({ path: '/subentry', query: { projectId: '212222', projectName: '新项目' } })
    }
  }
}
@@ -1459,12 +1416,10 @@
          font-size: 24px;
          // color: linear-gradient(90deg, #66FFFF 0%, #F7B500 100%, #F7B500 100%);
          line-height: 28px;
          background: linear-gradient(
            90deg,
            #66ffff 0%,
            #f7b500 100%,
            #f7b500 100%
          );
          background: linear-gradient(90deg,
              #66ffff 0%,
              #f7b500 100%,
              #f7b500 100%);
          /*设置渐变的方向从左到右 颜色从ff0000到ffff00*/
          -webkit-background-clip: text;
          /*将设置的背景颜色限制在文字中*/
@@ -1514,23 +1469,18 @@
    width: 42px;
    height: 150px;
    box-sizing: border-box;
    background: linear-gradient(
      90deg,
      #040622 0%,
      #0952cf 22.29%,
      #155edf 53.43%,
      #0952cf 81.31%,
      #011845 100%
    );
    background: linear-gradient(90deg,
        #040622 0%,
        #0952cf 22.29%,
        #155edf 53.43%,
        #0952cf 81.31%,
        #011845 100%);
    border-radius: 96px;
    border: 2px solid;
    border-image: linear-gradient(
        90deg,
    border-image: linear-gradient(90deg,
        rgba(159, 255, 255, 0),
        rgba(133, 255, 255, 1),
        rgba(102, 255, 255, 0)
      )
      3 1;
        rgba(102, 255, 255, 0)) 3 1;
    padding: 24px 12px;
    font-weight: bold;
    font-size: 17px;
screen/src/view/subentry/index.vue
@@ -4,53 +4,32 @@
    <div class="pt--35 px--20 pb--54">
      <!-- 返回 -->
      <div @click="backFun" class="flex a-center cursor-pointer">
        <img
          src="../../assets/img/back.png"
          class="w--22 h--20 shrink0 mr--13"
        />
        <img src="../../assets/img/back.png" class="w--22 h--20 shrink0 mr--13" />
        <div class="c-fff lh--22 fs--22 SHSBold">分项隐患数</div>
      </div>
      <!-- 搜索 -->
      <div class="flex a-center j-between mt--24 mb--20">
        <div class="flex a-center gap--16">
          <div class="flex a-center searchTabs">
            <div
              @click="handleTabClick(item)"
              v-for="item in searchTabs"
              :key="item"
              :class="{ activeTabs: item === activeTabs }"
            >
            <div @click="handleTabClick(item)" v-for="item in searchTabs" :key="item"
              :class="{ activeTabs: item === activeTabs }">
              {{ item }}
            </div>
          </div>
          <div
            @click="handleClick(2)"
            class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker"
          >
            <el-date-picker
              v-model="monthrange"
              ref="monthrangeRef"
              type="monthrange"
              placeholder=""
              :disabled="activeTabs != '自定义'"
            >
          <div @click="handleClick(2)" class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker">
            <el-date-picker v-model="monthrange" ref="monthrangeRef" type="monthrange" placeholder=""
              :disabled="activeTabs != '自定义'">
            </el-date-picker>
            <div class="absolute w100 h100 flex a-center c-fff fs--14 SHS">
              <div class="flex a-center flex1 shrink0 gap--13">
                <img
                  src="../../assets/img/home/Shape@2x (6).png"
                  class="w--13 h--13 shrink0"
                />
                <img src="../../assets/img/home/Shape@2x (6).png" class="w--13 h--13 shrink0" />
                <div style="width: calc(100% - 20px)" v-if="dateTxt1">
                  {{ dateTxt1 }}
                </div>
                <div v-else>请选择日期</div>
              </div>
              <div class="flex a-center flex1 shrink0 gap--13">
                <img
                  src="../../assets/img/home/Shape@2x (6).png"
                  class="w--13 h--13 shrink0"
                />
                <img src="../../assets/img/home/Shape@2x (6).png" class="w--13 h--13 shrink0" />
                <div style="width: calc(100% - 20px)" v-if="dateTxt2">
                  {{ dateTxt2 }}
                </div>
@@ -58,63 +37,30 @@
              </div>
            </div>
          </div>
          <div
            class="flex a-center j-between w--300 pl--20 h--40 relative bgcolor1 searchBox"
          >
            <el-select
              filterable
              v-model="projectId"
              placeholder="请输入搜索内容"
              class="flex1"
            >
              <el-option
                v-for="item in [1, 2]"
                :key="item"
                :label="item"
                :value="item"
              >
          <div class="flex a-center j-between w--300 pl--20 h--40 relative bgcolor1 searchBox">
            <el-select filterable v-model="projectId" placeholder="请输入搜索内容" class="flex1">
              <el-option v-for="item in [1, 2]" :key="item" :label="item" :value="item">
              </el-option>
            </el-select>
            <div class="btn SHSR">搜索</div>
          </div>
        </div>
        <div
          @click="formVisible = true"
          class="flex a-center j-center w--150 h--40 cursor-pointer br--4 bgcolor88"
        >
          <img
            src="../../assets/img/home/Shape.png"
            class="w--12 h--12 shrink0 mr--9"
          />
        <div @click="toChart()" class="flex a-center j-center w--150 h--40 cursor-pointer br--4 bgcolor88">
          <img src="../../assets/img/home/Shape.png" class="w--12 h--12 shrink0 mr--9" />
          <div class="fs--14 lh--14 c-fff SHSR">分析图表</div>
        </div>
      </div>
      <!-- 表格 -->
      <CustomTable
        :data="[
          { projectName: '文明施工' },
          { projectName: '文明施工' },
          { projectName: '文明施工' },
        ]"
        height="800"
        style="border: 2px solid #3d7fff"
      >
      <CustomTable :data="[
        { projectName: '文明施工' },
        { projectName: '文明施工' },
        { projectName: '文明施工' },
      ]" height="800" style="border: 2px solid #3d7fff">
        <el-table-column label="隐患类型" prop="projectName" />
        <el-table-column
          v-for="(item, index) in tableHeader"
          :key="index"
          :label="item.label"
          :prop="item.prop"
        />
        <el-table-column v-for="(item, index) in tableHeader" :key="index" :label="item.label" :prop="item.prop" />
      </CustomTable>
      <CustomPagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="currentPage"
        :page-sizes="[100, 200, 300, 400]"
        :page-size="10"
        :total="400"
      />
      <CustomPagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
        :current-page="currentPage" :page-sizes="[100, 200, 300, 400]" :page-size="10" :total="400" />
    </div>
  </div>
</template>
@@ -167,7 +113,7 @@
      this.updateByActiveTab();
    }
  },
  mounted() {},
  mounted() { },
  methods: {
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
@@ -213,6 +159,15 @@
        current.add(1, "month");
      }
      this.tableHeader = list;
    },
    toChart() {
      this.$router.push({
        path: "/subentryChart",
        query: {
          projectId: this.projectId,
          projectName: this.projectName,
        },
      });
    },
    backFun() {
@@ -350,12 +305,10 @@
    position: absolute;
    top: -2px;
    right: -2px;
    background: linear-gradient(
      90deg,
      #3467d0 0%,
      #0d226a 50.35%,
      #376fe0 100%
    );
    background: linear-gradient(90deg,
        #3467d0 0%,
        #0d226a 50.35%,
        #376fe0 100%);
    height: 38px;
    display: flex;
    align-items: center;
screen/src/view/subentryChart/index.vue
New file
@@ -0,0 +1,346 @@
<template>
  <div>
    <HeaderBox :title="projectName" />
    <div class="pt--35 px--20 pb--54">
      <!-- 返回 -->
      <div @click="backFun" class="flex a-center cursor-pointer">
        <img src="../../assets/img/back.png" class="w--22 h--20 shrink0 mr--13" />
        <div class="c-fff lh--22 fs--22 SHSBold">分项隐患数</div>
      </div>
      <!-- 搜索 -->
      <div class="flex a-center j-between mt--24 mb--20">
        <div class="flex a-center gap--16">
          <div class="flex a-center searchTabs">
            <div @click="handleTabClick(item)" v-for="item in searchTabs" :key="item"
              :class="{ activeTabs: item === activeTabs }">
              {{ item }}
            </div>
          </div>
          <div @click="handleClick(2)" class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker">
            <el-date-picker v-model="monthrange" ref="monthrangeRef" type="monthrange" placeholder=""
              :disabled="activeTabs != '自定义'">
            </el-date-picker>
            <div class="absolute w100 h100 flex a-center c-fff fs--14 SHS">
              <div class="flex a-center flex1 shrink0 gap--13">
                <img src="../../assets/img/home/Shape@2x (6).png" class="w--13 h--13 shrink0" />
                <div style="width: calc(100% - 20px)" v-if="dateTxt1">
                  {{ dateTxt1 }}
                </div>
                <div v-else>请选择日期</div>
              </div>
              <div class="flex a-center flex1 shrink0 gap--13">
                <img src="../../assets/img/home/Shape@2x (6).png" class="w--13 h--13 shrink0" />
                <div style="width: calc(100% - 20px)" v-if="dateTxt2">
                  {{ dateTxt2 }}
                </div>
                <div v-else>请选择日期</div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- 折线图-->
      <div ref="dangerChart" class="dangerChart"></div>
    </div>
  </div>
</template>
<script>
import HeaderBox from "../header/index.vue";
import moment from "moment";
import * as echarts from "echarts";
export default {
  components: { HeaderBox },
  data() {
    return {
      monthrange: null,
      projectId: null,
      projectName: null,
      searchTabs: ["本年", "上一年", "自定义"],
      activeTabs: "本年",
      dateTxt1: "",
      dateTxt2: "",
      chartInstance: null,
      chartData: {
        xAxisData: [], // X轴月份数据
        series: [], // 系列数据
      },
    };
  },
  watch: {
    monthrange: {
      handler(newVal) {
        if (newVal && newVal.length === 2) {
          this.dateTxt1 = moment(newVal[0]).format("YYYY年MM月");
          this.dateTxt2 = moment(newVal[1]).format("YYYY年MM月");
          this.generateXAxisData(newVal[0], newVal[1]);
          this.fetchChartData();
        } else {
          this.dateTxt1 = "";
          this.dateTxt2 = "";
        }
      },
      immediate: true,
    },
  },
  created() {
    this.projectId = this.$route.query.projectId;
    this.projectName = this.$route.query.projectName;
    if (this.activeTabs !== "自定义") {
      this.updateByActiveTab();
    }
  },
  mounted() {
    this.initChart();
    window.addEventListener("resize", this.handleResize);
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.handleResize);
    if (this.chartInstance) {
      this.chartInstance.dispose();
    }
  },
  methods: {
    // 初始化图表
    initChart() {
      if (!this.$refs.dangerChart) return;
      this.chartInstance = echarts.init(this.$refs.dangerChart);
      this.renderChart();
    },
    // 生成随机颜色
    generateRandomColor() {
      const h = Math.floor(Math.random() * 360);
      const s = Math.floor(Math.random() * 30) + 60; // 60-90%
      const l = Math.floor(Math.random() * 20) + 50; // 50-70%
      return `hsl(${h}, ${s}%, ${l}%)`;
    },
    // 生成X轴数据
    generateXAxisData(startDate, endDate) {
      const start = moment(startDate).startOf("month");
      const end = moment(endDate).startOf("month");
      const list = [];
      const current = start.clone();
      while (current.isSameOrBefore(end, "month")) {
        list.push(current.format("YYYY-MM"));
        current.add(1, "month");
      }
      this.chartData.xAxisData = list;
    },
    // 获取图表数据(这里使用模拟数据,实际应该调用接口)
    fetchChartData() {
      // TODO: 替换为实际的接口调用
      // 模拟数据
      const seriesNames = ['文明施工', '高处作业', '临时用电', '消防安全', '机械设备'];
      this.chartData.series = seriesNames.map((name) => {
        const color = this.generateRandomColor();
        return {
          name: name,
          type: 'line',
          smooth: false,
          symbol: 'circle',
          symbolSize: 6,
          itemStyle: {
            color: color
          },
          lineStyle: {
            width: 2,
            color: color
          },
          data: this.chartData.xAxisData.map(() => Math.floor(Math.random() * 100))
        };
      });
      this.renderChart();
    },
    // 渲染图表
    renderChart() {
      if (!this.chartInstance) return;
      const option = {
        backgroundColor: 'transparent',
        legend: {
          top: 20,
          left: 'center',
          textStyle: {
            color: '#fff',
            fontSize: 14
          },
          itemWidth: 25,
          itemHeight: 2,
          itemGap: 20,
          icon: 'rect'
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          top: '80',
          containLabel: true
        },
        tooltip: {
          trigger: 'axis',
          backgroundColor: 'rgba(0, 97, 255, 1)',
          borderColor: '#3d7fff',
          borderWidth: 1,
          padding: [5, 5],
          textStyle: {
            color: '#fff',
            fontSize: 14,
            align: 'left'
          },
          formatter: (params) => {
            let result = `<div style="padding: 5px 0; color: rgba(0, 235, 252, 1); font-weight: bold; font-size: 15px;">${params[0].axisValue}</div>`;
            params.forEach(item => {
              result += `<div style="display: flex; align-items: center; padding: 4px 0;margin-right: 20px;">
                <span style="display: inline-block; width: 10px; height: 10px; background-color: ${item.color}; border-radius: 50%; margin-right: 10px; flex-shrink: 0;"></span>
                <span style="flex: 1; text-align: left;">${item.seriesName}:${item.value}</span>
              </div>`;
            });
            return result;
          }
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: this.chartData.xAxisData,
          axisLine: {
            lineStyle: {
              color: '#3d7fff'
            }
          },
          axisLabel: {
            color: '#fff',
            fontSize: 12,
            interval: 0,
            rotate: 0
          },
          axisTick: {
            show: false
          }
        },
        yAxis: {
          type: 'value',
          axisLine: {
            show: true,
            lineStyle: {
              color: '#3d7fff'
            }
          },
          axisLabel: {
            color: '#fff',
            fontSize: 12
          },
          splitLine: {
            lineStyle: {
              color: 'rgba(61, 127, 255, 0.2)',
              type: 'dashed'
            }
          }
        },
        series: this.chartData.series
      };
      this.chartInstance.setOption(option);
    },
    // 窗口大小改变时重新渲染
    handleResize() {
      if (this.chartInstance) {
        this.chartInstance.resize();
      }
    },
    // 点击选择时间
    handleClick() {
      this.$refs.monthrangeRef.focus();
    },
    // 切换tab
    handleTabClick(item) {
      this.activeTabs = item;
      this.updateByActiveTab();
    },
    // 更新时间
    updateByActiveTab() {
      const currentYear = moment().year();
      if (this.activeTabs === "本年") {
        const start = moment().year(currentYear).month(0).startOf("month").toDate();
        const end = moment().year(currentYear).month(11).startOf("month").toDate();
        this.monthrange = [start, end];
      } else if (this.activeTabs === "上一年") {
        const lastYear = currentYear - 1;
        const start = moment().year(lastYear).month(0).startOf("month").toDate();
        const end = moment().year(lastYear).month(11).startOf("month").toDate();
        this.monthrange = [start, end];
      }
    },
    // 返回上一页
    backFun() {
      this.$router.go(-1);
    },
  },
};
</script>
<style scoped lang="less">
.bgcolor1 {
  background: rgba(2, 0, 77, 0.8);
  box-shadow: inset 0px 0px 34px 0px rgba(17, 40, 255, 0.66);
  border: 2px solid #0d53b7;
}
.searchTabs {
  background: rgba(2, 0, 77, 0.8);
  box-shadow: inset 0px 0px 34px 0px rgba(17, 40, 255, 0.66);
  font-size: 12px;
  color: rgba(255, 255, 255, 0.8);
  font-family: "Source Han Sans CN Normal";
  text-align: center;
  flex-shrink: 0;
  div {
    padding: 2px;
    width: 76px;
    cursor: pointer;
    line-height: 34px;
    border: 1px solid #0d53b7;
    &:hover {
      background: #017be7;
      color: #ffffff;
    }
  }
  .activeTabs {
    background: #017be7;
    color: #ffffff;
  }
}
.picker {
  ::v-deep .el-input__icon {
    display: none;
  }
  ::v-deep .el-range-separator {
    display: none;
  }
  ::v-deep .el-input__inner {
    background: transparent;
    border: none;
    color: transparent;
  }
  ::v-deep .el-range-input {
    background: transparent;
    border: none;
    color: transparent;
  }
}
.dangerChart {
  width: 1880px;
  height: 800px;
  background: #071166;
  border: 2px solid rgba(61, 127, 255, .2);
}
</style>