8caf427556921d82c53fb1d54bcf150e79d3def7..67d3b57765b0ba66ae25a9da84a16e44a4ef2937
2026-03-25 peng
调整
67d3b5 对比 | 目录
2026-03-24 peng
调整
05c747 对比 | 目录
2026-03-24 peng
调整
e46f78 对比 | 目录
7个文件已修改
1个文件已添加
765 ■■■■■ 已修改文件
src/assets/img/sdjt.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tools/Logo.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataAnalysis/CarInfoList.vue 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataAnalysis/components/CustTypeConfig.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataAnalysis/components/CustTypeModal.vue 180 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataAnalysis/components/DataReGasStation.vue 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataAnalysis/components/DataReLineChart.vue 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataAnalysis/components/DataRePieChart.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/sdjt.jpg
src/components/tools/Logo.vue
@@ -1,15 +1,15 @@
<template>
  <router-link :to="{ name: 'dashboard' }">
    <div class="flex logo-container flex-center">
      <!-- <img class="logo-img" :src="@/assets/img/logo.png" alt="logo" /> -->
      <img v-if="localInfo.logoPath" class="logo-img" :src="localInfo.logoPath" alt="logo" />
      <img v-else class="logo-img" src="@/assets/img/menuLogo.png" alt="logo" />
      <img class="logo-img" :src="homeLogo" alt="logo" />
      <!-- <h1 class="logo-title" v-if="showTitle">{{ title }}</h1> -->
    </div>
  </router-link>
</template>
<script>
import homeLogo from '@/assets/img/sdjt.jpg'
export default {
  name: 'Logo',
@@ -27,11 +27,8 @@
  },
  data() {
    return {
      localInfo: {},
      homeLogo,
    }
  },
  created() {
    this.localInfo = JSON.parse(localStorage.getItem('localInfo'))
  },
}
</script>
@@ -76,4 +73,3 @@
  }
}
</style>
src/views/dataAnalysis/CarInfoList.vue
@@ -44,12 +44,17 @@
          />
        </a-form-item>
        <a-form-item label="客户类型">
          <j-dict-select-tag
          <a-select
            style="width: 180px"
            v-model="queryParam.clientType"
            placeholder="请选择"
            dictCode="client_type"
          />
            allowClear
          >
            <a-select-option v-for="item in clientTypeOptions" :key="item.id" :value="item.id">
              {{ item.clientName }}
            </a-select-option>
          </a-select>
        </a-form-item>
          <!-- <a-select style="width: 180px" allowClear v-model="queryParam.clientType" placeholder="请选择客户类型">
            <a-select-option :value="1"> 普通客户 </a-select-option>
            <a-select-option :value="2"> 潜在客户 </a-select-option>
@@ -158,6 +163,7 @@
      description: 'CarInfo列表',
      queryParam: {},
      tagList: [], //所有标签集合
      clientTypeOptions: [], //客户类型列表
      // 表头
      columns: [
        {
@@ -244,6 +250,14 @@
  },
  methods: {
    // 获取客户类型列表
    getClientTypeOptions() {
      getAction('/jyz/client/listClientType').then((res) => {
        if (res.code === 200) {
          this.clientTypeOptions = res.result
        }
      })
    },
    delSlectTag(item) {
      console.log(item)
      let index = this.selectTagList.findIndex((el) => el == item)
@@ -321,10 +335,19 @@
      this.queryParam.orgCode = node.node.dataRef.orgCode
      this.loadData()
    },
    // 获取客户类型列表
    getClientTypeOptions() {
      getAction('/jyz/client/listClientType').then((res) => {
        if (res.code === 200) {
          this.clientTypeOptions = res.result
        }
      })
    },
  },
  created() {
    this.getTagInfo(true)
    this.getClientTypeOptions()
  },
}
</script>
src/views/dataAnalysis/components/CustTypeConfig.vue
@@ -46,6 +46,9 @@
<script>
import CustTypeModal from './CustTypeModal'
import { JeecgListMixin } from '@tievd/cube-block/lib/mixins/JeecgListMixin'
import { UI_CACHE_DB_DICT_DATA } from '@tievd/cube-block/lib/store/mutation-types'
import { deleteAction, getAction } from '@tievd/cube-block/lib/api/manage'
import Vue from 'vue'
export default {
  name: 'ActivityList',
@@ -123,6 +126,35 @@
  },
  methods: {
    modalFormOk() {
      this.loadData()
      this.refreshDictCache()
    },
    refreshDictCache() {
      getAction('/sys/dict/queryAllDictItems').then((res) => {
        if (res.success) {
          Vue.ls.remove(UI_CACHE_DB_DICT_DATA)
          Vue.ls.set(UI_CACHE_DB_DICT_DATA, res.result, 7 * 24 * 60 * 60 * 1000)
        }
      })
    },
    handleDelete(id) {
      // 删除前清除前端字典缓存
      // 删除后刷新页面以更新字典缓存
      deleteAction(this.url.delete, { id: id })
        .then((res) => {
          if (res.success) {
            this.$message.success(res.message)
            this.loadData()
            this.refreshDictCache()
          } else {
            this.$message.warning(res.message)
          }
        })
        .finally(() => {
          this.loading = false
        })
    },
    transformConfigText(obj) {
      var text = ''
      if (obj.timeStr == '1,YEARS') {
src/views/dataAnalysis/components/CustTypeModal.vue
@@ -17,6 +17,9 @@
            v-model="clientConfigsList.clientName"
          ></a-input>
        </a-form-item>
        <div v-if="!clientConfigsList.clientConfigs || clientConfigsList.clientConfigs.length === 0" style="text-align: center">
          <a-button type="primary" @click="addConditions"> 添加规则 </a-button>
        </div>
        <div v-for="(subItem, subIndex) in clientConfigsList.clientConfigs" :key="subIndex">
          <div style="display: flex">
            <div>条件{{ subIndex + 1 }}:</div>
@@ -44,34 +47,100 @@
              <a-select-option value="1,YEARS"> 近1年 </a-select-option>
            </a-select>
          </a-form-item>
          <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="加油频次">
          <!-- 规则类型选择:加油频次 OR 加油趋势 -->
          <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则类型">
            <a-select
              :disabled="subItem.timeStr == '7,DAYS' || subItem.timeStr == '30,DAYS'"
              style="width: 300px"
              v-model="subItem.countType"
              v-model="subItem.ruleType"
              placeholder="请选择"
              @change="ruleTypeChange($event, subIndex)"
            >
              <a-select-option :value="1"> 累计 </a-select-option>
              <a-select-option :value="2"> 每月 </a-select-option>
              <a-select-option :value="1"> 加油频次 </a-select-option>
              <a-select-option :value="2"> 加油趋势 </a-select-option>
            </a-select>
            <div style="display: flex">
              <a-select style="width: 150px" v-model="subItem.countRef" placeholder="请选择">
                <a-select-option :value="1"> 大于 </a-select-option>
                <a-select-option :value="0"> 等于 </a-select-option>
                <a-select-option :value="-1"> 小于 </a-select-option>
          </a-form-item>
          <!-- 加油频次配置(ruleType=1时显示) -->
          <div v-show="subItem.ruleType == 1">
            <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="加油频次">
              <a-select
                :disabled="subItem.timeStr == '7,DAYS' || subItem.timeStr == '30,DAYS'"
                style="width: 300px"
                v-model="subItem.countType"
                placeholder="请选择"
              >
                <a-select-option :value="1"> 累计 </a-select-option>
                <a-select-option :value="2"> 每月 </a-select-option>
              </a-select>
              <div style="display: flex">
                <a-select style="width: 150px" v-model="subItem.countRef" placeholder="请选择">
                  <a-select-option :value="1"> 大于 </a-select-option>
                  <a-select-option :value="0"> 等于 </a-select-option>
                  <a-select-option :value="-1"> 小于 </a-select-option>
                </a-select>
                <a-input
                  style="width: 150px"
                  placeholder="请输入次数"
                  onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}"
                  onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'0')}else{this.value=this.value.replace(/\D/g,'')}"
                  v-model="subItem.countNum"
                ></a-input>
              </div>
            </a-form-item>
          </div>
          <!-- 加油趋势配置(ruleType=2时显示) -->
          <div v-show="subItem.ruleType == 2">
            <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="加油趋势">
              <a-select
                style="width: 300px"
                v-model="subItem.countTrend"
                placeholder="请选择"
              >
                <a-select-option :value="1"> 稳定 </a-select-option>
                <a-select-option :value="2"> 减少 </a-select-option>
              </a-select>
              <div v-if="subItem.countTrend == 1" style="color: #999; font-size: 12px; margin-top: 5px;">
                稳定:历史月加油次数≥次数 且 近期月加油次数≥次数
              </div>
              <div v-if="subItem.countTrend == 2" style="color: #999; font-size: 12px; margin-top: 5px;">
                减少:历史月加油次数≥次数 但 近期月加油次数<次数
              </div>
            </a-form-item>
            <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="历史月数">
              <a-select
                style="width: 140px"
                v-model="subItem.historyMonths"
                placeholder="历史月数"
              >
                <a-select-option :value="3"> 3个月 </a-select-option>
                <a-select-option :value="6"> 6个月 </a-select-option>
                <a-select-option :value="9"> 9个月 </a-select-option>
                <a-select-option :value="12"> 12个月 </a-select-option>
              </a-select>
            </a-form-item>
            <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="近期月数">
              <a-select
                style="width: 140px"
                v-model="subItem.recentMonths"
                placeholder="近期月数"
              >
                <a-select-option :value="1"> 1个月 </a-select-option>
                <a-select-option :value="2"> 2个月 </a-select-option>
                <a-select-option :value="3"> 3个月 </a-select-option>
              </a-select>
            </a-form-item>
            <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="固定加油次数">
              <a-input
                style="width: 150px"
                placeholder="请输入次数"
                placeholder="每月固定次数"
                onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}"
                onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'0')}else{this.value=this.value.replace(/\D/g,'')}"
                v-model="subItem.countNum"
              ></a-input>
            </div>
          </a-form-item>
        </div>
        <div style="text-align: center">
          <a-button type="primary" @click="addConditions"> 增加条件 </a-button>
            </a-form-item>
          </div>
          <div style="text-align: center" v-if="subIndex === clientConfigsList.clientConfigs.length - 1">
            <a-button type="primary" @click="addConditions"> 增加条件 </a-button>
          </div>
        </div>
      </div>
    </a-spin>
@@ -119,6 +188,21 @@
  methods: {
    moment,
    buildCondition(condition = {}) {
      return Object.assign(
        {
          ruleType: 1,
          timeStr: '3,MONTHS',
          countType: 2,
          countRef: 1,
          countNum: 3,
          countTrend: null,
          historyMonths: null,
          recentMonths: null,
        },
        condition
      )
    },
    //删除条件
    delConditions(index) {
      console.log(index)
@@ -129,10 +213,46 @@
        this.clientConfigsList.clientConfigs[index].countType = 1
      }
    },
    trendChange(e, index) {
      // 选择趋势时,设置默认值
      if (e > 0) {
        this.clientConfigsList.clientConfigs[index].historyMonths = 3
        this.clientConfigsList.clientConfigs[index].recentMonths = 3
      } else {
        // 清除趋势配置
        this.clientConfigsList.clientConfigs[index].historyMonths = null
        this.clientConfigsList.clientConfigs[index].recentMonths = null
      }
    },
    ruleTypeChange(e, index) {
      let target = this.clientConfigsList.clientConfigs[index]
      // 切换规则类型时,清空对应的配置
      if (e == 1) {
        // 加油频次:清除趋势配置
        Object.assign(target, {
          ruleType: 1,
          countTrend: null,
          historyMonths: null,
          recentMonths: null,
          countType: target.countType || 2,
          countRef: target.countRef || 1,
        })
      } else if (e == 2) {
        // 加油趋势:清除频次配置,设置默认值
        Object.assign(target, {
          ruleType: 2,
          countType: 2,
          countRef: 1,
          countTrend: target.countTrend || 1,
          historyMonths: target.historyMonths || 3,
          recentMonths: target.recentMonths || 3,
        })
      }
    },
    //新增条件
    addConditions(index) {
      console.log(index)
      this.clientConfigsList.clientConfigs.push({})
      this.clientConfigsList.clientConfigs.push(this.buildCondition())
    },
    disabledDate(current) {
      // Can not select days before today and today
@@ -151,7 +271,7 @@
    add() {
      this.edit({
        clientName: '',
        clientConfigs: [{}],
        clientConfigs: [this.buildCondition()],
      })
    },
    edit(record) {
@@ -163,7 +283,11 @@
      // }
      this.visible = true
      this.model = Object.assign({}, record)
      this.clientConfigsList = record
      this.clientConfigsList = Object.assign({}, record, {
        clientConfigs: (record.clientConfigs || []).length
          ? record.clientConfigs.map((item) => this.buildCondition(item))
          : [],
      })
    },
    close() {
      this.$emit('close')
@@ -183,16 +307,20 @@
        if (JSON.stringify(el) == '{}') {
          isEmpty = true
        }
        for (let k in el) {
          if (el[k] == '' || el[k].length == '0') {
        // 根据规则类型校验
        if (el.ruleType == 1) {
          // 加油频次:检查时间范围、次数类型、关系、次数
          if (!el.timeStr || !el.countRef || !el.countType || !el.countNum) {
            isEmpty = true
          }
        }
        if (!el.timeStr || !el.countRef || !el.countType || !el.countNum) {
          isEmpty = true
          if (el.countRef == 0 || el.countNum == 0) {
            isEmpty = false
        } else if (el.ruleType == 2) {
          // 加油趋势:检查时间范围、趋势、历史月数、近期月数、次数
          if (!el.timeStr || !el.countTrend || !el.historyMonths || !el.recentMonths || !el.countNum) {
            isEmpty = true
          }
        } else {
          // 未选择规则类型
          isEmpty = true
        }
      })
      if (isEmpty) {
src/views/dataAnalysis/components/DataReGasStation.vue
@@ -1,5 +1,5 @@
<template>
  <div style="margin-right: 30px" class="data-reMechanism">
  <div style="margin-right: 30px" class="data-reMechanism" :class="{ 'trend-detail-open': trendDetailVisible }">
    <div style="display: flex; align-items: center">
      <div style="margin-right:12px;font-size:16px;font-weight:bold">趋势分析</div>
      <a-radio-group v-model="trendAnalysisType" @change="trendAnalysisTypeChange">
@@ -12,7 +12,7 @@
      </a-radio-group>
    </div>
    <div>
      <div style="display: flex; align-items: flex-end; flex-direction: column">
      <div class="trend-analysis-chart" :class="{ 'is-modal-open': trendDetailVisible }" style="display: flex; align-items: flex-end; flex-direction: column">
        <a-radio-group
          style="margin: 20px 0"
          size="small"
@@ -31,6 +31,8 @@
          :chartData="trendAnalysisData"
          style="width: 100%"
          :showpercent="trendAnalysisChartShowPercent"
          :enablePointClick="true"
          @chart-click="handleTrendChartClick"
        ></DataReLineChart>
      </div>
      <div id="trendAnalysisChartID"></div>
@@ -122,6 +124,41 @@
        </div>
      </div>
    </div>
    <div class="oil-freq-compare-ct">
      <div class="block-title">活动前后车辆加油频次</div>
      <div class="oil-freq-compare-operator">
        <span>活动前时段</span>
        <a-range-picker
          style="width: 360px; margin: 0 12px"
          :value="beforeTimeRange"
          format="YYYY-MM-DD HH:mm:ss"
          valueFormat="YYYY-MM-DD HH:mm:ss"
          :show-time="{ defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')] }"
          @change="beforeTimeChange"
        />
        <span>活动后时段</span>
        <a-range-picker
          style="width: 360px; margin: 0 12px"
          :value="afterTimeRange"
          format="YYYY-MM-DD HH:mm:ss"
          valueFormat="YYYY-MM-DD HH:mm:ss"
          :show-time="{ defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')] }"
          @change="afterTimeChange"
        />
        <a-button type="primary" @click="getOilFreqCompareData"> 统计频次 </a-button>
      </div>
      <a-table
        class="oil-freq-compare-table"
        rowKey="licenseNum"
        size="middle"
        :columns="oilFreqCompareColumns"
        :dataSource="oilFreqCompareData"
        :loading="oilFreqCompareLoading"
        :scroll="{ y: 420 }"
        :pagination="{ pageSize: 10, showSizeChanger: true, pageSizeOptions: ['10', '20', '50'] }"
      >
      </a-table>
    </div>
    <div>
      <div class="block-title">车型分析</div>
      <div style="display: flex">
@@ -175,6 +212,33 @@
      :typeOneTotal="customerOneTotal"
      :typeTwoTotal="customerTwoTotal"
    ></TopTenCustomers>
    <a-modal
      centered
      width="70%"
      :footer="null"
      v-model="trendDetailVisible"
      :title="trendDetailTitle"
      :zIndex="5000"
      :getContainer="getModalContainer"
      wrapClassName="trend-detail-modal-wrap"
      @cancel="closeTrendDetail"
    >
      <a-table
        size="middle"
        bordered
        :columns="trendDetailColumns"
        :dataSource="trendDetailData"
        :loading="trendDetailLoading"
        :scroll="{ y: 420 }"
        :pagination="{ pageSize: 10, showSizeChanger: true, pageSizeOptions: ['10', '20', '50'] }"
        :rowKey="
          (record, index) => {
            return record.id || record.licenseNum || record.captureTime || record.startTime || index
          }
        "
      >
      </a-table>
    </a-modal>
  </div>
</template>
@@ -183,6 +247,7 @@
import DataReLineChart from './DataReLineChart'
import DataRePieChart from './DataRePieChart'
import { getAction, postAction } from '@tievd/cube-block/lib/api/manage'
import moment from 'moment'
export default {
  name: 'DataReMechanism',
  components: {
@@ -228,10 +293,131 @@
      trendAnalysisUnit: 'DAYS', //趋势分析选择时间
      trendAnalysisData: {}, //趋势分析数据
      trendAnalysisChart: null, //趋势分析图表
      trendDetailVisible: false,
      trendDetailTitle: '趋势分析明细',
      trendDetailLoading: false,
      trendDetailData: [],
      trendDetailColumns: [],
      beforeStartTime: '',
      beforeEndTime: '',
      afterStartTime: '',
      afterEndTime: '',
      beforeTimeRange: [],
      afterTimeRange: [],
      oilFreqCompareLoading: false,
      oilFreqCompareData: [],
      oilFreqCompareColumns: [
        {
          title: '车牌号',
          align: 'center',
          dataIndex: 'licenseNum',
        },
        {
          title: '活动前加油次数',
          align: 'center',
          dataIndex: 'beforeOilCount',
        },
        {
          title: '活动后加油次数',
          align: 'center',
          dataIndex: 'afterOilCount',
        },
        {
          title: '变化值',
          align: 'center',
          dataIndex: 'diffOilCount',
        },
        {
          title: '变化率',
          align: 'center',
          dataIndex: 'diffRate',
          customRender: function (t) {
            if (t === null || t === undefined) {
              return '--'
            }
            return t + '%'
          },
        },
      ],
    }
  },
  methods: {
    moment,
    getModalContainer() {
      return document.body
    },
    getTrendDetailColumnsByType() {
      let commonIndexColumn = {
        title: '序号',
        dataIndex: '',
        key: 'rowIndex',
        width: 80,
        align: 'center',
        customRender: function (t, r, index) {
          return parseInt(index) + 1
        },
      }
      if (this.trendAnalysisType == 0) {
        return [
          commonIndexColumn,
          { title: '抓拍时间', align: 'center', dataIndex: 'captureTime' },
          { title: '车流量', align: 'center', dataIndex: 'carCount' },
          { title: '车型编码', align: 'center', dataIndex: 'modelCode' },
          { title: '设备编码', align: 'center', dataIndex: 'cameraCode' },
        ]
      }
      if (this.trendAnalysisType == 4 || this.trendAnalysisType == 5) {
        return [
          commonIndexColumn,
          { title: '更新时间', align: 'center', dataIndex: 'updateTimeSelf' },
          { title: '车牌号', align: 'center', dataIndex: 'licenseNum' },
          { title: '客户类型', align: 'center', dataIndex: 'clientName' },
          { title: '累计加油次数', align: 'center', dataIndex: 'oilCount' },
          { title: '累计油品销量', align: 'center', dataIndex: 'oilSum' },
        ]
      }
      return [
        commonIndexColumn,
        { title: '进站时间', align: 'center', dataIndex: 'startTime' },
        { title: '车牌号', align: 'center', dataIndex: 'licenseNum' },
        { title: '行为类型', align: 'center', dataIndex: 'behaviorText' },
        { title: '加油位', align: 'center', dataIndex: 'oilPosition' },
        { title: '油品销量', align: 'center', dataIndex: 'oilVolume' },
        { title: '通过率(分)', align: 'center', dataIndex: 'spandTime' },
      ]
    },
    handleTrendChartClick(params) {
      if (!params || !params.name) {
        return
      }
      if (this.$refs.trendAnalysisLineRef && this.$refs.trendAnalysisLineRef.hideTooltip) {
        this.$refs.trendAnalysisLineRef.hideTooltip()
      }
      this.trendDetailVisible = true
      this.trendDetailTitle = '趋势分析明细 - ' + params.name
      this.trendDetailColumns = this.getTrendDetailColumnsByType()
      this.trendDetailLoading = true
      postAction('/jyz/dataTable/statTrendDetail', {
        orgCode: this.selectTreeObj.orgCode,
        startTime: this.startTime,
        endTime: this.endTime,
        trendType: this.trendAnalysisType,
        timeUnit: this.trendAnalysisUnit,
        statTime: params.name,
        seriesName: params.seriesName,
      })
        .then((res) => {
          this.trendDetailData = res.result || []
        })
        .finally(() => {
          this.trendDetailLoading = false
        })
    },
    closeTrendDetail() {
      this.trendDetailVisible = false
      this.trendDetailData = []
    },
    //获取车型/加油位折线图数据
    getVehicleTypeLineData() {
      postAction('/jyz/dataTable/statisMidTable', {
@@ -340,15 +526,70 @@
        this.contrastObj = res.result
      })
    },
    beforeTimeChange(e, t) {
      this.beforeTimeRange = t
      this.beforeStartTime = t && t.length ? t[0] : ''
      this.beforeEndTime = t && t.length ? t[1] : ''
    },
    afterTimeChange(e, t) {
      this.afterTimeRange = t
      this.afterStartTime = t && t.length ? t[0] : ''
      this.afterEndTime = t && t.length ? t[1] : ''
    },
    setOilFreqDefaultRangeByActivity(activity) {
      if (!activity || !activity.startTime || !activity.endTime) {
        return
      }
      this.afterStartTime = activity.startTime
      this.afterEndTime = activity.endTime
      this.afterTimeRange = [this.afterStartTime, this.afterEndTime]
      let startMoment = moment(activity.startTime)
      let endMoment = moment(activity.endTime)
      let spanSeconds = endMoment.diff(startMoment, 'seconds')
      if (spanSeconds <= 0) {
        return
      }
      this.beforeEndTime = startMoment.format('YYYY-MM-DD HH:mm:ss')
      this.beforeStartTime = startMoment.clone().subtract(spanSeconds, 'seconds').format('YYYY-MM-DD HH:mm:ss')
      this.beforeTimeRange = [this.beforeStartTime, this.beforeEndTime]
    },
    getOilFreqCompareData() {
      if (!this.beforeStartTime || !this.beforeEndTime || !this.afterStartTime || !this.afterEndTime) {
        this.oilFreqCompareData = []
        return
      }
      this.oilFreqCompareLoading = true
      postAction('/jyz/dataTable/statisOilFreqCompare', {
        orgCode: this.selectTreeObj.orgCode,
        beforeStartTime: this.beforeStartTime,
        beforeEndTime: this.beforeEndTime,
        afterStartTime: this.afterStartTime,
        afterEndTime: this.afterEndTime,
      })
        .then((res) => {
          this.oilFreqCompareData = res.result || []
        })
        .finally(() => {
          this.oilFreqCompareLoading = false
        })
    },
    activeTimeChange(e) {
      console.log(e)
      if (e) {
        let item = this.activeOptions.find((el) => el.id == e)
        this.startTime = item.startTime
        this.endTime = item.endTime
        this.setOilFreqDefaultRangeByActivity(item)
      } else {
        this.startTime = ''
        this.endTime = ''
        this.beforeStartTime = ''
        this.beforeEndTime = ''
        this.afterStartTime = ''
        this.afterEndTime = ''
        this.beforeTimeRange = []
        this.afterTimeRange = []
        this.oilFreqCompareData = []
      }
    },
    activeChange() {},
@@ -390,6 +631,7 @@
      this.getVehicleStationPieData()
      this.getFuelingStationPieData()
      this.getTrendAnalysis()
      this.getOilFreqCompareData()
    },
    initData() {
      this.getCustomerData()
@@ -410,7 +652,7 @@
<style scoped lang="less">
@import '~@assets/less/common.less';
/deep/ .ant-table-content {
  height: 500px;
  height: auto;
  .ant-table-placeholder {
    background: none;
    border: none;
@@ -420,7 +662,29 @@
  background: #fff;
}
.data-reMechanism {
  position: relative;
  isolation: isolate;
  > div {
    position: relative;
    z-index: 2;
  }
  > div:nth-child(2) {
    z-index: 1;
  }
  &.trend-detail-open {
    pointer-events: none;
  }
  .trend-analysis-chart {
    position: relative;
    z-index: 1;
    overflow: hidden;
  }
  .trend-analysis-chart.is-modal-open {
    pointer-events: none;
  }
  .table-operator {
    position: relative;
    z-index: 2;
    margin: 24px 0 0 0;
  }
  .block-title {
@@ -428,6 +692,8 @@
    font-size: 16px;
  }
  .fueling-overview-ct {
    position: relative;
    z-index: 2;
    // margin: 0 12px;
    .fueling-overview-block {
      display: flex;
@@ -443,5 +709,33 @@
      }
    }
  }
  .oil-freq-compare-ct {
    position: relative;
    z-index: 2;
    margin: 8px 12px 24px 12px;
    .oil-freq-compare-operator {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 8px 0;
      margin: 12px 0;
    }
    .oil-freq-compare-table {
      /deep/ .ant-table-body {
        min-height: 240px;
      }
      /deep/ .ant-table-pagination.ant-pagination {
        margin: 12px 0 4px 0;
      }
    }
  }
}
</style>
<style lang="less">
.trend-detail-modal-wrap {
  z-index: 5000 !important;
}
.trend-detail-modal-wrap + .ant-modal-mask {
  z-index: 4999 !important;
}
</style>
src/views/dataAnalysis/components/DataReLineChart.vue
@@ -22,13 +22,204 @@
      type: Boolean,
      default: false,
    },
    enablePointClick: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      chartEntity: null,
      zrClickHandler: null,
      zrMoveHandler: null,
      zrMouseOutHandler: null,
      lastHoverIndex: -1,
      lastEmitName: '',
      lastEmitAt: 0,
      lastLegendToggleAt: 0,
    }
  },
  methods: {
    getOffsetPoint(event) {
      if (!event) {
        return null
      }
      if (event.offsetX !== undefined && event.offsetY !== undefined) {
        return [event.offsetX, event.offsetY]
      }
      if (event.event && event.event.offsetX !== undefined && event.event.offsetY !== undefined) {
        return [event.event.offsetX, event.event.offsetY]
      }
      return null
    },
    isPointInGrid(point) {
      if (!this.chartEntity || !this.chartEntity.containPixel || !point) {
        return false
      }
      return this.chartEntity.containPixel({ gridIndex: 0 }, point)
    },
    getXData() {
      return this.chartData && this.chartData.xData ? this.chartData.xData : []
    },
    resolveNearestIndexByOffsetX(offsetX) {
      let xData = this.getXData()
      if (!this.chartEntity || !this.chartEntity.convertToPixel || !xData.length) {
        return -1
      }
      let nearestIndex = -1
      let minDistance = Number.MAX_VALUE
      for (let i = 0; i < xData.length; i++) {
        let xPixel = this.chartEntity.convertToPixel({ xAxisIndex: 0 }, xData[i])
        if (Array.isArray(xPixel)) {
          xPixel = xPixel[0]
        }
        if (xPixel === undefined || xPixel === null || Number.isNaN(Number(xPixel))) {
          continue
        }
        let distance = Math.abs(Number(offsetX) - Number(xPixel))
        if (distance < minDistance) {
          minDistance = distance
          nearestIndex = i
        }
      }
      if (nearestIndex === -1) {
        return -1
      }
      return nearestIndex
    },
    resolveClickIndex(event, params) {
      let xData = this.getXData()
      if (!xData.length) {
        return -1
      }
      let point = this.getOffsetPoint(event)
      if (!this.isPointInGrid(point)) {
        return -1
      }
      if (params && typeof params.dataIndex === 'number' && xData[params.dataIndex] !== undefined) {
        return params.dataIndex
      }
      if (params && params.name) {
        let nameIndex = xData.findIndex((item) => item === params.name)
        if (nameIndex > -1) {
          return nameIndex
        }
      }
      if (this.chartEntity && this.chartEntity.convertFromPixel && event) {
        let xAxisValue = this.chartEntity.convertFromPixel({ xAxisIndex: 0 }, [event.offsetX, event.offsetY])
        if (xAxisValue !== undefined && xAxisValue !== null && !Number.isNaN(Number(xAxisValue))) {
          let xIndex = Math.round(Number(xAxisValue))
          if (xIndex >= 0 && xIndex < xData.length) {
            return xIndex
          }
        }
      }
      if (event && event.offsetX !== undefined && event.offsetX !== null) {
        return this.resolveNearestIndexByOffsetX(event.offsetX)
      }
      return -1
    },
    emitChartClickByIndex(index, seriesName) {
      let xData = this.getXData()
      if (index < 0 || index >= xData.length) {
        return
      }
      let clickName = xData[index]
      let now = Date.now()
      if (this.lastEmitName === clickName && now - this.lastEmitAt < 120) {
        return
      }
      this.lastEmitName = clickName
      this.lastEmitAt = now
      this.$emit('chart-click', {
        name: clickName,
        seriesName: seriesName || null,
      })
    },
    hideTooltip() {
      if (!this.chartEntity) {
        return
      }
      this.chartEntity.dispatchAction({
        type: 'hideTip',
      })
    },
    bindChartClick() {
      if (!this.chartEntity) {
        return
      }
      this.chartEntity.off('click')
      if (this.chartEntity.getZr) {
        let zr = this.chartEntity.getZr()
        if (this.zrClickHandler) {
          zr.off('click', this.zrClickHandler)
        }
        if (this.zrMoveHandler) {
          zr.off('mousemove', this.zrMoveHandler)
        }
        if (this.zrMouseOutHandler) {
          zr.off('globalout', this.zrMouseOutHandler)
        }
      }
      if (!this.enablePointClick) {
        let chartDom = document.getElementById(this.domId)
        if (chartDom) {
          chartDom.style.cursor = 'default'
        }
        return
      }
      let chartDom = document.getElementById(this.domId)
      if (chartDom) {
        chartDom.style.cursor = 'pointer'
      }
      this.chartEntity.on('legendselectchanged', () => {
        this.lastLegendToggleAt = Date.now()
      })
      this.chartEntity.on('click', (params) => {
        if (params && params.componentType === 'legend') {
          return
        }
        if (Date.now() - this.lastLegendToggleAt < 180) {
          return
        }
        let index = this.resolveClickIndex(params && params.event ? params.event : null, params)
        this.emitChartClickByIndex(index, params ? params.seriesName : null)
      })
      this.zrMoveHandler = (event) => {
        let xData = this.getXData()
        if (!xData.length) {
          return
        }
        let xIndex = this.resolveClickIndex(event, null)
        if (xIndex < 0 || xIndex >= xData.length) {
          return
        }
        if (this.lastHoverIndex === xIndex) {
          return
        }
        this.lastHoverIndex = xIndex
        this.chartEntity.dispatchAction({
          type: 'showTip',
          seriesIndex: 0,
          dataIndex: xIndex,
        })
      }
      this.zrMouseOutHandler = () => {
        this.lastHoverIndex = -1
        this.hideTooltip()
      }
      this.zrClickHandler = (event) => {
        if (Date.now() - this.lastLegendToggleAt < 180) {
          return
        }
        let index = this.resolveClickIndex(event, null)
        this.emitChartClickByIndex(index, null)
      }
      let zr = this.chartEntity.getZr()
      zr.on('mousemove', this.zrMoveHandler)
      zr.on('globalout', this.zrMouseOutHandler)
      zr.on('click', this.zrClickHandler)
    },
    setChart() {
      if (!this.chartEntity) {
        let chartDom = document.getElementById(this.domId)
@@ -38,6 +229,8 @@
      var option = {
        tooltip: {
          trigger: 'axis',
          confine: true,
          extraCssText: 'pointer-events:none;',
          axisPointer: {
            // 坐标轴指示器,坐标轴触发有效
            type: 'line', // 默认为直线,可选为:'line' | 'shadow'
@@ -208,6 +401,7 @@
        ],
      }
      option && this.chartEntity.setOption(option, true)
      this.bindChartClick()
    },
    setPieUpChart() {
      if (!this.chartEntity) {
@@ -233,6 +427,8 @@
      var option = {
        tooltip: {
          trigger: 'axis',
          confine: true,
          extraCssText: 'pointer-events:none;',
          axisPointer: {
            // 坐标轴指示器,坐标轴触发有效
            type: 'line', // 默认为直线,可选为:'line' | 'shadow'
@@ -291,6 +487,7 @@
        series: [...data],
      }
      option && this.chartEntity.setOption(option, true)
      this.bindChartClick()
    },
    setOnlyLineChart() {
      if (!this.chartEntity) {
@@ -301,6 +498,8 @@
      var option = {
        tooltip: {
          trigger: 'axis',
          confine: true,
          extraCssText: 'pointer-events:none;',
          axisPointer: {
            // 坐标轴指示器,坐标轴触发有效
            type: 'line', // 默认为直线,可选为:'line' | 'shadow'
@@ -434,6 +633,7 @@
        ],
      }
      option && this.chartEntity.setOption(option, true)
      this.bindChartClick()
    },
  },
}
@@ -441,5 +641,7 @@
<style lang="less" scoped>
.line-chart {
  height: 20vh;
  position: relative;
  overflow: hidden;
}
</style>
src/views/dataAnalysis/components/DataRePieChart.vue
@@ -65,10 +65,10 @@
        legend: {
          orient: 'vertical',
          icon: 'circle',
          right: '0',
          top: '10%',
          right: '2%',
          top: 'middle',
          itemWidth: 14,
          itemGap: 20,
          itemGap: 12,
          textStyle: {
            rich: {
              a: {
@@ -106,9 +106,9 @@
            emphasis: {
              scale: false,
            },
            center: ['40%', '50%'],
            center: ['28%', '52%'],
            top: 'center',
            right: '60%',
            right: '62%',
            label: {
              show: true,
              position: 'inside',